From 152405613e1644c60544d49094223818d8979a1e Mon Sep 17 00:00:00 2001 From: Draff Date: Mon, 22 Jan 2024 23:53:22 +0000 Subject: [PATCH] doesnt build. Dont want to fix it --- src/all/kavita/AndroidManifest.xml | 2 - src/all/kavita/CHANGELOG.md | 93 -- src/all/kavita/README.md | 37 - .../kavita/assets/i18n/messages_en.properties | 18 - .../assets/i18n/messages_es_es.properties | 18 - .../assets/i18n/messages_fr_fr.properties | 20 - .../assets/i18n/messages_nb_no.properties | 21 - src/all/kavita/build.gradle | 12 - .../kavita/res/mipmap-hdpi/ic_launcher.png | Bin 3175 -> 0 bytes .../kavita/res/mipmap-mdpi/ic_launcher.png | Bin 1883 -> 0 bytes .../kavita/res/mipmap-xhdpi/ic_launcher.png | Bin 3949 -> 0 bytes .../kavita/res/mipmap-xxhdpi/ic_launcher.png | Bin 6506 -> 0 bytes .../kavita/res/mipmap-xxxhdpi/ic_launcher.png | Bin 9189 -> 0 bytes .../tachiyomi/extension/all/kavita/Filters.kt | 112 -- .../tachiyomi/extension/all/kavita/Kavita.kt | 1264 ----------------- .../extension/all/kavita/KavitaConstants.kt | 81 -- .../extension/all/kavita/KavitaFactory.kt | 13 - .../extension/all/kavita/KavitaHelper.kt | 141 -- .../extension/all/kavita/KavitaInt.kt | 17 - .../extension/all/kavita/dto/FilterDto.kt | 124 -- .../extension/all/kavita/dto/MangaDto.kt | 103 -- .../extension/all/kavita/dto/MetadataDto.kt | 104 -- .../extension/all/kavita/dto/Responses.kt | 28 - 23 files changed, 2208 deletions(-) delete mode 100644 src/all/kavita/AndroidManifest.xml delete mode 100644 src/all/kavita/CHANGELOG.md delete mode 100644 src/all/kavita/README.md delete mode 100644 src/all/kavita/assets/i18n/messages_en.properties delete mode 100644 src/all/kavita/assets/i18n/messages_es_es.properties delete mode 100644 src/all/kavita/assets/i18n/messages_fr_fr.properties delete mode 100644 src/all/kavita/assets/i18n/messages_nb_no.properties delete mode 100644 src/all/kavita/build.gradle delete mode 100644 src/all/kavita/res/mipmap-hdpi/ic_launcher.png delete mode 100644 src/all/kavita/res/mipmap-mdpi/ic_launcher.png delete mode 100644 src/all/kavita/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 src/all/kavita/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 src/all/kavita/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Filters.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaConstants.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaFactory.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaHelper.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaInt.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/FilterDto.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MangaDto.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MetadataDto.kt delete mode 100644 src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/Responses.kt diff --git a/src/all/kavita/AndroidManifest.xml b/src/all/kavita/AndroidManifest.xml deleted file mode 100644 index 8072ee00d..000000000 --- a/src/all/kavita/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/all/kavita/CHANGELOG.md b/src/all/kavita/CHANGELOG.md deleted file mode 100644 index 2843891e9..000000000 --- a/src/all/kavita/CHANGELOG.md +++ /dev/null @@ -1,93 +0,0 @@ -## 1.3.13 - -### Fixed - - * Fixed 'null cannot be cast to non-null type' exception - -## 1.3.12 - -### Features - -* Migrate filters to v2 -* Implemented smartFilters -* Added localization support - -### Fixed - -* Fixed publication status not showing - -## 1.3.10 - -### Features - -* API Change for Kavita v0.7.2 - -## 1.3.9 - -### Features - -* Added pdf support - -## 1.3.8 - -### Fix - -* Fixed `Expected URL scheme 'http' or 'https` when downloading - -## 1.3.7 - -### Features - -* New Sort filter: Time to read -* New Filter: Year release filter - -### Fix - -* Filters can now be used together with search -* Epub and pdfs no longer show in format filter (currently not supported) - -## 1.3.6 - -### Fix - -* Fixed "lateinit property title not initialized" - -## 1.3.5 - -### Features - -* Ignore DOH -* Added sort option `Item Added` -* Latest button now shows latest `Item Added` - -## 1.3.4 - -### Features - -* Exclude from bulk update warnings - -## 1.2.3 - -### Fix - -* Fixed Rating filter -* Fixed Chapter list not sorting correctly -* Fixed search -* Fixed manga details not showing correctly -* Fixed filters not populating if account was not admin - -### Features -* The extension is now ready to implement tracking. -* Min required version for the extension to work properly: `v0.5.1.1` - -## 1.2.2 - -### Features - -* Add `CHANGELOG.md` & `README.md` - -## 1.2.1 - -### Features - -* first version diff --git a/src/all/kavita/README.md b/src/all/kavita/README.md deleted file mode 100644 index 79f467edd..000000000 --- a/src/all/kavita/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Kavita - -Table of Content -- [FAQ](#FAQ) - - [Why do I see no manga?](#why-do-i-see-no-manga) - - [Where can I get more information about Kavita?](#where-can-i-get-more-information-about-kavita) - - [The Kavita extension stopped working?](#the-kavita-extension-stopped-working) - - [Can I add more than one Kavita server or user?](#can-i-add-more-than-one-kavita-server-or-user) - - [Can I test the Kavita extension before setting up my own server?](#can-i-test-the-kavita-extension-before-setting-up-my-own-server) -- [Guides](#Guides) - - [How do I add my Kavita server to Tachiyomi?](#how-do-i-add-my-kavita-server-to-tachiyomi) - -Don't find the question you are look for go check out our general FAQs and Guides over at [Extension FAQ](https://tachiyomi.org/help/faq/#extensions) or [Getting Started](https://tachiyomi.org/help/guides/getting-started/#installation) - -Kavita also has a documentation about the Tachiyomi Kavita extension at the [Kavita wiki](https://wiki.kavitareader.com/en/guides/misc/tachiyomi). - -## FAQ - -### Why do I see no manga? -Kavita is a self-hosted comic/manga media server. - -### Where can I get more information about Kavita? -You can visit the [Kavita](https://www.kavitareader.com/) website for for more information. - -### The Kavita extension stopped working? -Make sure that your Kavita server and extension are on the newest version. - -### Can I add more than one Kavita server or user? -Yes, currently you can add up to 3 different Kavita instances to Tachiyomi. - -### Can I test the Kavita extension before setting up my own server? -Yes, you can try it out with the DEMO servers OPDS url `https://demo.kavitareader.com/api/opds/aca1c50d-7e08-4f37-b356-aecd6bf69b72`. - -## Guides - -### How do I add my Kavita server to Tachiyomi? -Go into the settings of the Kavita extension from the Extension tab in Browse and fill in your OPDS url. diff --git a/src/all/kavita/assets/i18n/messages_en.properties b/src/all/kavita/assets/i18n/messages_en.properties deleted file mode 100644 index 52f341d65..000000000 --- a/src/all/kavita/assets/i18n/messages_en.properties +++ /dev/null @@ -1,18 +0,0 @@ -login_errors_failed_login=Login failed. Something went wrong -login_errors_header_token_empty="Error: The JSON Web Token is empty.\nTry opening the extension first." -login_errors_invalid_url=Invalid URL: -login_errors_parse_tokendto=There was an error parsing the auth token -pref_customsource_title=Displayed name for source -pref_edit_customsource_summary=Here you can change this source name.\nYou can write a descriptive name to identify this OPDS URL. -pref_filters_summary=Show these filters in the filter list -pref_filters_title=Default filters shown -pref_opds_badformed_url=Incorrect OPDS address. Please copy it from User settings \u2192 3rd party apps \u2192 OPDS URL -pref_opds_duplicated_source_url=The URL is configured in a different source -> -pref_opds_must_setup_address=You must set up the address to communicate with Kavita -pref_opds_summary=The OPDS URL copied from User Settings. This should include address and end with the API key. -restartapp_settings=Restart Tachiyomi to apply new setting. -version_exceptions_chapters_parse=Unhandled exception parsing chapters. Send your logs to the Kavita devs. -check_version=Ensure you have the newest version of the extension and Kavita. (0.7.8 or newer.)\nIf the issue persists, report it to the Kavita developers with the accompanying logs. -version_exceptions_smart_filter=Could not decode SmartFilter. Ensure you are using Kavita version 0.7.11 or later. -http_errors_500=Something went wrong -http_errors_401=There was an error logging in. Try again or reload the app diff --git a/src/all/kavita/assets/i18n/messages_es_es.properties b/src/all/kavita/assets/i18n/messages_es_es.properties deleted file mode 100644 index 8f6d3058c..000000000 --- a/src/all/kavita/assets/i18n/messages_es_es.properties +++ /dev/null @@ -1,18 +0,0 @@ -pref_customsource_title=Nombre de la instancia -pref_edit_customsource_summary=Aqui puedes cambiar el nombre de la instancia.\nPuedes escribir un nombre descriptivo que identifique esta url/instancia -restartapp_settings=Reinicia la aplicación para aplicar los cambios -version_exceptions_chapters_parse=Algo ha ido mal al procesar los capitulos. Envia los registros de fallo a los desarrolladores de Kavita -check_version=Comprueba que tienes tanto Kavita como la extension actualizada. (Version minima: 0.7.8)\nSi el problema persiste, reportalo a los desarrolladores de Kavita aportando los registros de fallo. -version_exceptions_smart_filter=Fallo al decodificar los filtros inteligentes. Aseg\u00FArate que estas al menos en la version 0.7.11 de Kavita -http_errors_500=Algo ha ido mal -http_errors_401=Ha habido un error al iniciar sesi\u00F3n. Prueba otra vez o reinicia la aplicaci\u00F3n -pref_opds_summary=La url del OPDS copiada de la configuraci\u00F3n del usuario. Debe incluir la direcci\u00F3n y la clave api al final. -pref_filters_summary=Mostrar estos filtros en la lista de filtros -pref_filters_title=Filtros por defecto -pref_opds_badformed_url=La direcci\u00F3n OPDS no es correcta. Por favor, c\u00F3piela desde la Configuraci\u00F3n de usuario-> aplicaciones de terceros -> url de OPDS -login_errors_parse_tokendto=Se ha producido un error al procesar el token de autenticaci\u00F3n -pref_opds_duplicated_source_url=Url est\u00E1 configurado en una fuente diferente -> -pref_opds_must_setup_address=Debe configurar la direcci\u00F3n para comunicarse con Kavita -login_errors_failed_login=Error en el inicio de sesi\u00F3n. Algo ha ido mal -login_errors_header_token_empty="Error: el token jwt est\u00E1 vac\u00EDo.\nIntente abrir primero la extensi\u00F3n" -login_errors_invalid_url=URL no v\u00E1lida: diff --git a/src/all/kavita/assets/i18n/messages_fr_fr.properties b/src/all/kavita/assets/i18n/messages_fr_fr.properties deleted file mode 100644 index bd29b962a..000000000 --- a/src/all/kavita/assets/i18n/messages_fr_fr.properties +++ /dev/null @@ -1,20 +0,0 @@ - - -version_exceptions_chapters_parse=Exception non trait\u00E9e durant l'analyse des chapitres. Envoyez les journaux aux d\u00E9velopeurs de Kavita -pref_customsource_title=Nom d'affichage pour la source -version_exceptions_smart_filter=\u00C9chec du d\u00E9codage de SmartFilter. Assurez-vous que vous utilisez au moins Kavita version 0.7.11 -pref_opds_summary=L'URL OPDS a \u00E9t\u00E9 copi\u00E9e \u00E0 partir des param\u00E8tres de l'utilisateur. Ceci devrait inclure l'adresse et la cl\u00E9 API. -pref_filters_summary=Afficher ces filtres dans la liste des filtres -check_version=Assurez-vous que vous avez l'extension et Kavita mises \u00E0 jour. (version Mini\u202F: 0.7.8)\nSi le probl\u00E8me persiste, signalez-le aux d\u00E9veloppeurs de Kavita en fournissant des journaux -pref_filters_title=Filtres par d\u00E9faut affich\u00E9s -pref_edit_customsource_summary=Ici vous pouvez changer ce nom source.\nVous pouvez \u00E9crire un nom descriptif pour identifier cette URL opds -pref_opds_badformed_url=L'adresse OPDS n'est pas correcte. Veuillez la copiez \u00E0 partir des param\u00E8tres de l'utilisateur - > Applis tierces -> URL OPDS -login_errors_parse_tokendto=Il y a eu une erreur pendant l'analyse du jeton d'authentification -restartapp_settings=Red\u00E9marrez Tachiyomi pour appliquer le nouveau r\u00E9glage. -pref_opds_duplicated_source_url=L'URL est configur\u00E9e dans une autre source -> -pref_opds_must_setup_address=Vous devez configurer l'adresse pour communiquer avec Kavita -login_errors_failed_login=\u00C9chec de la connexion. Quelque chose s'est mal pass\u00E9 -http_errors_500=Quelque chose s'est mal pass\u00E9 -login_errors_header_token_empty=\u00AB\u00A0Erreur\u202F: le jeton jwt est vide.\nEssayez d'abord d'ouvrir l'extension\u00A0\u00BB -login_errors_invalid_url=URL invalide\u202F: -http_errors_401=Il y a eu une erreur. Essayez de nouveau ou rechargez l'application diff --git a/src/all/kavita/assets/i18n/messages_nb_no.properties b/src/all/kavita/assets/i18n/messages_nb_no.properties deleted file mode 100644 index c2e0a530d..000000000 --- a/src/all/kavita/assets/i18n/messages_nb_no.properties +++ /dev/null @@ -1,21 +0,0 @@ - - -pref_customsource_title=Vist kildenavn -pref_edit_customsource_summary=Her kan du endre dette kildenavnet.\nDu kan skrive et beskrivende navn for \u00E5 identifisere denne OPDS-nettadressen. -restartapp_settings=Ny innstilling trer i kraft n\u00E5r du starter Tachiyomi p\u00E5 ny. -duplicated_source_url=Nettadressen er satt opp i en annen Kavita-instans -pref_filters_summary=Vis disse filterne i filterlisten -pref_filters_title=Forvalgte filtre valgt -login_errors_parse_tokendto=Kunne ikke tolke identifiseringssymbolet -login_errors_failed_login=Innlogging mislyktes. Noe gikk galt. -http_errors_500=Noe gikk galt -login_errors_header_token_empty="Feil: JSON-nettsymbol er tomt.\nPr\u00F8v \u00E5 \u00E5pne utvidelsen f\u00F8rst." -login_errors_invalid_url=Ugyldig nettadresse: -version_exceptions_chapters_parse=Uh\u00E5ndtert unntak i tolking av kapitler. Send loggene dine til Kavita-utviklerne. -version_exceptions_smart_filter=Kunne ikke dekode smartfilter. Forsikre deg om at du bruker Kavita versjon 0.7.11 eller nyere. -pref_opds_summary=OPDS-nettadressen kopiert fra brukerinnstillingene. Denne skal inkludere med adressen og slutte med API-n\u00F8kkelen. -check_version=Forsikre deg om at b\u00E5de utvidelsen og Kavita er av nyeste versjon. (Ihvertfall 0.7.8)\nHvis problemet vedvarer kan du rapportere det til Kavita-utviklerne med tilh\u00F8rende loggf\u00F8ring. -pref_opds_badformed_url=OPDS-adressen er ikke riktig. Kopier den fra brukerinnstillinger -> tredjepartsprogrammer -> OPDS-nettadresse -pref_opds_duplicated_source_url=Nettadressen er satt opp i en annen instans -> -pref_opds_must_setup_address=Du m\u00E5 sette opp adressen som skal kommunisere med Kavita -http_errors_401=Feil med innlogging. Pr\u00F8v \u00E5 laste inn p\u00E5 ny, eller start programmet p\u00E5 ny. diff --git a/src/all/kavita/build.gradle b/src/all/kavita/build.gradle deleted file mode 100644 index bb2376766..000000000 --- a/src/all/kavita/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -ext { - extName = 'Kavita' - extClass = '.KavitaFactory' - extVersionCode = 13 -} - -dependencies { - implementation 'info.debatty:java-string-similarity:2.0.0' - implementation(project(':lib:i18n')) -} - -apply from: "$rootDir/common.gradle" diff --git a/src/all/kavita/res/mipmap-hdpi/ic_launcher.png b/src/all/kavita/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 16cee6c3e562ab2df5129ed30737a02362d999de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3175 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 Ver@jQlf?i4002ovPDHLkV1h5CE(M{uV9T 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 Hu0mjfERi0s diff --git a/src/all/kavita/res/mipmap-xxhdpi/ic_launcher.png b/src/all/kavita/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 98f3ea1ff226dc3810f6663eb95098365d4ea1fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6506 zcmV-w8I|UVP)=NklHZC9)+u|^os#FmFfL%$5 zQ)O^2hZt~T2XJgBp<+w6g{{bmg9NyOu?q=ZxP*{|F0}XV+jr*oo0;C%Gp~EPduHCb zGt}d0cW1hLxniQC}NpV{wDZW>XlV=c2#uG zf(u0F>y?II)!Jp`Ajm79xNPdBre{I}jZ~_Y-&Ur|FO3{M{=NOL{pP@HkAHg%ic*G8 zV32|UMBzz5K@y+a^3;mVX|umL6|a1Bs$A?XRLi7XwG^|e36FV=;Cbf)5fVIlY4Zyx z1Y8jw(X*zPt}`xM7k3DN$JGc-~RJW_rGF8gdq7=BfkK#@EG_0*RwZ~ z*|{f%OZn4F)v_bahQs4Qn~wVED_iFPL`#ZoVZ;*E0V8`PJ~;WnH*eVRBO9WUUx@qy zguV_ck8$UVPp-@?n)lpLDSrm9N_7F!Ql60Dv62zW7?%|7aw;m2NUVb>5DO6f_nc(z z#MHi{>%aHuM}7srD>5ccp#A(``vC~AG2yxF?wjXa@!4CyIUXxrU*R->EpP!V%7e#@#4PkZhP>TU;o#W3`FXV7F^^9Ak*i8PwKXBeQ9lO$)f)Ne<$PS z(+@nI0#KvW$Gm?|;yPe@)}bqL@QEh#yN|5jeD{}MhU*Hp3ZYM=eCPs1)h7u#r0?4L z)Dx4v#$7FgCnN>(Y#?rf^6OWpp}EnQW-uUXWg*}kI&;H?zyAKmA9@VZn)0JUx&Wc? zW1#xLC-a%@&+aHBs^_ye61MVKjOh9&0`nlKasBk`6Vic4bWdTJFVQn4YL`qg_N#xt z`rhl|x=CAw(4nRsj#4G^1CXgc@X3Dam1o|Dn5&28;ujuI+4bt{(tKjIO-~cm$E*X* z`_WM#QQH{R_|Si@y=RFHP#(S!AZ0VA0}!5z3WNZm&y)MZSD!g--ME(DRd?Uo4eLRi zPU!AW-5R4lVF9FLzA#U0)J<32cP4yIzy|?BUx;=vhN?iU|C2?4{@|6T-<9SdIl&7( zebb`fhpUr^FVH#nFN8b<1=0lwi?{iz`xe31I0F#vT$DDit^%3Sm#sj(0MI+q9NGXN z?S|KLxx_x2;NF`Jkjz2=K<7YFad?9&5FO@_Hm_fRa<&5b2FRE4c$h9PbJPg{h2j7B zQ6F1@7TW+(N4g;Z(cWkDfe@e`P@n@+M1@bG_)6Wd+6x{p=5)-LuXH-Vd@%r_2}3`K z_6I3NVmb;G8bEEWJ|3j-${j}dZf4n_4n sh-2g#AJ7sX($=t>wz236D(q+yc~W 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 diff --git a/src/all/kavita/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/kavita/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index dc7d8341f3c7731c7f291bce231453c019cf53c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9189 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 D^5d?$ diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Filters.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Filters.kt deleted file mode 100644 index 80d0e4075..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Filters.kt +++ /dev/null @@ -1,112 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita - -import eu.kanade.tachiyomi.extension.all.kavita.KavitaConstants.noSmartFilterSelected -import eu.kanade.tachiyomi.source.model.Filter - -class UserRating : - Filter.Select( - "Minimum Rating", - arrayOf( - "Any", - "1 star", - "2 stars", - "3 stars", - "4 stars", - "5 stars", - ), - ) -class SmartFiltersFilter(smartFilters: Array) : - Filter.Select("Smart Filters", arrayOf(noSmartFilterSelected) + smartFilters) -class SortFilter(sortables: Array) : Filter.Sort("Sort by", sortables, Selection(0, true)) - -val sortableList = listOf( - Pair("Sort name", 1), - Pair("Created", 2), - Pair("Last modified", 3), - Pair("Item added", 4), - Pair("Time to Read", 5), - Pair("Release year", 6), -) - -class StatusFilter(name: String) : Filter.CheckBox(name, false) -class StatusFilterGroup(filters: List) : - Filter.Group("Status", filters) - -class ReleaseYearRange(name: String) : Filter.Text(name) -class ReleaseYearRangeGroup(filters: List) : - Filter.Group("Release Year", filters) -class GenreFilter(name: String) : Filter.TriState(name) -class GenreFilterGroup(genres: List) : - Filter.Group("Genres", genres) - -class TagFilter(name: String) : Filter.TriState(name) -class TagFilterGroup(tags: List) : Filter.Group("Tags", tags) - -class AgeRatingFilter(name: String) : Filter.TriState(name) -class AgeRatingFilterGroup(ageRatings: List) : - Filter.Group("Age Rating", ageRatings) - -class FormatFilter(name: String) : Filter.CheckBox(name, false) -class FormatsFilterGroup(formats: List) : - Filter.Group("Formats", formats) - -class CollectionFilter(name: String) : Filter.TriState(name) -class CollectionFilterGroup(collections: List) : - Filter.Group("Collection", collections) - -class LanguageFilter(name: String) : Filter.TriState(name) -class LanguageFilterGroup(languages: List) : - Filter.Group("Language", languages) - -class LibraryFilter(library: String) : Filter.TriState(library) -class LibrariesFilterGroup(libraries: List) : - Filter.Group("Libraries", libraries) - -class PubStatusFilter(name: String) : Filter.CheckBox(name, false) -class PubStatusFilterGroup(status: List) : - Filter.Group("Publication Status", status) - -class PeopleHeaderFilter(name: String) : - Filter.Header(name) -class PeopleSeparatorFilter : - Filter.Separator() - -class WriterPeopleFilter(name: String) : Filter.CheckBox(name, false) -class WriterPeopleFilterGroup(peoples: List) : - Filter.Group("Writer", peoples) - -class PencillerPeopleFilter(name: String) : Filter.CheckBox(name, false) -class PencillerPeopleFilterGroup(peoples: List) : - Filter.Group("Penciller", peoples) - -class InkerPeopleFilter(name: String) : Filter.CheckBox(name, false) -class InkerPeopleFilterGroup(peoples: List) : - Filter.Group("Inker", peoples) - -class ColoristPeopleFilter(name: String) : Filter.CheckBox(name, false) -class ColoristPeopleFilterGroup(peoples: List) : - Filter.Group("Colorist", peoples) - -class LettererPeopleFilter(name: String) : Filter.CheckBox(name, false) -class LettererPeopleFilterGroup(peoples: List) : - Filter.Group("Letterer", peoples) - -class CoverArtistPeopleFilter(name: String) : Filter.CheckBox(name, false) -class CoverArtistPeopleFilterGroup(peoples: List) : - Filter.Group("Cover Artist", peoples) - -class EditorPeopleFilter(name: String) : Filter.CheckBox(name, false) -class EditorPeopleFilterGroup(peoples: List) : - Filter.Group("Editor", peoples) - -class PublisherPeopleFilter(name: String) : Filter.CheckBox(name, false) -class PublisherPeopleFilterGroup(peoples: List) : - Filter.Group("Publisher", peoples) - -class CharacterPeopleFilter(name: String) : Filter.CheckBox(name, false) -class CharacterPeopleFilterGroup(peoples: List) : - Filter.Group("Character", peoples) - -class TranslatorPeopleFilter(name: String) : Filter.CheckBox(name, false) -class TranslatorPeopleFilterGroup(peoples: List) : - Filter.Group("Translator", peoples) diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt deleted file mode 100644 index 079e457a9..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt +++ /dev/null @@ -1,1264 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita - -import android.app.Application -import android.content.SharedPreferences -import android.text.InputType -import android.util.Log -import android.widget.Toast -import androidx.preference.EditTextPreference -import androidx.preference.MultiSelectListPreference -import eu.kanade.tachiyomi.AppInfo -import eu.kanade.tachiyomi.extension.all.kavita.dto.AuthenticationDto -import eu.kanade.tachiyomi.extension.all.kavita.dto.FilterComparison -import eu.kanade.tachiyomi.extension.all.kavita.dto.FilterField -import eu.kanade.tachiyomi.extension.all.kavita.dto.FilterStatementDto -import eu.kanade.tachiyomi.extension.all.kavita.dto.FilterV2Dto -import eu.kanade.tachiyomi.extension.all.kavita.dto.MangaFormat -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataAgeRatings -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataCollections -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataGenres -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataLanguages -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataLibrary -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataPayload -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataPeople -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataPubStatus -import eu.kanade.tachiyomi.extension.all.kavita.dto.MetadataTag -import eu.kanade.tachiyomi.extension.all.kavita.dto.PersonRole -import eu.kanade.tachiyomi.extension.all.kavita.dto.SeriesDto -import eu.kanade.tachiyomi.extension.all.kavita.dto.SeriesMetadataDto -import eu.kanade.tachiyomi.extension.all.kavita.dto.ServerInfoDto -import eu.kanade.tachiyomi.extension.all.kavita.dto.SmartFilter -import eu.kanade.tachiyomi.extension.all.kavita.dto.SortFieldEnum -import eu.kanade.tachiyomi.extension.all.kavita.dto.SortOptions -import eu.kanade.tachiyomi.extension.all.kavita.dto.VolumeDto -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.network.asObservableSuccess -import eu.kanade.tachiyomi.source.ConfigurableSource -import eu.kanade.tachiyomi.source.UnmeteredSource -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.source.model.Filter.TriState.Companion.STATE_EXCLUDE -import eu.kanade.tachiyomi.source.model.Filter.TriState.Companion.STATE_INCLUDE -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.HttpSource -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.encodeToJsonElement -import kotlinx.serialization.json.put -import okhttp3.Dns -import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response -import rx.Observable -import rx.Single -import rx.schedulers.Schedulers -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy -import java.io.IOException -import java.net.ConnectException -import java.security.MessageDigest -import java.util.Locale - -class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSource, HttpSource() { - private val helper = KavitaHelper() - override val client: OkHttpClient = - network.client.newBuilder() - .dns(Dns.SYSTEM) - .build() - override val id by lazy { - val key = "${"kavita_$suffix"}/all/$versionId" - val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray()) - (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE - } - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - override val name = "${KavitaInt.KAVITA_NAME} (${preferences.getString(KavitaConstants.customSourceNamePref, suffix)})" - override val lang = "all" - override val supportsLatest = true - private val apiUrl by lazy { getPrefApiUrl() } - override val baseUrl by lazy { getPrefBaseUrl() } - private val address by lazy { getPrefAddress() } // Address for the Kavita OPDS url. Should be http(s)://host:(port)/api/opds/api-key - private val apiKey by lazy { getPrefApiKey() } - private var jwtToken = "" // * JWT Token for authentication with the server. Stored in memory. - private val LOG_TAG = """Kavita_${"[$suffix]_" + preferences.getString(KavitaConstants.customSourceNamePref, "[$suffix]")!!.replace(' ', '_')}""" - private var isLogged = false // Used to know if login was correct and not send login requests anymore - private val json: Json by injectLazy() - - private var series = emptyList() // Acts as a cache - - private inline fun Response.parseAs(): T = - use { - if (it.code == 401) { - Log.e(LOG_TAG, "Http error 401 - Not authorized: ${it.request.url}") - Throwable("Http error 401 - Not authorized: ${it.request.url}") - } - - if (it.peekBody(Long.MAX_VALUE).string().isEmpty()) { - Log.e(LOG_TAG, "Empty body String for request url: ${it.request.url}") - throw EmptyRequestBody( - "Body of the response is empty. RequestUrl=${it.request.url}\nPlease check your kavita instance is up to date", - Throwable("Error. Request body is empty"), - ) - } - json.decodeFromString(it.body.string()) - } - - /** - * Custom implementation for fetch popular, latest and search - * Handles and logs errors to provide a more detailed exception to the users. - */ - private fun fetch(request: Request): Observable { - return client.newCall(request) - .asObservableSuccess() - .onErrorResumeNext { throwable -> - // Get Http code - val field = throwable.javaClass.getDeclaredField("code") - field.isAccessible = true // Make the field accessible - try { - var code = field.get(throwable) // Get the value of the code property - Log.e(LOG_TAG, "Error fetching manga: ${throwable.message}", throwable) - if (code as Int !in intArrayOf(401, 201, 500)) { - code = 500 - } - return@onErrorResumeNext Observable.error(IOException("Http Error: $code\n ${helper.intl["http_errors_$code"]}\n${helper.intl["check_version"]}")) - } catch (e: Exception) { - Log.e(LOG_TAG, e.toString(), e) - return@onErrorResumeNext Observable.error(e) - } - } - .map { response -> - popularMangaParse(response) - } - } - - override fun fetchPopularManga(page: Int) = - fetch(popularMangaRequest(page)) - - override fun fetchLatestUpdates(page: Int) = - fetch(latestUpdatesRequest(page)) - - override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable = - fetch(searchMangaRequest(page, query, filters)) - - override fun popularMangaParse(response: Response): MangasPage { - try { - val result = response.parseAs>() - series = result - val mangaList = result.map { item -> helper.createSeriesDto(item, apiUrl, apiKey) } - return MangasPage(mangaList, helper.hasNextPage(response)) - } catch (e: Exception) { - Log.e(LOG_TAG, "Unhandled exception", e) - throw IOException(helper.intl["check_version"]) - } - } - - override fun popularMangaRequest(page: Int): Request { - if (!isLogged) { - doLogin() - } - val payload = buildFilterBody(currentFilter) - return POST( - "$apiUrl/series/all-v2?pageNumber=$page&pageSize=20", - headersBuilder().build(), - payload.toRequestBody(JSON_MEDIA_TYPE), - ) - } - - override fun latestUpdatesRequest(page: Int): Request { - if (!isLogged) { - doLogin() - } - // Hardcode exclude epubs - val filter = FilterV2Dto(sortOptions = SortOptions(SortFieldEnum.LastChapterAdded.type, false)) - filter.statements.add(FilterStatementDto(FilterComparison.NotContains.type, FilterField.Formats.type, "3")) - val payload = json.encodeToJsonElement(filter).toString() - return POST( - "$apiUrl/series/all-v2?pageNumber=$page&pageSize=20", - headersBuilder().build(), - payload.toRequestBody(JSON_MEDIA_TYPE), - ) - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val newFilter = MetadataPayload() // need to reset it or will double - val smartFilterFilter = filters.find { it is SmartFiltersFilter } - // If a SmartFilter selected, apply its filter and return that - if (smartFilterFilter?.state != 0 && smartFilterFilter != null) { - val index = try { - smartFilterFilter?.state as Int - 1 - } catch (e: Exception) { - Log.e(LOG_TAG, e.toString(), e) - 0 - } - - val filter: SmartFilter = smartFilters[index] - val payload = buildJsonObject { - put("EncodedFilter", filter.filter) - } - // Decode selected filters - val request = POST( - "$apiUrl/filter/decode", - headersBuilder().build(), - payload.toString().toRequestBody(JSON_MEDIA_TYPE), - ) - client.newCall(request).execute().use { - if (it.code == 200) { - // Hardcode exclude epub - val decoded_filter = json.decodeFromString(it.body.string()) - decoded_filter.statements.add(FilterStatementDto(FilterComparison.NotContains.type, FilterField.Formats.type, "3")) - - // Make request with selected filters - return POST( - "$apiUrl/series/all-v2?pageNumber=$page&pageSize=20", - headersBuilder().build(), - json.encodeToJsonElement(decoded_filter).toString().toRequestBody(JSON_MEDIA_TYPE), - ) - } else { - Log.e(LOG_TAG, "Failed to decode SmartFilter: ${it.code}\n" + it.message) - throw IOException(helper.intl["version_exceptions_smart_filter"]) - } - } - } - // Else apply user filters - - filters.forEach { filter -> - when (filter) { - is SortFilter -> { - if (filter.state != null) { - newFilter.sorting = filter.state!!.index + 1 - newFilter.sorting_asc = filter.state!!.ascending - } - } - - is StatusFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.readStatus.add(content.name) - } - } - } - - is ReleaseYearRangeGroup -> { - filter.state.forEach { content -> - if (content.state.isNotEmpty()) { - if (content.name == "Min") { - newFilter.releaseYearRangeMin = content.state.toInt() - } - if (content.name == "Max") { - newFilter.releaseYearRangeMax = content.state.toInt() - } - } - } - } - - is GenreFilterGroup -> { - filter.state.forEach { content -> - if (content.state == STATE_INCLUDE) { - newFilter.genres_i.add(genresListMeta.find { it.title == content.name }!!.id) - } else if (content.state == STATE_EXCLUDE) { - newFilter.genres_e.add(genresListMeta.find { it.title == content.name }!!.id) - } - } - } - - is UserRating -> { - newFilter.userRating = filter.state - } - - is TagFilterGroup -> { - filter.state.forEach { content -> - if (content.state == STATE_INCLUDE) { - newFilter.tags_i.add(tagsListMeta.find { it.title == content.name }!!.id) - } else if (content.state == STATE_EXCLUDE) { - newFilter.tags_e.add(tagsListMeta.find { it.title == content.name }!!.id) - } - } - } - - is AgeRatingFilterGroup -> { - filter.state.forEach { content -> - if (content.state == STATE_INCLUDE) { - newFilter.ageRating_i.add(ageRatingsListMeta.find { it.title == content.name }!!.value) - } else if (content.state == STATE_EXCLUDE) { - newFilter.ageRating_e.add(ageRatingsListMeta.find { it.title == content.name }!!.value) - } - } - } - - is FormatsFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.formats.add(MangaFormat.valueOf(content.name).ordinal) - } - } - } - - is CollectionFilterGroup -> { - filter.state.forEach { content -> - if (content.state == STATE_INCLUDE) { - newFilter.collections_i.add(collectionsListMeta.find { it.title == content.name }!!.id) - } else if (content.state == STATE_EXCLUDE) { - newFilter.collections_e.add(collectionsListMeta.find { it.title == content.name }!!.id) - } - } - } - - is LanguageFilterGroup -> { - filter.state.forEach { content -> - if (content.state == STATE_INCLUDE) { - newFilter.language_i.add(languagesListMeta.find { it.title == content.name }!!.isoCode) - } else if (content.state == STATE_EXCLUDE) { - newFilter.language_e.add(languagesListMeta.find { it.title == content.name }!!.isoCode) - } - } - } - - is LibrariesFilterGroup -> { - filter.state.forEach { content -> - if (content.state == STATE_INCLUDE) { - newFilter.libraries_i.add(libraryListMeta.find { it.name == content.name }!!.id) - } else if (content.state == STATE_EXCLUDE) { - newFilter.libraries_e.add(libraryListMeta.find { it.name == content.name }!!.id) - } - } - } - - is PubStatusFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.pubStatus.add(pubStatusListMeta.find { it.title == content.name }!!.value) - } - } - } - - is WriterPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peopleWriters.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is PencillerPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peoplePenciller.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is InkerPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peopleInker.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is ColoristPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peoplePeoplecolorist.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is LettererPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peopleLetterer.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is CoverArtistPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peopleCoverArtist.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is EditorPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peopleEditor.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is PublisherPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peoplePublisher.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is CharacterPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peopleCharacter.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - is TranslatorPeopleFilterGroup -> { - filter.state.forEach { content -> - if (content.state) { - newFilter.peopleTranslator.add(peopleListMeta.find { it.name == content.name }!!.id) - } - } - } - - else -> {} - } - } - newFilter.seriesNameQuery = query - currentFilter = newFilter - return popularMangaRequest(page) - } - - /* - * MANGA DETAILS (metadata about series) - * **/ - - override fun fetchMangaDetails(manga: SManga): Observable { - val serieId = helper.getIdFromUrl(manga.url) - return client.newCall(GET("$apiUrl/series/metadata?seriesId=$serieId", headersBuilder().build())) - .asObservableSuccess() - .map { response -> - mangaDetailsParse(response).apply { initialized = true } - } - } - - override fun mangaDetailsRequest(manga: SManga): Request { - val serieId = helper.getIdFromUrl(manga.url) - val foundSerie = series.find { dto -> dto.id == serieId } - return GET( - "$baseUrl/library/${foundSerie!!.libraryId}/series/$serieId", - headersBuilder().build(), - ) - } - - override fun mangaDetailsParse(response: Response): SManga { - val result = response.parseAs() - - val existingSeries = series.find { dto -> dto.id == result.seriesId } - if (existingSeries != null) { - val manga = helper.createSeriesDto(existingSeries, apiUrl, apiKey) - manga.url = "$apiUrl/Series/${result.seriesId}" - manga.artist = result.coverArtists.joinToString { it.name } - manga.description = result.summary - manga.author = result.writers.joinToString { it.name } - manga.genre = result.genres.joinToString { it.title } - manga.thumbnail_url = "$apiUrl/image/series-cover?seriesId=${result.seriesId}&apiKey=$apiKey" - - return manga - } - val serieDto = client.newCall(GET("$apiUrl/Series/${result.seriesId}", headersBuilder().build())) - .execute() - .parseAs() - - return SManga.create().apply { - url = "$apiUrl/Series/${result.seriesId}" - artist = result.coverArtists.joinToString { it.name } - description = result.summary - author = result.writers.joinToString { it.name } - genre = result.genres.joinToString { it.title } - title = serieDto.name - thumbnail_url = "$apiUrl/image/series-cover?seriesId=${result.seriesId}&apiKey=$apiKey" - status = when (result.publicationStatus) { - 4 -> SManga.PUBLISHING_FINISHED - 2 -> SManga.COMPLETED - 0 -> SManga.ONGOING - 3 -> SManga.CANCELLED - 1 -> SManga.ON_HIATUS - else -> SManga.UNKNOWN - } - } - } - - /* - * CHAPTER LIST - * **/ - override fun chapterListRequest(manga: SManga): Request { - val url = "$apiUrl/Series/volumes?seriesId=${helper.getIdFromUrl(manga.url)}" - return GET(url, headersBuilder().build()) - } - - override fun chapterListParse(response: Response): List { - try { - val volumes = response.parseAs>() - val allChapterList = mutableListOf() - volumes.forEach { volume -> - run { - if (volume.number == 0) { - // Regular chapters - volume.chapters.map { - allChapterList.add(helper.chapterFromObject(it)) - } - } else { - // Volume chapter - volume.chapters.map { - allChapterList.add(helper.chapterFromVolume(it, volume)) - } - } - } - } - - allChapterList.sortWith(KavitaHelper.CompareChapters) - return allChapterList - } catch (e: Exception) { - Log.e(LOG_TAG, "Unhandled exception parsing chapters. Send logs to kavita devs", e) - throw IOException(helper.intl["version_exceptions_chapters_parse"]) - } - } - - /** - * Fetches the "url" of each page from the chapter - * **/ - override fun pageListRequest(chapter: SChapter): Request { - return GET("$apiUrl/${chapter.url}", headersBuilder().build()) - } - - override fun fetchPageList(chapter: SChapter): Observable> { - val chapterId = chapter.url - val numPages = chapter.scanlator?.replace(" pages", "")?.toInt() - val numPages2 = "$numPages".toInt() - 1 - val pages = mutableListOf() - for (i in 0..numPages2) { - pages.add( - Page( - index = i, - imageUrl = "$apiUrl/Reader/image?chapterId=$chapterId&page=$i&extractPdf=true&apiKey=$apiKey", - ), - ) - } - return Observable.just(pages) - } - - override fun latestUpdatesParse(response: Response): MangasPage = - throw UnsupportedOperationException("Not used") - - override fun pageListParse(response: Response): List = - throw UnsupportedOperationException("Not used") - - override fun searchMangaParse(response: Response): MangasPage = - throw UnsupportedOperationException("Not used") - - override fun imageUrlParse(response: Response): String = "" - - /* - * FILTERING - **/ - - private var currentFilter: MetadataPayload = MetadataPayload() - - /** Some variable names already exist. im not good at naming add Meta suffix */ - private var genresListMeta = emptyList() - private var tagsListMeta = emptyList() - private var ageRatingsListMeta = emptyList() - private var peopleListMeta = emptyList() - private var pubStatusListMeta = emptyList() - private var languagesListMeta = emptyList() - private var libraryListMeta = emptyList() - private var collectionsListMeta = emptyList() - private var smartFilters = emptyList() - private val personRoles = listOf( - "Writer", - "Penciller", - "Inker", - "Colorist", - "Letterer", - "CoverArtist", - "Editor", - "Publisher", - "Character", - "Translator", - ) - - /** - * Loads the enabled filters if they are not empty so tachiyomi can show them to the user - */ - override fun getFilterList(): FilterList { - val toggledFilters = getToggledFilters() - - val filters = try { - val peopleInRoles = mutableListOf>() - personRoles.map { role -> - val peoplesWithRole = mutableListOf() - peopleListMeta.map { - if (it.role == helper.safeValueOf(role).role) { - peoplesWithRole.add(it) - } - } - peopleInRoles.add(peoplesWithRole) - } - - val filtersLoaded = mutableListOf>() - - if (sortableList.isNotEmpty() and toggledFilters.contains("Sort Options")) { - filtersLoaded.add( - SortFilter(sortableList.map { it.first }.toTypedArray()), - ) - if (smartFilters.isNotEmpty()) { - filtersLoaded.add( - SmartFiltersFilter(smartFilters.map { it.name }.toTypedArray()), - - ) - } - } - if (toggledFilters.contains("Read Status")) { - filtersLoaded.add( - StatusFilterGroup( - listOf( - "notRead", - "inProgress", - "read", - ).map { StatusFilter(it) }, - ), - ) - } - if (toggledFilters.contains("ReleaseYearRange")) { - filtersLoaded.add( - ReleaseYearRangeGroup( - listOf("Min", "Max").map { ReleaseYearRange(it) }, - ), - ) - } - - if (genresListMeta.isNotEmpty() and toggledFilters.contains("Genres")) { - filtersLoaded.add( - GenreFilterGroup(genresListMeta.map { GenreFilter(it.title) }), - ) - } - if (tagsListMeta.isNotEmpty() and toggledFilters.contains("Tags")) { - filtersLoaded.add( - TagFilterGroup(tagsListMeta.map { TagFilter(it.title) }), - ) - } - if (ageRatingsListMeta.isNotEmpty() and toggledFilters.contains("Age Rating")) { - filtersLoaded.add( - AgeRatingFilterGroup(ageRatingsListMeta.map { AgeRatingFilter(it.title) }), - ) - } - if (toggledFilters.contains("Format")) { - filtersLoaded.add( - FormatsFilterGroup( - listOf( - "Image", - "Archive", - "Pdf", - "Unknown", - ).map { FormatFilter(it) }, - ), - ) - } - if (collectionsListMeta.isNotEmpty() and toggledFilters.contains("Collections")) { - filtersLoaded.add( - CollectionFilterGroup(collectionsListMeta.map { CollectionFilter(it.title) }), - ) - } - if (languagesListMeta.isNotEmpty() and toggledFilters.contains("Languages")) { - filtersLoaded.add( - LanguageFilterGroup(languagesListMeta.map { LanguageFilter(it.title) }), - ) - } - if (libraryListMeta.isNotEmpty() and toggledFilters.contains("Libraries")) { - filtersLoaded.add( - LibrariesFilterGroup(libraryListMeta.map { LibraryFilter(it.name) }), - ) - } - if (pubStatusListMeta.isNotEmpty() and toggledFilters.contains("Publication Status")) { - filtersLoaded.add( - PubStatusFilterGroup(pubStatusListMeta.map { PubStatusFilter(it.title) }), - ) - } - if (pubStatusListMeta.isNotEmpty() and toggledFilters.contains("Rating")) { - filtersLoaded.add( - UserRating(), - ) - } - - // People Metadata: - if (personRoles.isNotEmpty() and toggledFilters.any { personRoles.contains(it) }) { - filtersLoaded.addAll( - listOf>( - PeopleHeaderFilter(""), - PeopleSeparatorFilter(), - PeopleHeaderFilter("PEOPLE"), - ), - ) - if (peopleInRoles[0].isNotEmpty() and toggledFilters.contains("Writer")) { - filtersLoaded.add( - WriterPeopleFilterGroup( - peopleInRoles[0].map { WriterPeopleFilter(it.name) }, - ), - ) - } - if (peopleInRoles[1].isNotEmpty() and toggledFilters.contains("Penciller")) { - filtersLoaded.add( - PencillerPeopleFilterGroup( - peopleInRoles[1].map { PencillerPeopleFilter(it.name) }, - ), - ) - } - if (peopleInRoles[2].isNotEmpty() and toggledFilters.contains("Inker")) { - filtersLoaded.add( - InkerPeopleFilterGroup( - peopleInRoles[2].map { InkerPeopleFilter(it.name) }, - ), - ) - } - if (peopleInRoles[3].isNotEmpty() and toggledFilters.contains("Colorist")) { - filtersLoaded.add( - ColoristPeopleFilterGroup( - peopleInRoles[3].map { ColoristPeopleFilter(it.name) }, - ), - ) - } - if (peopleInRoles[4].isNotEmpty() and toggledFilters.contains("Letterer")) { - filtersLoaded.add( - LettererPeopleFilterGroup( - peopleInRoles[4].map { LettererPeopleFilter(it.name) }, - ), - ) - } - if (peopleInRoles[5].isNotEmpty() and toggledFilters.contains("CoverArtist")) { - filtersLoaded.add( - CoverArtistPeopleFilterGroup( - peopleInRoles[5].map { CoverArtistPeopleFilter(it.name) }, - ), - ) - } - if (peopleInRoles[6].isNotEmpty() and toggledFilters.contains("Editor")) { - filtersLoaded.add( - EditorPeopleFilterGroup( - peopleInRoles[6].map { EditorPeopleFilter(it.name) }, - ), - ) - } - - if (peopleInRoles[7].isNotEmpty() and toggledFilters.contains("Publisher")) { - filtersLoaded.add( - PublisherPeopleFilterGroup( - peopleInRoles[7].map { PublisherPeopleFilter(it.name) }, - ), - ) - } - if (peopleInRoles[8].isNotEmpty() and toggledFilters.contains("Character")) { - filtersLoaded.add( - CharacterPeopleFilterGroup( - peopleInRoles[8].map { CharacterPeopleFilter(it.name) }, - ), - ) - } - if (peopleInRoles[9].isNotEmpty() and toggledFilters.contains("Translator")) { - filtersLoaded.add( - TranslatorPeopleFilterGroup( - peopleInRoles[9].map { TranslatorPeopleFilter(it.name) }, - ), - ) - filtersLoaded - } else { - filtersLoaded - } - } else { - filtersLoaded - } - } catch (e: Exception) { - Log.e(LOG_TAG, "[FILTERS] Error while creating filter list", e) - emptyList() - } - return FilterList(filters) - } - - /** - * Returns a FilterV2Dto encoded as a json string with values taken from filter - */ - private fun buildFilterBody(filter: MetadataPayload): String { - val filter_dto = FilterV2Dto() - filter_dto.sortOptions.sortField = filter.sorting - filter_dto.sortOptions.isAscending = filter.sorting_asc - - // Fields that support contains and not contains statements - val containsAndNotTriplets = listOf( - Triple(FilterField.Libraries, filter.libraries_i, filter.libraries_e), - Triple(FilterField.Tags, filter.tags_i, filter.tags_e), - Triple(FilterField.Languages, filter.language_i, filter.genres_e), - Triple(FilterField.AgeRating, filter.ageRating_i, filter.ageRating_e), - Triple(FilterField.Genres, filter.genres_i, filter.genres_e), - Triple(FilterField.CollectionTags, filter.collections_i, filter.collections_e), - ) - filter_dto.addContainsNotTriple(containsAndNotTriplets) - // Fields that have must contains statements - val peoplePairs = listOf( - - Pair(FilterField.Writers, filter.peopleWriters), - Pair(FilterField.Penciller, filter.peoplePenciller), - Pair(FilterField.Inker, filter.peopleInker), - Pair(FilterField.Colorist, filter.peopleCharacter), - Pair(FilterField.Letterer, filter.peopleLetterer), - Pair(FilterField.CoverArtist, filter.peopleCoverArtist), - Pair(FilterField.Editor, filter.peopleEditor), - Pair(FilterField.Publisher, filter.peoplePublisher), - Pair(FilterField.Characters, filter.peopleCharacter), - Pair(FilterField.Translators, filter.peopleTranslator), - - Pair(FilterField.PublicationStatus, filter.pubStatus), - ) - filter_dto.addPeople(peoplePairs) - - // Customized statements - filter_dto.addStatement(FilterComparison.Contains, FilterField.Formats, filter.formats) - filter_dto.addStatement(FilterComparison.Matches, FilterField.SeriesName, filter.seriesNameQuery) - // Hardcoded statement to filter out epubs: - filter_dto.addStatement(FilterComparison.NotContains, FilterField.Formats, "3") - if (filter.readStatus.isNotEmpty()) { - filter.readStatus.forEach { - if (it == "notRead") { - filter_dto.addStatement(FilterComparison.Equal, FilterField.ReadProgress, "0") - } else if (it == "inProgress") { - filter_dto.addStatement(FilterComparison.GreaterThan, FilterField.ReadProgress, "0") - filter_dto.addStatement(FilterComparison.LessThan, FilterField.ReadProgress, "100") - } else if (it == "read") { - filter_dto.addStatement(FilterComparison.Equal, FilterField.ReadProgress, "100") - } - } - } - // todo: check statement - // filter_dto.addStatement(FilterComparison.GreaterThanEqual, FilterField.UserRating, filter.userRating.toString()) - if (filter.releaseYearRangeMin != 0) { - filter_dto.addStatement(FilterComparison.GreaterThan, FilterField.ReleaseYear, filter.releaseYearRangeMin.toString()) - } - - if (filter.releaseYearRangeMax != 0) { - filter_dto.addStatement(FilterComparison.LessThan, FilterField.ReleaseYear, filter.releaseYearRangeMax.toString()) - } - return json.encodeToJsonElement(filter_dto).toString() - } - - class LoginErrorException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) { - constructor(cause: Throwable) : this(null, cause) - } - - class OpdsurlExistsInPref(message: String? = null, cause: Throwable? = null) : Exception(message, cause) { - constructor(cause: Throwable) : this(null, cause) - } - - class EmptyRequestBody(message: String? = null, cause: Throwable? = null) : Exception(message, cause) { - constructor(cause: Throwable) : this(null, cause) - } - - class LoadingFilterFailed(message: String? = null, cause: Throwable? = null) : Exception(message, cause) { - constructor(cause: Throwable) : this(null, cause) - } - - override fun headersBuilder(): Headers.Builder { - if (jwtToken.isEmpty()) { - doLogin() - if (jwtToken.isEmpty()) throw LoginErrorException(helper.intl["login_errors_header_token_empty"]) - } - return Headers.Builder() - .add("User-Agent", "Tachiyomi Kavita v${AppInfo.getVersionName()}") - .add("Content-Type", "application/json") - .add("Authorization", "Bearer $jwtToken") - } - - private fun setupLoginHeaders(): Headers.Builder { - return Headers.Builder() - .add("User-Agent", "Tachiyomi Kavita v${AppInfo.getVersionName()}") - .add("Content-Type", "application/json") - .add("Authorization", "Bearer $jwtToken") - } - - override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { - val opdsAddressPref = screen.editTextPreference( - ADDRESS_TITLE, - "OPDS url", - "", - helper.intl["pref_opds_summary"], - ) - val enabledFiltersPref = MultiSelectListPreference(screen.context).apply { - key = KavitaConstants.toggledFiltersPref - title = helper.intl["pref_filters_title"] - summary = helper.intl["pref_filters_summary"] - entries = KavitaConstants.filterPrefEntries - entryValues = KavitaConstants.filterPrefEntriesValue - setDefaultValue(KavitaConstants.defaultFilterPrefEntries) - setOnPreferenceChangeListener { _, newValue -> - @Suppress("UNCHECKED_CAST") - val checkValue = newValue as Set - preferences.edit() - .putStringSet(KavitaConstants.toggledFiltersPref, checkValue) - .commit() - } - } - val customSourceNamePref = EditTextPreference(screen.context).apply { - key = KavitaConstants.customSourceNamePref - title = helper.intl["pref_customsource_title"] - summary = helper.intl["pref_edit_customsource_summary"] - setOnPreferenceChangeListener { _, newValue -> - val res = preferences.edit() - .putString(KavitaConstants.customSourceNamePref, newValue.toString()) - .commit() - Toast.makeText( - screen.context, - helper.intl["restartapp_settings"], - Toast.LENGTH_LONG, - ).show() - Log.v(LOG_TAG, "[Preferences] Successfully modified custom source name: $newValue") - res - } - } - screen.addPreference(customSourceNamePref) - screen.addPreference(opdsAddressPref) - screen.addPreference(enabledFiltersPref) - } - - private fun androidx.preference.PreferenceScreen.editTextPreference( - preKey: String, - title: String, - default: String, - summary: String, - isPassword: Boolean = false, - ): EditTextPreference { - return EditTextPreference(context).apply { - key = preKey - this.title = title - val input = preferences.getString(title, null) - this.summary = if (input == null || input.isEmpty()) summary else input - this.setDefaultValue(default) - dialogTitle = title - - if (isPassword) { - setOnBindEditTextListener { - it.inputType = - InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD - } - } - setOnPreferenceChangeListener { _, newValue -> - try { - val opdsUrlInPref = opdsUrlInPreferences(newValue.toString()) // We don't allow hot have multiple sources with same ip or domain - if (opdsUrlInPref.isNotEmpty()) { - // TODO("Add option to allow multiple sources with same url at the cost of tracking") - preferences.edit().putString(title, "").apply() - - Toast.makeText( - context, - helper.intl["pref_opds_duplicated_source_url"] + ": " + opdsUrlInPref, - Toast.LENGTH_LONG, - ).show() - throw OpdsurlExistsInPref(helper.intl["pref_opds_duplicated_source_url"] + opdsUrlInPref) - } - - val res = preferences.edit().putString(title, newValue as String).commit() - Toast.makeText( - context, - helper.intl["restartapp_settings"], - Toast.LENGTH_LONG, - ).show() - setupLogin(newValue) - Log.v(LOG_TAG, "[Preferences] Successfully modified OPDS URL") - res - } catch (e: OpdsurlExistsInPref) { - Log.e(LOG_TAG, "Url exists in a different sourcce") - false - } catch (e: Exception) { - Log.e(LOG_TAG, "Unrecognised error", e) - false - } - } - } - } - - private fun getPrefBaseUrl(): String = preferences.getString("BASEURL", "")!! - private fun getPrefApiUrl(): String = preferences.getString("APIURL", "")!! - private fun getPrefKey(): String = preferences.getString("APIKEY", "")!! - private fun getToggledFilters() = preferences.getStringSet(KavitaConstants.toggledFiltersPref, KavitaConstants.defaultFilterPrefEntries)!! - - // We strip the last slash since we will append it above - private fun getPrefAddress(): String { - var path = preferences.getString(ADDRESS_TITLE, "")!! - if (path.isNotEmpty() && path.last() == '/') { - path = path.substring(0, path.length - 1) - } - return path - } - - private fun getPrefApiKey(): String { - // http(s)://host:(port)/api/opds/api-key - val existingKey = preferences.getString("APIKEY", "") - return existingKey!!.ifEmpty { preferences.getString(ADDRESS_TITLE, "")!!.split("/opds/")[1] } - } - - companion object { - private const val ADDRESS_TITLE = "Address" - private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() - } - - /* - * LOGIN - **/ - /** - * Used to check if a url is configured already in any of the sources - * This is a limitation needed for tracking. - * **/ - private fun opdsUrlInPreferences(url: String): String { - fun getCleanedApiUrl(url: String): String = "${url.split("/api/").first()}/api" - - for (sourceId in 1..3) { // There's 3 sources so 3 preferences to check - val sourceSuffixID by lazy { - val key = "${"kavita_$sourceId"}/all/1" // Hardcoded versionID to 1 - val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray()) - (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) } - .reduce(Long::or) and Long.MAX_VALUE - } - val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$sourceSuffixID", 0x0000) - } - val prefApiUrl = preferences.getString("APIURL", "")!! - - if (prefApiUrl.isNotEmpty()) { - if (prefApiUrl == getCleanedApiUrl(url)) { - if (sourceId.toString() != suffix) { - return preferences.getString(KavitaConstants.customSourceNamePref, sourceId.toString())!! - } - } - } - } - return "" - } - - private fun setupLogin(addressFromPreference: String = "") { - Log.v(LOG_TAG, "[Setup Login] Starting setup") - val validAddress = address.ifEmpty { addressFromPreference } - val tokens = validAddress.split("/api/opds/") - val apiKey = tokens[1] - val baseUrlSetup = tokens[0].replace("\n", "\\n") - - if (baseUrlSetup.toHttpUrlOrNull() == null) { - Log.e(LOG_TAG, "Invalid URL $baseUrlSetup") - throw Exception("""${helper.intl["login_errors_invalid_url"]}: $baseUrlSetup""") - } - preferences.edit().putString("BASEURL", baseUrlSetup).apply() - preferences.edit().putString("APIKEY", apiKey).apply() - preferences.edit().putString("APIURL", "$baseUrlSetup/api").apply() - Log.v(LOG_TAG, "[Setup Login] Setup successful") - } - - private fun doLogin() { - if (address.isEmpty()) { - Log.e(LOG_TAG, "OPDS URL is empty or null") - throw IOException(helper.intl["pref_opds_must_setup_address"]) - } - if (address.split("/opds/").size != 2) { - throw IOException(helper.intl["pref_opds_badformed_url"]) - } - if (jwtToken.isEmpty()) setupLogin() - Log.v(LOG_TAG, "[Login] Starting login") - val request = POST( - "$apiUrl/Plugin/authenticate?apiKey=${getPrefKey()}&pluginName=Tachiyomi-Kavita", - setupLoginHeaders().build(), - "{}".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()), - ) - client.newCall(request).execute().use { - val peekbody = it.peekBody(Long.MAX_VALUE).toString() - - if (it.code == 200) { - try { - jwtToken = it.parseAs().token - isLogged = true - } catch (e: Exception) { - Log.e(LOG_TAG, "Possible outdated kavita", e) - throw IOException(helper.intl["login_errors_parse_tokendto"]) - } - } else { - if (it.code == 500) { - Log.e(LOG_TAG, "[LOGIN] login failed. There was some error -> Code: ${it.code}.Response message: ${it.message} Response body: $peekbody.") - throw LoginErrorException(helper.intl["login_errors_failed_login"]) - } else { - Log.e(LOG_TAG, "[LOGIN] login failed. Authentication was not successful -> Code: ${it.code}.Response message: ${it.message} Response body: $peekbody.") - throw LoginErrorException(helper.intl["login_errors_failed_login"]) - } - } - } - Log.v(LOG_TAG, "[Login] Login successful") - } - - init { - if (apiUrl.isNotBlank()) { - Single.fromCallable { - // Login - doLogin() - try { // Get current version - val requestUrl = "$apiUrl/Server/server-info" - val serverInfoDto = client.newCall(GET(requestUrl, headersBuilder().build())) - .execute() - .parseAs() - Log.e( - LOG_TAG, - "Extension version: code=${AppInfo.getVersionCode()} name=${AppInfo.getVersionName()}" + - " - - Kavita version: ${serverInfoDto.kavitaVersion} - - Lang:${Locale.getDefault()}", - ) // this is not a real error. Using this so it gets printed in dump logs if there's any error - } catch (e: EmptyRequestBody) { - Log.e(LOG_TAG, "Extension version: code=${AppInfo.getVersionCode()} - name=${AppInfo.getVersionName()}") - } catch (e: Exception) { - Log.e(LOG_TAG, "Tachiyomi version: code=${AppInfo.getVersionCode()} - name=${AppInfo.getVersionName()}", e) - } - try { // Load Filters - // Genres - Log.v(LOG_TAG, "[Filter] Fetching filters ") - client.newCall(GET("$apiUrl/Metadata/genres", headersBuilder().build())) - .execute().use { response -> - - genresListMeta = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e(LOG_TAG, "[Filter] Error decoding JSON for genres filter -> ${response.body}", e) - emptyList() - } - } - // tagsListMeta - client.newCall(GET("$apiUrl/Metadata/tags", headersBuilder().build())) - .execute().use { response -> - tagsListMeta = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e(LOG_TAG, "[Filter] Error decoding JSON for tagsList filter", e) - emptyList() - } - } - // age-ratings - client.newCall(GET("$apiUrl/Metadata/age-ratings", headersBuilder().build())) - .execute().use { response -> - ageRatingsListMeta = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e( - LOG_TAG, - "[Filter] Error decoding JSON for age-ratings filter", - e, - ) - emptyList() - } - } - // collectionsListMeta - client.newCall(GET("$apiUrl/Collection", headersBuilder().build())) - .execute().use { response -> - collectionsListMeta = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e( - LOG_TAG, - "[Filter] Error decoding JSON for collectionsListMeta filter", - e, - ) - emptyList() - } - } - // languagesListMeta - client.newCall(GET("$apiUrl/Metadata/languages", headersBuilder().build())) - .execute().use { response -> - languagesListMeta = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e( - LOG_TAG, - "[Filter] Error decoding JSON for languagesListMeta filter", - e, - ) - emptyList() - } - } - // libraries - client.newCall(GET("$apiUrl/Library", headersBuilder().build())) - .execute().use { response -> - libraryListMeta = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e( - LOG_TAG, - "[Filter] Error decoding JSON for libraries filter", - e, - ) - emptyList() - } - } - // peopleListMeta - client.newCall(GET("$apiUrl/Metadata/people", headersBuilder().build())) - .execute().use { response -> - peopleListMeta = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e( - LOG_TAG, - "error while decoding JSON for peopleListMeta filter", - e, - ) - emptyList() - } - } - client.newCall(GET("$apiUrl/Metadata/publication-status", headersBuilder().build())) - .execute().use { response -> - pubStatusListMeta = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e( - LOG_TAG, - "error while decoding JSON for publicationStatusListMeta filter", - e, - ) - emptyList() - } - } - client.newCall(GET("$apiUrl/filter", headersBuilder().build())) - .execute().use { response -> - smartFilters = try { - response.body.use { json.decodeFromString(it.string()) } - } catch (e: Exception) { - Log.e( - LOG_TAG, - "error while decoding JSON for smartfilters", - e, - ) - emptyList() - } - } - Log.v(LOG_TAG, "[Filter] Successfully loaded metadata tags from server") - } catch (e: Exception) { - throw LoadingFilterFailed("Failed Loading Filters", e.cause) - } - } - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.io()) - .subscribe( - {}, - { tr -> - // Avoid polluting logs with traces of exception - if (tr is EmptyRequestBody || tr is LoginErrorException) { - Log.e(LOG_TAG, "error while doing initial calls\n${tr.cause}") - return@subscribe - } - if (tr is ConnectException) { // avoid polluting logs with traces of exception - Log.e(LOG_TAG, "Error while doing initial calls\n${tr.cause}") - return@subscribe - } - Log.e(LOG_TAG, "error while doing initial calls", tr) - }, - ) - } - } -} diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaConstants.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaConstants.kt deleted file mode 100644 index 24f284410..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaConstants.kt +++ /dev/null @@ -1,81 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita - -object KavitaConstants { - // toggle filters - const val toggledFiltersPref = "toggledFilters" - val filterPrefEntries = arrayOf( - "Sort Options", - "Format", - "Libraries", - "Read Status", - "Genres", - "Tags", - "Collections", - "Languages", - "Publication Status", - "Rating", - "Age Rating", - "Writers", - "Penciller", - "Inker", - "Colorist", - "Letterer", - "Cover Artist", - "Editor", - "Publisher", - "Character", - "Translators", - "ReleaseYearRange", - ) - val filterPrefEntriesValue = arrayOf( - "Sort Options", - "Format", - "Libraries", - "Read Status", - "Genres", - "Tags", - "Collections", - "Languages", - "Publication Status", - "Rating", - "Age Rating", - "Writers", - "Penciller", - "Inker", - "Colorist", - "Letterer", - "CoverArtist", - "Editor", - "Publisher", - "Character", - "Translators", - "ReleaseYearRange", - ) - val defaultFilterPrefEntries = setOf( - "Sort Options", - "Format", - "Libraries", - "Read Status", - "Genres", - "Tags", - "Collections", - "Languages", - "Publication Status", - "Rating", - "Age Rating", - "Writers", - "Penciller", - "Inker", - "Colorist", - "Letterer", - "CoverArtist", - "Editor", - "Publisher", - "Character", - "Translators", - "ReleaseYearRange", - ) - - const val customSourceNamePref = "customSourceName" - const val noSmartFilterSelected = "No smart filter loaded" -} diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaFactory.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaFactory.kt deleted file mode 100644 index 7639b6e53..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaFactory.kt +++ /dev/null @@ -1,13 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita - -import eu.kanade.tachiyomi.source.Source -import eu.kanade.tachiyomi.source.SourceFactory - -class KavitaFactory : SourceFactory { - override fun createSources(): List = - listOf( - Kavita("1"), - Kavita("2"), - Kavita("3"), - ) -} diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaHelper.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaHelper.kt deleted file mode 100644 index f588f684b..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaHelper.kt +++ /dev/null @@ -1,141 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita - -import eu.kanade.tachiyomi.extension.all.kavita.dto.ChapterDto -import eu.kanade.tachiyomi.extension.all.kavita.dto.PaginationInfo -import eu.kanade.tachiyomi.extension.all.kavita.dto.SeriesDto -import eu.kanade.tachiyomi.extension.all.kavita.dto.VolumeDto -import eu.kanade.tachiyomi.lib.i18n.Intl -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import okhttp3.Response -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.TimeZone - -class KavitaHelper { - val json = Json { - isLenient = true - ignoreUnknownKeys = true - allowSpecialFloatingPointValues = true - useArrayPolymorphism = true - prettyPrint = true - } - inline fun > safeValueOf(type: String): T { - return java.lang.Enum.valueOf(T::class.java, type) - } - val dateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSS", Locale.US) - .apply { timeZone = TimeZone.getTimeZone("UTC") } - fun parseDate(dateAsString: String): Long = - dateFormatter.parse(dateAsString)?.time ?: 0 - - fun hasNextPage(response: Response): Boolean { - val paginationHeader = response.header("Pagination") - var hasNextPage = false - if (!paginationHeader.isNullOrEmpty()) { - val paginationInfo = json.decodeFromString(paginationHeader) - hasNextPage = paginationInfo.currentPage + 1 > paginationInfo.totalPages - } - return !hasNextPage - } - - fun getIdFromUrl(url: String): Int { - return url.split("/").last().toInt() - } - - fun createSeriesDto(obj: SeriesDto, baseUrl: String, apiKey: String): SManga = - SManga.create().apply { - url = "$baseUrl/Series/${obj.id}" - title = obj.name - // Deprecated: description = obj.summary - thumbnail_url = "$baseUrl/image/series-cover?seriesId=${obj.id}&apiKey=$apiKey" - } - class CompareChapters { - companion object : Comparator { - override fun compare(a: SChapter, b: SChapter): Int { - if (a.chapter_number < 1.0 && b.chapter_number < 1.0) { - // Both are volumes, multiply by 100 and do normal sort - return if ((a.chapter_number * 100) < (b.chapter_number * 100)) { - 1 - } else { - -1 - } - } else { - if (a.chapter_number < 1.0 && b.chapter_number >= 1.0) { - // A is volume, b is not. A should sort first - return 1 - } else if (a.chapter_number >= 1.0 && b.chapter_number < 1.0) { - return -1 - } - } - if (a.chapter_number < b.chapter_number) return 1 - if (a.chapter_number > b.chapter_number) return -1 - return 0 - } - } - } - fun chapterFromObject(obj: ChapterDto): SChapter = SChapter.create().apply { - url = obj.id.toString() - name = if (obj.number == "0" && obj.isSpecial) { - // This is a special. Chapter name is special name - obj.range - } else { - val cleanedName = obj.title.replaceFirst("^0+(?!$)".toRegex(), "") - "Chapter $cleanedName" - } - date_upload = parseDate(obj.created) - chapter_number = obj.number.toFloat() - scanlator = "${obj.pages} pages" - } - - fun chapterFromVolume(obj: ChapterDto, volume: VolumeDto): SChapter = - SChapter.create().apply { - // If there are multiple chapters to this volume, then prefix with Volume number - if (volume.chapters.isNotEmpty() && obj.number != "0") { - // This volume is not volume 0, hence they are not loose chapters - // We just add a nice Volume X to the chapter title - // Chapter-based Volume - name = "Volume ${volume.number} Chapter ${obj.number}" - chapter_number = obj.number.toFloat() - } else if (obj.number == "0") { - // Both specials and volume has chapter number 0 - if (volume.number == 0) { - // Treat as special - // Special is not in a volume - if (obj.range == "") { - // Special does not have any Title - name = "Chapter 0" - chapter_number = obj.number.toFloat() - } else { - // We use it's own special tile - name = obj.range - chapter_number = obj.number.toFloat() - } - } else { - // Is a single-file volume - // We encode the chapter number to support tracking - name = "Volume ${volume.number}" - chapter_number = volume.number.toFloat() / 10000 - } - } else { - name = "Unhandled Else Volume ${volume.number}" - } - url = obj.id.toString() - date_upload = parseDate(obj.created) - - scanlator = "${obj.pages} pages" - } - val intl = Intl( - language = Locale.getDefault().toString(), - baseLanguage = "en", - availableLanguages = KavitaInt.AVAILABLE_LANGS, - classLoader = this::class.java.classLoader!!, - createMessageFileName = { lang -> - when (lang) { - KavitaInt.SPANISH_LATAM -> Intl.createDefaultMessageFileName(KavitaInt.SPANISH) - else -> Intl.createDefaultMessageFileName(lang) - } - }, - ) -} diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaInt.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaInt.kt deleted file mode 100644 index fbaf93af0..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/KavitaInt.kt +++ /dev/null @@ -1,17 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita - -object KavitaInt { - const val ENGLISH = "en" - const val SPANISH = "es_ES" - const val SPANISH_LATAM = "es-419" - const val FRENCH = "fr_FR" - const val NORWEGIAN = "nb_NO" - val AVAILABLE_LANGS = setOf( - ENGLISH, - SPANISH, - SPANISH_LATAM, - NORWEGIAN, - FRENCH, - ) - const val KAVITA_NAME = "Kavita" -} diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/FilterDto.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/FilterDto.kt deleted file mode 100644 index 77b669e51..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/FilterDto.kt +++ /dev/null @@ -1,124 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita.dto - -import kotlinx.serialization.Serializable -import kotlin.Triple - -@Serializable -data class FilterV2Dto( - val id: Int? = null, - val name: String? = null, - val statements: MutableList = mutableListOf(), - val combination: Int = 0, // FilterCombination = FilterCombination.And, - val sortOptions: SortOptions = SortOptions(), - val limitTo: Int = 0, -) { - fun addStatement(comparison: FilterComparison, field: FilterField, value: String) { - if (value.isNotBlank()) { - statements.add(FilterStatementDto(comparison.type, field.type, value)) - } - } - fun addStatement(comparison: FilterComparison, field: FilterField, values: java.util.ArrayList) { - if (values.isNotEmpty()) { - statements.add(FilterStatementDto(comparison.type, field.type, values.joinToString(","))) - } - } - - fun addContainsNotTriple(list: List, ArrayList>>) { - list.map { - addStatement(FilterComparison.Contains, it.first, it.second) - addStatement(FilterComparison.NotContains, it.first, it.third) - } - } - fun addPeople(list: List>>) { - list.map { - addStatement(FilterComparison.MustContains, it.first, it.second) - } - } -} - -@Serializable -data class FilterStatementDto( - // todo: Create custom serializator for comparison and field and remove .type extension in Kavita.kt - val comparison: Int, - val field: Int, - val value: String, - -) - -@Serializable -enum class SortFieldEnum(val type: Int) { - SortName(1), - CreatedDate(2), - LastModifiedDate(3), - LastChapterAdded(4), - TimeToRead(5), - ReleaseYear(6), - ; - - companion object { - private val map = SortFieldEnum.values().associateBy(SortFieldEnum::type) - fun fromInt(type: Int) = map[type] - } -} - -@Serializable -data class SortOptions( - var sortField: Int = SortFieldEnum.SortName.type, - var isAscending: Boolean = true, -) - -@Serializable -enum class FilterCombination { - Or, - And, -} - -@Serializable -enum class FilterField(val type: Int) { - Summary(0), - SeriesName(1), - PublicationStatus(2), - Languages(3), - AgeRating(4), - UserRating(5), - Tags(6), - CollectionTags(7), - Translators(8), - Characters(9), - Publisher(10), - Editor(11), - CoverArtist(12), - Letterer(13), - Colorist(14), - Inker(15), - Penciller(16), - Writers(17), - Genres(18), - Libraries(19), - ReadProgress(20), - Formats(21), - ReleaseYear(22), - ReadTime(23), - Path(24), - FilePath(25), -} - -@Serializable -enum class FilterComparison(val type: Int) { - Equal(0), - GreaterThan(1), - GreaterThanEqual(2), - LessThan(3), - LessThanEqual(4), - Contains(5), - MustContains(6), - Matches(7), - NotContains(8), - NotEqual(9), - BeginsWith(10), - EndsWith(11), - IsBefore(12), - IsAfter(13), - IsInLast(14), - IsNotInLast(15), -} diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MangaDto.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MangaDto.kt deleted file mode 100644 index 1c09db11d..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MangaDto.kt +++ /dev/null @@ -1,103 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita.dto - -import kotlinx.serialization.Serializable - -@Serializable -enum class MangaFormat(val format: Int) { - Image(0), - Archive(1), - Unknown(2), - Epub(3), - Pdf(4), - ; - companion object { - private val map = PersonRole.values().associateBy(PersonRole::role) - fun fromInt(type: Int) = map[type] - } -} -enum class PersonRole(val role: Int) { - Other(1), - Writer(3), - Penciller(4), - Inker(5), - Colorist(6), - Letterer(7), - CoverArtist(8), - Editor(9), - Publisher(10), - Character(11), - Translator(12), - ; - companion object { - private val map = PersonRole.values().associateBy(PersonRole::role) - fun fromInt(type: Int) = map[type] - } -} - -@Serializable -data class SeriesDto( - val id: Int, - val name: String, - val originalName: String = "", - val thumbnail_url: String? = "", - val localizedName: String? = "", - val sortName: String? = "", - val pages: Int, - val coverImageLocked: Boolean = true, - val pagesRead: Int, - val userRating: Float, - val userReview: String? = "", - val format: Int, - val created: String? = "", - val libraryId: Int, - val libraryName: String? = "", -) - -@Serializable -data class SeriesMetadataDto( - val id: Int, - val summary: String? = "", - val writers: List = emptyList(), - val coverArtists: List = emptyList(), - val genres: List = emptyList(), - val seriesId: Int, - val ageRating: Int, - val publicationStatus: Int, -) - -@Serializable -data class Genres( - val title: String, -) - -@Serializable -data class Person( - val name: String, -) - -@Serializable -data class VolumeDto( - val id: Int, - val number: Int, - val name: String, - val pages: Int, - val pagesRead: Int, - val lastModified: String, - val created: String, - val seriesId: Int, - val chapters: List = emptyList(), -) - -@Serializable -data class ChapterDto( - val id: Int, - val range: String, - val number: String, - val pages: Int, - val isSpecial: Boolean, - val title: String, - val pagesRead: Int, - val coverImageLocked: Boolean, - val volumeId: Int, - val created: String, -) diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MetadataDto.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MetadataDto.kt deleted file mode 100644 index f14256719..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/MetadataDto.kt +++ /dev/null @@ -1,104 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita.dto - -import kotlinx.serialization.Serializable -/** -* This file contains all class for filtering -* */ -@Serializable -data class MetadataGenres( - val id: Int, - val title: String, -) - -@Serializable -data class MetadataPeople( - val id: Int, - val name: String, - val role: Int, -) - -@Serializable -data class MetadataPubStatus( - val value: Int, - val title: String, -) - -@Serializable -data class MetadataTag( - val id: Int, - val title: String, -) - -@Serializable -data class MetadataAgeRatings( - val value: Int, - val title: String, -) - -@Serializable -data class MetadataLanguages( - val isoCode: String, - val title: String, -) - -@Serializable -data class MetadataLibrary( - val id: Int, - val name: String, - val type: Int, -) - -@Serializable -data class MetadataCollections( - val id: Int, - val title: String, -) - -data class MetadataPayload( - val forceUseMetadataPayload: Boolean = true, - var sorting: Int = 1, - var sorting_asc: Boolean = true, - var readStatus: ArrayList = arrayListOf(), - val readStatusList: List = listOf("notRead", "inProgress", "read"), - // _i = included, _e = excluded - var genres_i: ArrayList = arrayListOf(), - var genres_e: ArrayList = arrayListOf(), - var tags_i: ArrayList = arrayListOf(), - var tags_e: ArrayList = arrayListOf(), - var ageRating_i: ArrayList = arrayListOf(), - var ageRating_e: ArrayList = arrayListOf(), - - var formats: ArrayList = arrayListOf(), - var collections_i: ArrayList = arrayListOf(), - var collections_e: ArrayList = arrayListOf(), - var userRating: Int = 0, - var people: ArrayList = arrayListOf(), - // _i = included, _e = excluded - var language_i: ArrayList = arrayListOf(), - var language_e: ArrayList = arrayListOf(), - - var libraries_i: ArrayList = arrayListOf(), - var libraries_e: ArrayList = arrayListOf(), - var pubStatus: ArrayList = arrayListOf(), - var seriesNameQuery: String = "", - var releaseYearRangeMin: Int = 0, - var releaseYearRangeMax: Int = 0, - - var peopleWriters: ArrayList = arrayListOf(), - var peoplePenciller: ArrayList = arrayListOf(), - var peopleInker: ArrayList = arrayListOf(), - var peoplePeoplecolorist: ArrayList = arrayListOf(), - var peopleLetterer: ArrayList = arrayListOf(), - var peopleCoverArtist: ArrayList = arrayListOf(), - var peopleEditor: ArrayList = arrayListOf(), - var peoplePublisher: ArrayList = arrayListOf(), - var peopleCharacter: ArrayList = arrayListOf(), - var peopleTranslator: ArrayList = arrayListOf(), -) - -@Serializable -data class SmartFilter( - val id: Int, - val name: String, - val filter: String, -) diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/Responses.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/Responses.kt deleted file mode 100644 index 424cc1233..000000000 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/dto/Responses.kt +++ /dev/null @@ -1,28 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.kavita.dto - -import kotlinx.serialization.Serializable - -@Serializable // Used to process login -data class AuthenticationDto( - val username: String, - val token: String, - val apiKey: String, -) - -@Serializable -data class PaginationInfo( - val currentPage: Int, - val itemsPerPage: Int, - val totalItems: Int, - val totalPages: Int, -) - -@Serializable -data class ServerInfoDto( - val installId: String, - val os: String, - val isDocker: Boolean, - val dotnetVersion: String, - val kavitaVersion: String, - val numOfCores: Int, -)