43
.github/ISSUE_TEMPLATE/01_report_issue.yml
vendored
43
.github/ISSUE_TEMPLATE/01_report_issue.yml
vendored
@@ -3,26 +3,6 @@ description: Report a source issue in Tachiyomi
|
||||
labels: [Bug]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: source
|
||||
attributes:
|
||||
label: Source information
|
||||
description: |
|
||||
You can find the extension name and version in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "Mangahere 1.3.18"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduce-steps
|
||||
attributes:
|
||||
@@ -59,14 +39,25 @@ body:
|
||||
- type: input
|
||||
id: tachiyomi-version
|
||||
attributes:
|
||||
label: Tachiyomi version
|
||||
label: Mihon/Komikku version
|
||||
description: |
|
||||
You can find your Tachiyomi version in **More → About**.
|
||||
You can find your Mihon/Komikku version in **More → About**.
|
||||
placeholder: |
|
||||
Example: "0.14.7"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: kavita-version
|
||||
attributes:
|
||||
label: Kavita version
|
||||
description: |
|
||||
You can find your Kavita version in **Settings → System**.
|
||||
placeholder: |
|
||||
Example: "0.8.7.2"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: android-version
|
||||
attributes:
|
||||
@@ -95,13 +86,11 @@ body:
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
- label: I have updated Mihon/Komikku to last version.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
- label: I have updated the extension to last version.
|
||||
required: true
|
||||
- label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/).
|
||||
required: true
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/tachiyomiorg/tachiyomi/issues/new/choose).
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/mihonapp/mihon/issues/new/choose).
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
|
||||
@@ -3,26 +3,6 @@ description: Suggest a feature to improve an existing source
|
||||
labels: [Feature request]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: source
|
||||
attributes:
|
||||
label: Source name
|
||||
description: |
|
||||
You can find the extension name in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "Mangahere"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
@@ -51,9 +31,7 @@ body:
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/tachiyomiorg/tachiyomi/issues/new/choose).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/mihonapp/mihon/issues/new/choose).
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
59
.github/ISSUE_TEMPLATE/02_request_source.yml
vendored
59
.github/ISSUE_TEMPLATE/02_request_source.yml
vendored
@@ -1,59 +0,0 @@
|
||||
name: 🌐 Source request
|
||||
description: Suggest a new source for Tachiyomi
|
||||
labels: [Source request]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: name
|
||||
attributes:
|
||||
label: Source name
|
||||
placeholder: |
|
||||
Example: "Not Real Scans"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Source link
|
||||
placeholder: |
|
||||
Example: "https://notrealscans.org"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
placeholder: |
|
||||
Additional details and attachments.
|
||||
Example:
|
||||
"18+/NSFW = yes"
|
||||
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have checked that the extension does not already exist on the [website extensions list](https://tachiyomi.org/extensions/) or the app.
|
||||
required: true
|
||||
- label: I have checked that the extension is not on [the removed sources list](https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master/REMOVED_SOURCES.md).
|
||||
required: true
|
||||
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
|
||||
required: true
|
||||
- label: I have checked that the extension does not already exist by searching the [GitHub repository](https://github.com/tachiyomiorg/tachiyomi-extensions/) and verified it does not appear in the code base.
|
||||
required:
|
||||
- label: I have written a meaningful title with the source name.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
57
.github/ISSUE_TEMPLATE/03_report_url_change.yml
vendored
57
.github/ISSUE_TEMPLATE/03_report_url_change.yml
vendored
@@ -1,57 +0,0 @@
|
||||
name: 🔗 URL change report
|
||||
description: Report URL change of an existing source
|
||||
labels: [Bug,Domain changed]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: source
|
||||
attributes:
|
||||
label: Source information
|
||||
description: |
|
||||
You can find the extension name and version in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "NotRealScans 1.3.1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Source new URL
|
||||
placeholder: |
|
||||
Example: "https://notrealscans.org"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
placeholder: |
|
||||
Additional details and attachments.
|
||||
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
- label: I have opened WebView and checked that the source URL is not updated yet.
|
||||
required: true
|
||||
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
@@ -31,11 +31,7 @@ body:
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/tachiyomiorg/tachiyomi/issues/new/choose).
|
||||
required: true
|
||||
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
- label: If this is an issue with the app itself, I should be opening an issue in the [app repository](https://github.com/mihonapp/mihon/issues/new/choose).
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
63
.github/ISSUE_TEMPLATE/04_report_dead_source.yml
vendored
63
.github/ISSUE_TEMPLATE/04_report_dead_source.yml
vendored
@@ -1,63 +0,0 @@
|
||||
name: ❌ Dead source report
|
||||
description: Source is down and website is closed
|
||||
labels: [Source is down]
|
||||
body:
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Notice
|
||||
If you have a lot of dead sources to report, please go back and submit a single meta request.
|
||||
|
||||
- type: input
|
||||
id: source
|
||||
attributes:
|
||||
label: Source name
|
||||
description: |
|
||||
You can find the extension name in **Browse → Extensions**.
|
||||
placeholder: |
|
||||
Example: "NotRealScans"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: language
|
||||
attributes:
|
||||
label: Source language
|
||||
placeholder: |
|
||||
Example: "English"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Source link
|
||||
placeholder: |
|
||||
Example: "https://notrealscans.org"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
placeholder: |
|
||||
Additional details and attachments.
|
||||
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
- label: I have opened WebView and checked that the source website is down.
|
||||
required: true
|
||||
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open or closed issue.
|
||||
required: true
|
||||
- label: I have written a meaningful title with the source name.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
33
.github/ISSUE_TEMPLATE/07_request_removal.yml
vendored
33
.github/ISSUE_TEMPLATE/07_request_removal.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: 🗑 Source removal request
|
||||
description: Scanlators can request their site to be removed
|
||||
labels: [Meta request]
|
||||
body:
|
||||
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Source link
|
||||
placeholder: |
|
||||
Example: "https://notrealscans.org"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
placeholder: |
|
||||
Additional details and attachments.
|
||||
|
||||
- type: checkboxes
|
||||
id: requirements
|
||||
attributes:
|
||||
label: Requirements
|
||||
description: Your request will be denied if you don't meet these requirements.
|
||||
options:
|
||||
- label: I've added a `TXT` DNS record for my domain with the value `tachiyomi-verification`
|
||||
required: true
|
||||
- label: Site only hosts content scanlated by the group and not stolen from other scanlators or official releases (i.e., not an aggregator site)
|
||||
required: true
|
||||
- label: Site is not infested with user-hostile features (e.g., invasive or malicious ads)
|
||||
required: true
|
||||
@@ -2,7 +2,7 @@
|
||||
* Add or remove modules to load as needed for local development here.
|
||||
*/
|
||||
loadAllIndividualExtensions()
|
||||
// loadIndividualExtension("all", "mangadex")
|
||||
loadIndividualExtension("all", "kavita")
|
||||
|
||||
/**
|
||||
* ===================================== COMMON CONFIGURATION ======================================
|
||||
|
||||
@@ -1,32 +1,107 @@
|
||||
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_group_tags_title=Group Tags (fork must support grouping)
|
||||
pref_group_tags_summary_on=Will prefix tags with their type
|
||||
pref_group_tags_summary_off=List all tags together
|
||||
pref_last_volume_cover_title=Keep cover updated
|
||||
pref_last_volume_cover_summary_off=Keep current cover
|
||||
pref_last_volume_cover_summary_on=Use the last volume cover when updating
|
||||
pref_show_epub_title=Show EPUB (EPUB can't be read in Mihon)
|
||||
pref_show_epub_summary_off=Hide EPUB titles in browsing
|
||||
pref_show_epub_summary_on=Show EPUB titles in browsing
|
||||
# ================ #
|
||||
# Authentication #
|
||||
# ================ #
|
||||
login_errors_failed_login=Authentication failed.\nPlease check your credentials
|
||||
login_errors_header_token_empty=Authorization token missing\nPlease open the extension first to generate a token
|
||||
login_errors_invalid_url=Invalid server URL:
|
||||
login_errors_parse_tokendto=Failed to process authentication token
|
||||
http_errors_401=Authentication expired\nPlease log in again
|
||||
http_errors_500=Server error occurred
|
||||
|
||||
# ============== #
|
||||
# Preferences #
|
||||
# ============== #
|
||||
pref_customsource_title=Source Display Name
|
||||
pref_edit_customsource_summary=Customize how this Kavita instance appears in your library\nExample: "Home Server" or "Cloud Instance"
|
||||
|
||||
pref_group_tags_title=Tag Grouping (fork must support grouping)
|
||||
pref_group_tags_summary_on=Show tags with category prefixes
|
||||
pref_group_tags_summary_off=Display tags as flat list
|
||||
|
||||
pref_last_volume_cover_title=Keep Cover Updated
|
||||
pref_last_volume_cover_summary_off=Keep series cover
|
||||
pref_last_volume_cover_summary_on=Always show current Volume/Issue cover on refresh
|
||||
|
||||
pref_show_epub_title=EPUB Visibility
|
||||
pref_show_epub_summary_off=Hide EPUB entries
|
||||
pref_show_epub_summary_on=Show EPUB entries (unreadable in Mihon)
|
||||
|
||||
pref_rd_date_title=Reading List: Display Release Date
|
||||
pref_rd_date_summary_off=Do not display the release date
|
||||
pref_rd_date_summary_on=Display the release date | !! Once you toggle it on, if you toggle it off again, it will stays as N/A !!
|
||||
pref_reset_title=Reset All Settings
|
||||
pref_reset_summary=Clear all preferences for this Kavita instance (Only affect the selected instance)
|
||||
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 Mihon 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
|
||||
pref_rd_date_summary_off=Hide publication dates
|
||||
pref_rd_date_summary_on=Show publication dates\nNote: Once you toggle it on, if you toggle it off again, it will stays as N/A.
|
||||
|
||||
pref_reset_title=Reset Settings
|
||||
pref_reset_summary=Clear all preferences for this Kavita instance
|
||||
|
||||
pref_filters_title=Default Filters
|
||||
pref_filters_summary=Select which filters appear by default
|
||||
|
||||
# ============== #
|
||||
# OPDS Setup #
|
||||
# ============== #
|
||||
pref_opds_must_setup_address=OPDS URL required for connection
|
||||
pref_opds_badformed_url=Invalid OPDS address\nCopy from: User Settings > API Key / OPDS > OPDS URL (settings#clients)
|
||||
pref_opds_duplicated_source_url=URL already used in another source:
|
||||
pref_opds_summary=Complete OPDS URL including API key\nFormat: http://address:port/opds?apiKey=XXX
|
||||
|
||||
# ============== #
|
||||
# System Messages #
|
||||
# ============== #
|
||||
restartapp_settings=Restart Mihon to apply changes
|
||||
check_version=Requires Kavita v0.8.7+\nIf issues persist, contact support with logs
|
||||
version_exceptions_chapters_parse=Chapter parsing error\nPlease report with logs
|
||||
version_exceptions_smart_filter=Smart Filter error\nRequires Kavita v0.7.11+
|
||||
|
||||
|
||||
# ============== #
|
||||
# Filters #
|
||||
# ============== #
|
||||
# Filter headers
|
||||
filters_special=These filters can't be combined with other filters
|
||||
filters_people=People Filters
|
||||
filters_special_list=Special Lists
|
||||
filters_smart_filters=Smart Filters
|
||||
|
||||
# @todo
|
||||
# Basic filters
|
||||
filters_user_rating=User Rating
|
||||
filters_status=Status
|
||||
filters_sort=Sort By
|
||||
filters_release_year=Release Year
|
||||
filters_genres=Genres
|
||||
filters_tags=Tags
|
||||
filters_age_ratings=Age Ratings
|
||||
filters_collections=Collections
|
||||
filters_languages=Languages
|
||||
filters_libraries=Libraries
|
||||
filters_pub_status=Publication Status
|
||||
|
||||
# People filters
|
||||
filters_writers=Writers
|
||||
filters_pencillers=Pencillers
|
||||
filters_inkers=Inkers
|
||||
filters_colorists=Colorists
|
||||
filters_letterers=Letterers
|
||||
filters_cover_artists=Cover Artists
|
||||
filters_editors=Editors
|
||||
filters_publishers=Publishers
|
||||
filters_characters=Characters
|
||||
filters_translators=Translators
|
||||
|
||||
# Status options
|
||||
filters_status_any=Any
|
||||
filters_status_unread=Unread
|
||||
filters_status_in_progress=In Progress
|
||||
filters_status_read=Read
|
||||
|
||||
# Sort options
|
||||
filters_sort_title=Title
|
||||
filters_sort_rating=Rating
|
||||
filters_sort_date_added=Date Added
|
||||
filters_sort_release_date=Release Date
|
||||
filters_sort_read_progress=Read Progress
|
||||
|
||||
# Error messages
|
||||
filters_error_loading=Failed to load filters
|
||||
filters_empty_list=No filters available
|
||||
|
||||
|
||||
@@ -1,18 +1,107 @@
|
||||
version_exceptions_chapters_parse=Exception non trait\u00E9e durant l'analyse des chapitres. Envoyez les logs 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 copier \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 Mihon 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 token 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 relancez l'application
|
||||
# ================ #
|
||||
# Authentification #
|
||||
# ================ #
|
||||
login_errors_failed_login=Échec de l'authentification. Vérifiez vos identifiants
|
||||
login_errors_header_token_empty=Token d'autorisation manquant\nOuvrez d'abord l'extension pour générer un token
|
||||
login_errors_invalid_url=URL du serveur invalide :
|
||||
login_errors_parse_tokendto=Échec du traitement du token d'authentification
|
||||
http_errors_401=Authentification expirée. Veuillez vous reconnecter
|
||||
http_errors_500=Erreur du serveur
|
||||
|
||||
# ============== #
|
||||
# Préférences #
|
||||
# ============== #
|
||||
pref_customsource_title=Nom d'affichage de la source
|
||||
pref_edit_customsource_summary=Personnalisez l'affichage de cette instance Kavita\nExemple : "Serveur Maison" ou "Instance Cloud"
|
||||
|
||||
pref_group_tags_title=Groupement des tags (fork doit supporter le groupement)
|
||||
pref_group_tags_summary_on=Afficher les tags avec préfixes
|
||||
pref_group_tags_summary_off=Afficher les tags en liste plate
|
||||
|
||||
pref_last_volume_cover_title=Garder la couverture à jour
|
||||
pref_last_volume_cover_summary_off=Conserver la couverture de la série
|
||||
pref_last_volume_cover_summary_on=Toujours afficher la dernière couverture
|
||||
|
||||
pref_show_epub_title=Visibilité EPUB
|
||||
pref_show_epub_summary_off=Masquer les EPUB
|
||||
pref_show_epub_summary_on=Afficher les EPUB (illisibles dans Mihon)
|
||||
|
||||
pref_rd_date_title=Listes de Lecture: Affichage date de publication
|
||||
pref_rd_date_summary_off=Masquer les dates de publication
|
||||
pref_rd_date_summary_on=Afficher les dates de publication\nNote : Désactiver affichera toujours "N/A" après activation
|
||||
|
||||
pref_reset_title=Réinitialisation
|
||||
pref_reset_summary=Effacer toutes les préférences pour cette instance Kavita
|
||||
|
||||
pref_filters_title=Filtres par défaut
|
||||
pref_filters_summary=Sélectionnez les filtres visibles par défaut
|
||||
|
||||
# ============== #
|
||||
# Configuration OPDS #
|
||||
# ============== #
|
||||
pref_opds_must_setup_address=URL OPDS requise pour la connexion
|
||||
pref_opds_badformed_url=URL OPDS invalide\nCopiez depuis : Paramètres > Clé API / OPDS > URL OPDS
|
||||
pref_opds_duplicated_source_url=URL déjà utilisée dans une autre source :
|
||||
pref_opds_summary=URL OPDS complète incluant la clé API\nFormat : http://adresse:port/opds?apiKey=XXX
|
||||
|
||||
# ============== #
|
||||
# Messages système #
|
||||
# ============== #
|
||||
restartapp_settings=Redémarrez Mihon pour appliquer les changements
|
||||
check_version=Nécessite Kavita v0.8.7+\nSi problème persiste, contactez le support avec les logs
|
||||
version_exceptions_chapters_parse=Erreur d'analyse des chapitres\nMerci de rapporter avec les logs
|
||||
version_exceptions_smart_filter=Erreur Smart Filter\nNécessite Kavita v0.7.11+
|
||||
|
||||
|
||||
# ============== #
|
||||
# Filtres #
|
||||
# ============== #
|
||||
# Filter headers
|
||||
filters_special=Ces filtres ne peuvent pas être combinés avec d'autres filtres
|
||||
filters_people=Filtres Personnalités
|
||||
|
||||
# Special filters
|
||||
filters_special_list=Listes Spéciales
|
||||
filters_smart_filters=Filtres Intelligents
|
||||
|
||||
# Basic filters
|
||||
filters_user_rating=Note Utilisateur
|
||||
filters_status=Statut
|
||||
filters_sort=Trier Par
|
||||
filters_release_year=Année de Sortie
|
||||
filters_genres=Genres
|
||||
filters_tags=Étiquettes
|
||||
filters_age_ratings=Classifications d'Âge
|
||||
filters_collections=Collections
|
||||
filters_languages=Langues
|
||||
filters_libraries=Bibliothèques
|
||||
filters_pub_status=Statut de Publication
|
||||
|
||||
# People filters
|
||||
filters_writers=Auteur
|
||||
filters_pencillers=Dessinateurs
|
||||
filters_inkers=Encreurs
|
||||
filters_colorists=Coloristes
|
||||
filters_letterers=Lettrists
|
||||
filters_cover_artists=Artistes de Couverture
|
||||
filters_editors=Éditeurs
|
||||
filters_publishers=Éditeurs
|
||||
filters_characters=Personnalités
|
||||
filters_translators=Traducteurs
|
||||
|
||||
# Status options
|
||||
filters_status_any=Tous
|
||||
filters_status_unread=Non Lu
|
||||
filters_status_in_progress=En Cours
|
||||
filters_status_read=Lu
|
||||
|
||||
# Sort options
|
||||
filters_sort_title=Titre
|
||||
filters_sort_rating=Note
|
||||
filters_sort_date_added=Date d'Ajout
|
||||
filters_sort_release_date=Date de Sortie
|
||||
filters_sort_read_progress=Progrès de Lecture
|
||||
|
||||
# Error messages
|
||||
filters_error_loading=Échec du chargement des filtres
|
||||
filters_empty_list=Aucun filtre disponible
|
||||
|
||||
@@ -2,7 +2,7 @@ ext {
|
||||
kmkVersionCode = 1
|
||||
extName = 'Kavita'
|
||||
extClass = '.KavitaFactory'
|
||||
extVersionCode = 19
|
||||
extVersionCode = 20
|
||||
isNsfw = false
|
||||
}
|
||||
|
||||
|
||||
@@ -3,25 +3,6 @@ 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<Int>(
|
||||
"Minimum Rating",
|
||||
arrayOf(
|
||||
"Any",
|
||||
"1 star",
|
||||
"2 stars",
|
||||
"3 stars",
|
||||
"4 stars",
|
||||
"5 stars",
|
||||
).mapIndexed { index, _ -> index }.toTypedArray(),
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return when (state) {
|
||||
0 -> "Any"
|
||||
else -> "$state star${if (state > 1) "s" else ""}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SmartFiltersFilter(smartFilters: Array<String>) :
|
||||
Filter.Select<String>("Smart Filters", arrayOf(noSmartFilterSelected) + smartFilters)
|
||||
class SortFilter(sortables: Array<String>) : Filter.Sort("Sort by", sortables, Selection(0, true))
|
||||
@@ -38,15 +19,22 @@ val sortableList = listOf(
|
||||
Pair("Random", 9),
|
||||
)
|
||||
|
||||
class SpecialListFilter : Filter.Select<String>(
|
||||
"Special Lists",
|
||||
class SpecialListFilter(name: String = "Special Lists") : Filter.Select<String>(
|
||||
name,
|
||||
arrayOf("None", "Want to Read", "Reading Lists"),
|
||||
)
|
||||
|
||||
// Use Filter.CheckBox directly for status
|
||||
class StatusFilter(name: String) : Filter.CheckBox(name, false)
|
||||
class StatusFilterGroup(filters: List<StatusFilter>) :
|
||||
Filter.Group<StatusFilter>("Status", filters)
|
||||
class StatusFilter : Filter.Select<String>(
|
||||
"Status",
|
||||
arrayOf("Any", "Unread", "In Progress", "Read"),
|
||||
)
|
||||
class StatusSeparator :
|
||||
Filter.Separator()
|
||||
|
||||
class StatusFilterGroup(name: String = "Status") : Filter.Group<StatusFilter>(
|
||||
name,
|
||||
listOf(StatusFilter()),
|
||||
)
|
||||
|
||||
// Use Filter.Text directly for release year range
|
||||
class ReleaseYearRange(name: String) : Filter.Text(name, "")
|
||||
@@ -59,7 +47,8 @@ class GenreFilterGroup(genres: List<GenreFilter>) :
|
||||
Filter.Group<GenreFilter>("Genres", genres)
|
||||
|
||||
class TagFilter(name: String) : Filter.TriState(name)
|
||||
class TagFilterGroup(tags: List<TagFilter>) : Filter.Group<TagFilter>("Tags", tags)
|
||||
class TagFilterGroup(tags: List<TagFilter>) :
|
||||
Filter.Group<TagFilter>("Tags", tags)
|
||||
|
||||
class AgeRatingFilter(name: String) : Filter.TriState(name)
|
||||
class AgeRatingFilterGroup(ageRatings: List<AgeRatingFilter>) :
|
||||
@@ -85,8 +74,29 @@ class PubStatusFilter(name: String) : Filter.CheckBox(name, false)
|
||||
class PubStatusFilterGroup(status: List<PubStatusFilter>) :
|
||||
Filter.Group<PubStatusFilter>("Publication Status", status)
|
||||
|
||||
class UserRating : Filter.Select<Int>(
|
||||
"Minimum Rating",
|
||||
arrayOf(
|
||||
"Any",
|
||||
"1 star",
|
||||
"2 stars",
|
||||
"3 stars",
|
||||
"4 stars",
|
||||
"5 stars",
|
||||
).mapIndexed { index, _ -> index }.toTypedArray(),
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return when (state) {
|
||||
0 -> "Any"
|
||||
else -> "$state star${if (state > 1) "s" else ""}"
|
||||
}
|
||||
}
|
||||
}
|
||||
class UserRatingSeparator :
|
||||
Filter.Separator()
|
||||
|
||||
class PeopleHeaderFilter(name: String) :
|
||||
Filter.Header(name)
|
||||
Filter.Header(name.toString())
|
||||
class PeopleSeparatorFilter :
|
||||
Filter.Separator()
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,11 @@ package eu.kanade.tachiyomi.extension.all.kavita
|
||||
|
||||
object KavitaConstants {
|
||||
|
||||
const val UNNUMBERED_VOLUME = -100000
|
||||
const val UNNUMBERED_VOLUME_STR = "-100000"
|
||||
const val SPECIAL_NUMBER = 100_000
|
||||
const val VOLUME_NUMBER_OFFSET = 10000f
|
||||
|
||||
val PERSON_ROLES = listOf(
|
||||
"Writer", "Penciller", "Inker", "Colorist",
|
||||
"Letterer", "CoverArtist", "Editor",
|
||||
@@ -23,18 +28,7 @@ object KavitaConstants {
|
||||
"Publication Status",
|
||||
"Rating",
|
||||
"Age Rating",
|
||||
"Writers",
|
||||
"Penciller",
|
||||
"Inker",
|
||||
"Colorist",
|
||||
"Letterer",
|
||||
"Cover Artist",
|
||||
"Editor",
|
||||
"Publisher",
|
||||
"Character",
|
||||
"Translators",
|
||||
"ReleaseYearRange",
|
||||
"Average Rating",
|
||||
"Random",
|
||||
"Read Progress",
|
||||
)
|
||||
@@ -51,18 +45,7 @@ object KavitaConstants {
|
||||
"Publication Status",
|
||||
"Rating",
|
||||
"Age Rating",
|
||||
"Writers",
|
||||
"Penciller",
|
||||
"Inker",
|
||||
"Colorist",
|
||||
"Letterer",
|
||||
"CoverArtist",
|
||||
"Editor",
|
||||
"Publisher",
|
||||
"Character",
|
||||
"Translators",
|
||||
"ReleaseYearRange",
|
||||
"Average Rating",
|
||||
"Random",
|
||||
"Read Progress",
|
||||
)
|
||||
@@ -79,18 +62,7 @@ object KavitaConstants {
|
||||
"Publication Status",
|
||||
"Rating",
|
||||
"Age Rating",
|
||||
"Writers",
|
||||
"Penciller",
|
||||
"Inker",
|
||||
"Colorist",
|
||||
"Letterer",
|
||||
"CoverArtist",
|
||||
"Editor",
|
||||
"Publisher",
|
||||
"Character",
|
||||
"Translators",
|
||||
"ReleaseYearRange",
|
||||
"Average Rating",
|
||||
"Random",
|
||||
"Read Progress",
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package eu.kanade.tachiyomi.extension.all.kavita
|
||||
|
||||
import android.util.Log
|
||||
import android.annotation.SuppressLint
|
||||
import eu.kanade.tachiyomi.extension.all.kavita.dto.ChapterDto
|
||||
import eu.kanade.tachiyomi.extension.all.kavita.dto.ChapterType
|
||||
import eu.kanade.tachiyomi.extension.all.kavita.dto.LibraryTypeEnum
|
||||
@@ -13,170 +13,204 @@ 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
|
||||
coerceInputValues = true
|
||||
explicitNulls = false
|
||||
allowSpecialFloatingPointValues = true
|
||||
useArrayPolymorphism = true
|
||||
prettyPrint = true
|
||||
}
|
||||
inline fun <reified T : Enum<T>> 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<PaginationInfo>(paginationHeader)
|
||||
hasNextPage = paginationInfo.currentPage + 1 > paginationInfo.totalPages
|
||||
hasNextPage = paginationInfo.currentPage + 1 < paginationInfo.totalPages
|
||||
}
|
||||
return !hasNextPage
|
||||
return hasNextPage
|
||||
}
|
||||
|
||||
fun getIdFromUrl(url: String): Int {
|
||||
return try {
|
||||
val id = url.substringAfterLast("/").toInt()
|
||||
Log.d("KavitaHelper", "Extracted ID $id from URL: $url")
|
||||
// Log.d("KavitaHelper", "Extracted ID $id from URL: $url")
|
||||
id
|
||||
} catch (e: Exception) {
|
||||
Log.e("KavitaHelper", "Failed to extract ID from URL: $url", e)
|
||||
// Log.e("KavitaHelper", "Failed to extract ID from URL: $url", e)
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
// Rating Providers from Series-Details-Plus
|
||||
private fun getProviderName(provider: Int): String = when (provider) {
|
||||
0 -> "User"
|
||||
1 -> "AniList"
|
||||
2 -> "MyAnimeList"
|
||||
3 -> "MangaUpdates"
|
||||
else -> "Rating"
|
||||
}
|
||||
// @todo Rating Providers from Series-Details-Plus
|
||||
// private fun getProviderName(provider: Int): String = when (provider) {
|
||||
// 0 -> "User"
|
||||
// 1 -> "AniList"
|
||||
// 2 -> "MyAnimeList"
|
||||
// 3 -> "MangaUpdates"
|
||||
// else -> "Rating"
|
||||
// }
|
||||
|
||||
fun createSeriesDto(obj: SeriesDto, baseUrl: String, apiUrl: String, apiKey: String): SManga =
|
||||
fun createSeriesDto(obj: SeriesDto, baseUrl: String, apiKey: String): SManga =
|
||||
SManga.create().apply {
|
||||
// url = "$baseUrl/library/${obj.libraryId}/series/${obj.id}"
|
||||
url = "$baseUrl/Series/${obj.id}"
|
||||
title = obj.name
|
||||
thumbnail_url = "$baseUrl/image/series-cover?seriesId=${obj.id}&apiKey=$apiKey"
|
||||
|
||||
// Set status based on read progress
|
||||
status = when {
|
||||
obj.pagesRead >= obj.pages -> SManga.COMPLETED
|
||||
obj.pagesRead > 0 -> SManga.ONGOING
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
// class CompareChapters {
|
||||
// companion object : Comparator<SChapter> {
|
||||
// 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 chapterFromVolume(chapter: ChapterDto, volume: VolumeDto, singleFileVolumeNumber: Int? = null, libraryType: LibraryTypeEnum? = null): SChapter =
|
||||
fun chapterFromVolume(
|
||||
chapter: ChapterDto,
|
||||
volume: VolumeDto,
|
||||
singleFileVolumeNumber: Double? = null,
|
||||
libraryType: LibraryTypeEnum? = null,
|
||||
isWebtoon: Boolean = false,
|
||||
mangaTitle: String = "",
|
||||
): SChapter =
|
||||
SChapter.create().apply {
|
||||
val type = ChapterType.of(chapter, volume, libraryType)
|
||||
val titleName = chapter.titleName
|
||||
val title = chapter.title
|
||||
val range = chapter.range
|
||||
// For webtoon
|
||||
val chapterLabel = if (isWebtoon) "Episode" else "Chapter"
|
||||
val volumeLabel = if (isWebtoon) "Season" else "Volume"
|
||||
|
||||
if (titleName != null) {
|
||||
name = when (type) {
|
||||
ChapterType.Regular -> {
|
||||
val volumeNum = volume.number.toString().toIntOrNull()?.let {
|
||||
it.toString().padStart(2, '0')
|
||||
} ?: volume.number.toString()
|
||||
val chapterNum = chapter.number.toIntOrNull()?.let {
|
||||
it.toString().padStart(2, '0')
|
||||
} ?: chapter.number
|
||||
val chapterNum = formatChapterNumber(chapter)
|
||||
when {
|
||||
chapter.titleName.isBlank() -> "Volume $volumeNum Chapter $chapterNum"
|
||||
chapter.titleName.trim().matches(Regex("^\\d+$")) -> "Volume $volumeNum Chapter ${chapter.titleName.trim().padStart(2, '0')}"
|
||||
else -> "Volume $volumeNum $chapterNum - ${chapter.titleName}"
|
||||
titleName.isNotBlank() && titleName.any { it.isLetter() } ->
|
||||
"$chapterNum - ${cleanChapterTitle(
|
||||
titleName,
|
||||
ChapterTitleContext(
|
||||
mangaTitle = mangaTitle,
|
||||
chapterNumber = chapterNum,
|
||||
volumeName = volume.name.ifBlank { title },
|
||||
),
|
||||
)}"
|
||||
|
||||
else ->
|
||||
"$volumeLabel ${formatVolumeNumber(volume)} $chapterLabel $chapterNum"
|
||||
}
|
||||
}
|
||||
|
||||
ChapterType.SingleFileVolume -> {
|
||||
val volumeNumber = formatVolumeNumber(volume)
|
||||
val cleanVolumeName = volume.name.trim()
|
||||
|
||||
// Always use the API's volume number, never parse from title
|
||||
when {
|
||||
volume.name.isBlank() -> "Volume ${volume.number}"
|
||||
volume.name.trim().matches(Regex("^\\d+$")) -> "Volume ${volume.name.trim().toIntOrNull()?.toString() ?: volume.name.trim()}"
|
||||
else -> "${volume.number} - ${volume.name}"
|
||||
// If name is empty or just numbers (Kavita default)
|
||||
cleanVolumeName.isEmpty() || cleanVolumeName.none { it.isLetter() } -> {
|
||||
cleanChapterTitle("Volume $volumeNumber")
|
||||
}
|
||||
// If name already contains volume info (Vol. 3, V3, etc.)
|
||||
cleanVolumeName.contains(Regex("(?i)(vol|volume|v)[.\\s]*\\d+")) -> {
|
||||
cleanChapterTitle(cleanVolumeName)
|
||||
}
|
||||
// Normal case - prefix with volume number
|
||||
else -> {
|
||||
cleanChapterTitle("$volumeNumber - $cleanVolumeName")
|
||||
}
|
||||
}
|
||||
ChapterType.Special -> chapter.titleName.takeIf { it.isNotBlank() } ?: chapter.range
|
||||
}
|
||||
|
||||
ChapterType.Special -> {
|
||||
cleanChapterTitle(
|
||||
when {
|
||||
title.isNotBlank() -> title
|
||||
range.isNotBlank() -> range
|
||||
else -> "Special"
|
||||
},
|
||||
ChapterTitleContext(
|
||||
mangaTitle = mangaTitle,
|
||||
volumeName = volume.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
ChapterType.Chapter -> {
|
||||
val chapterNum = chapter.number.toIntOrNull()?.let {
|
||||
it.toString().padStart(2, '0')
|
||||
} ?: chapter.number
|
||||
val chapterNum = formatChapterNumber(chapter)
|
||||
when {
|
||||
chapter.titleName.isBlank() -> "Chapter $chapterNum"
|
||||
chapter.titleName.trim().matches(Regex("^\\d+$")) -> "Chapter ${chapter.titleName.trim().padStart(2, '0')}"
|
||||
else -> "$chapterNum - ${chapter.titleName}"
|
||||
titleName.isNotBlank() && titleName.any { it.isLetter() } ->
|
||||
"$chapterNum - ${cleanChapterTitle(
|
||||
titleName,
|
||||
ChapterTitleContext(
|
||||
mangaTitle = mangaTitle,
|
||||
chapterNumber = chapterNum,
|
||||
volumeName = volume.name,
|
||||
),
|
||||
)}"
|
||||
|
||||
else ->
|
||||
"$chapterLabel $chapterNum"
|
||||
}
|
||||
}
|
||||
|
||||
ChapterType.Issue -> {
|
||||
val issueNum = chapter.number.toIntOrNull()?.let {
|
||||
it.toString().padStart(3, '0')
|
||||
} ?: chapter.number
|
||||
val issueNum = chapter.number.toIntOrNull()?.toString()?.padStart(3, '0') ?: chapter.number
|
||||
cleanChapterTitle(
|
||||
when {
|
||||
chapter.titleName.isNotBlank() && !chapter.titleName.trim().matches(Regex("^\\d+$")) ->
|
||||
"#$issueNum ${chapter.titleName}"
|
||||
chapter.title.isNotBlank() && !chapter.title.trim().matches(Regex("^\\d+$")) ->
|
||||
"#$issueNum ${chapter.title}"
|
||||
titleName.any { it.isLetter() } -> "$titleName (#$issueNum)"
|
||||
else -> "Issue #$issueNum"
|
||||
},
|
||||
ChapterTitleContext(
|
||||
mangaTitle = mangaTitle,
|
||||
chapterNumber = issueNum,
|
||||
volumeName = volume.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// chapter_number = try {
|
||||
// // if (type == ChapterType.SingleFileVolume) {
|
||||
// // volume.number.toFloat() / 10000
|
||||
// // } else {
|
||||
// // chapter.number.toFloatOrNull() ?: 0f
|
||||
// // }
|
||||
// // } catch (e: NumberFormatException) {
|
||||
// // 0f
|
||||
// // }
|
||||
|
||||
// Handle decimal chapter numbers properly
|
||||
chapter_number = when {
|
||||
type == ChapterType.SingleFileVolume && singleFileVolumeNumber != null -> singleFileVolumeNumber.toFloat()
|
||||
type != ChapterType.SingleFileVolume -> chapter.number.toFloatOrNull() ?: 0f
|
||||
else -> 0f
|
||||
type == ChapterType.SingleFileVolume && singleFileVolumeNumber != null ->
|
||||
volume.minNumber.toFloat()
|
||||
|
||||
type == ChapterType.SingleFileVolume -> {
|
||||
// For standalone volumes, use positive numbers
|
||||
volume.minNumber.toFloat()
|
||||
}
|
||||
|
||||
// For regular chapters
|
||||
type != ChapterType.SingleFileVolume && type != ChapterType.Special -> {
|
||||
// Handle decimal chapter numbers
|
||||
if (chapter.minNumber % 1 != 0.0) {
|
||||
chapter.minNumber.toFloat()
|
||||
} else {
|
||||
chapter.minNumber.toInt().toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
// For volumes/specials in mixed content, place them below chapter 0
|
||||
else -> {
|
||||
val volumeNum = try {
|
||||
if (volume.minNumber % 1 != 0.0) {
|
||||
volume.minNumber.toFloat()
|
||||
} else {
|
||||
volume.minNumber.toInt().toFloat()
|
||||
}
|
||||
} catch (e: NumberFormatException) {
|
||||
0f
|
||||
}
|
||||
volumeNum / KavitaConstants.VOLUME_NUMBER_OFFSET
|
||||
}
|
||||
}
|
||||
|
||||
url = when {
|
||||
type == ChapterType.SingleFileVolume && singleFileVolumeNumber != null ->
|
||||
"volume_${volume.id}"
|
||||
else -> chapter.id.toString()
|
||||
else -> "chapter_${chapter.id}"
|
||||
}
|
||||
|
||||
// url = chapter.id.toString()
|
||||
|
||||
if (chapter.fileCount > 1) {
|
||||
// salt/offset to recognize chapters with new merged part-chapters as new and hence unread
|
||||
chapter_number += 0.001f * chapter.fileCount
|
||||
@@ -185,11 +219,86 @@ class KavitaHelper {
|
||||
|
||||
date_upload = parseDateSafe(chapter.created)
|
||||
scanlator = when (type) {
|
||||
ChapterType.SingleFileVolume -> "Volume"
|
||||
ChapterType.SingleFileVolume -> if (isWebtoon) "Season" else "Volume"
|
||||
ChapterType.Special -> "Special"
|
||||
ChapterType.Issue -> "Issue"
|
||||
ChapterType.Chapter -> "Chapter"
|
||||
ChapterType.Regular -> "Chapter"
|
||||
ChapterType.Chapter -> if (isWebtoon) "Episode" else "Chapter"
|
||||
ChapterType.Regular -> if (isWebtoon) "Episode" else "Chapter"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatVolumeNumber(volume: VolumeDto): String {
|
||||
return when {
|
||||
volume.maxNumber > volume.minNumber ->
|
||||
"${removeTrailingZero(volume.minNumber)}-${removeTrailingZero(volume.maxNumber)}"
|
||||
else -> removeTrailingZero(volume.minNumber)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatChapterNumber(chapter: ChapterDto, padLength: Int = 2): String {
|
||||
val chapterNum = when {
|
||||
chapter.maxNumber > chapter.minNumber ->
|
||||
"${removeTrailingZero(chapter.minNumber)}-${removeTrailingZero(chapter.maxNumber)}"
|
||||
else -> removeTrailingZero(chapter.minNumber)
|
||||
}
|
||||
|
||||
// Only pad if it's a single whole number (not a range and not decimal)
|
||||
return if (!chapterNum.contains('-') && !chapterNum.contains('.')) {
|
||||
chapterNum.toIntOrNull()?.toString()?.padStart(padLength, '0') ?: chapterNum
|
||||
} else {
|
||||
chapterNum
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to remove .0 from whole numbers while preserving actual decimals
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun removeTrailingZero(number: Double): String {
|
||||
return if (number % 1 == 0.0) {
|
||||
number.toInt().toString()
|
||||
} else {
|
||||
// Use String.format to avoid scientific notation for very small decimals
|
||||
String.format("%.10f", number).replace(",", ".").trimEnd('0').trimEnd('.')
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanChapterTitle(
|
||||
originalTitle: String,
|
||||
context: ChapterTitleContext? = null,
|
||||
): String {
|
||||
var title = originalTitle.trim()
|
||||
val mangaTitle = context?.mangaTitle ?: ""
|
||||
|
||||
// Remove manga name if present
|
||||
if (mangaTitle.isNotBlank()) {
|
||||
title = title.replace(Regex("(?i)\\b${Regex.escape(mangaTitle)}\\b[\\s\\-:]*"), "").trim()
|
||||
}
|
||||
|
||||
// Remove chapter number patterns like c0116, c001, etc.
|
||||
title = title.replace(Regex("""(?i)\bc\d+\b"""), "").trim()
|
||||
|
||||
// Clean up problematic hyphen/space patterns
|
||||
title = title.replace(Regex("""\s*[-.]\s*[-.]\s*"""), " - ") // Handles -.-, - . -, etc.
|
||||
.replace(Regex("""\s*-\s*-\s*"""), " - ") // Double hyphens with spaces
|
||||
.replace(Regex("""^\s*[-.]\s*|\s*[-.]\s*$"""), "") // Leading/trailing hyphens or dots
|
||||
.trim()
|
||||
|
||||
return title.ifBlank { originalTitle }
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class to hold context information for cleaning chapter titles
|
||||
*/
|
||||
data class ChapterTitleContext(
|
||||
val mangaTitle: String = "",
|
||||
val chapterNumber: String = "",
|
||||
val volumeName: String = "",
|
||||
val isWebtoon: Boolean = false,
|
||||
)
|
||||
|
||||
fun isWebtoonOrLongStrip(tags: List<String>): Boolean {
|
||||
return tags.any {
|
||||
val normalized = it.trim().lowercase()
|
||||
normalized.contains("webtoon") || normalized.contains("long strip")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,6 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
val formatterDate = SimpleDateFormat("yyyy-MM-dd", Locale.US)
|
||||
.apply { timeZone = TimeZone.getTimeZone("UTC") }
|
||||
val formatterDateTime = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US)
|
||||
.apply { timeZone = TimeZone.getTimeZone("UTC") }
|
||||
|
||||
fun parseDateSafe(date: String?): Long {
|
||||
return date?.let {
|
||||
try {
|
||||
@@ -80,7 +75,7 @@ fun PreferenceScreen.addEditTextPreference(
|
||||
val result = text.isBlank() || validate?.invoke(text) ?: true
|
||||
|
||||
if (restartRequired && result) {
|
||||
Toast.makeText(context, "Restart Tachiyomi to apply new setting.", Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(context, "Restart Mihon to apply new setting.", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.extension.all.kavita.dto
|
||||
|
||||
import eu.kanade.tachiyomi.extension.all.kavita.KavitaConstants
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -8,7 +9,7 @@ interface ConvertibleToSManga {
|
||||
fun toSManga(baseUrl: String, apiUrl: String, apiKey: String): SManga
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Serializable // https://github.com/Kareadita/Kavita/blob/develop/API/Entities/Enums/MangaFormat.cs
|
||||
enum class MangaFormat(val format: Int) {
|
||||
Image(0),
|
||||
Archive(1),
|
||||
@@ -19,7 +20,6 @@ enum class MangaFormat(val format: Int) {
|
||||
|
||||
companion object {
|
||||
private val map = values().associateBy(MangaFormat::format)
|
||||
fun fromInt(type: Int): MangaFormat? = map[type]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,8 +189,9 @@ data class AuthorDto(
|
||||
@Serializable
|
||||
data class VolumeDto(
|
||||
val id: Int,
|
||||
val number: Int,
|
||||
val name: String,
|
||||
val minNumber: Double,
|
||||
val maxNumber: Double,
|
||||
val name: String = "",
|
||||
val pages: Int,
|
||||
val pagesRead: Int,
|
||||
val lastModified: String,
|
||||
@@ -202,27 +203,34 @@ data class VolumeDto(
|
||||
|
||||
@Serializable
|
||||
enum class ChapterType {
|
||||
Regular, // chapter with volume information
|
||||
Chapter, // manga chapter without volume information
|
||||
Regular, // Chapter with volume information
|
||||
Chapter, // Chapter without volume information
|
||||
SingleFileVolume,
|
||||
Special,
|
||||
Issue, // For comics
|
||||
;
|
||||
|
||||
companion object {
|
||||
private const val UNNUMBERED_VOLUME_NUMBER = -100_000
|
||||
|
||||
fun of(chapter: ChapterDto, volume: VolumeDto, libraryType: LibraryTypeEnum? = null): ChapterType =
|
||||
when {
|
||||
volume.number == 100_000 -> Special
|
||||
volume.number == -100_000 -> when (libraryType) {
|
||||
// Special
|
||||
volume.minNumber.toInt() == KavitaConstants.SPECIAL_NUMBER ||
|
||||
chapter.minNumber.toInt() == KavitaConstants.SPECIAL_NUMBER -> Special
|
||||
// Issue
|
||||
volume.minNumber.toInt() == UNNUMBERED_VOLUME_NUMBER -> when (libraryType) {
|
||||
LibraryTypeEnum.Comic, LibraryTypeEnum.ComicVine -> Issue
|
||||
LibraryTypeEnum.Manga, LibraryTypeEnum.LightNovel, LibraryTypeEnum.Book -> Chapter
|
||||
else -> Chapter // Default to Chapter for other types
|
||||
else -> Chapter
|
||||
}
|
||||
chapter.number == "-100000" -> SingleFileVolume
|
||||
// SingleFileVolume
|
||||
chapter.number == KavitaConstants.UNNUMBERED_VOLUME_STR -> SingleFileVolume
|
||||
// Regular
|
||||
volume.minNumber > 0 -> Regular
|
||||
// Everything else depends on library type
|
||||
else -> when (libraryType) {
|
||||
LibraryTypeEnum.Comic, LibraryTypeEnum.ComicVine -> Issue
|
||||
LibraryTypeEnum.Manga, LibraryTypeEnum.LightNovel, LibraryTypeEnum.Book -> Chapter
|
||||
else -> Regular
|
||||
else -> Chapter
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,12 +241,15 @@ data class ChapterDto(
|
||||
val id: Int,
|
||||
val range: String,
|
||||
val number: String,
|
||||
val minNumber: Double,
|
||||
val maxNumber: Double,
|
||||
val pages: Int,
|
||||
val isSpecial: Boolean,
|
||||
val title: String,
|
||||
val titleName: String,
|
||||
val titleName: String?,
|
||||
val pagesRead: Int,
|
||||
val coverImageLocked: Boolean,
|
||||
val coverImage: String,
|
||||
val volumeId: Int,
|
||||
val created: String,
|
||||
val lastModifiedUtc: String,
|
||||
@@ -259,13 +270,13 @@ data class ReadingListDto(
|
||||
val title: String,
|
||||
val coverImage: String? = null,
|
||||
val promoted: Boolean = false,
|
||||
val summary: String?,
|
||||
val summary: String? = null,
|
||||
val itemCount: Int,
|
||||
val startingYear: Int,
|
||||
val startingMonth: Int,
|
||||
val endingYear: Int,
|
||||
val endingMonth: Int,
|
||||
val ownerUserName: String?,
|
||||
val ownerUserName: String? = null,
|
||||
@SerialName("items") val items: List<ReadingListItemDto> = emptyList(),
|
||||
)
|
||||
|
||||
@@ -278,11 +289,11 @@ data class ReadingListItemDto(
|
||||
val seriesName: String,
|
||||
val chapterNumber: String?,
|
||||
val volumeNumber: String?,
|
||||
val chapterTitleName: String?,
|
||||
val chapterTitleName: String? = null,
|
||||
val volumeId: Int?,
|
||||
val title: String?,
|
||||
val summary: String?,
|
||||
val releaseDate: String?,
|
||||
val title: String? = null,
|
||||
val summary: String? = null,
|
||||
val releaseDate: String? = null,
|
||||
val libraryName: String?,
|
||||
@SerialName("readingListId") val readingListId: Int,
|
||||
)
|
||||
|
||||
@@ -22,6 +22,9 @@ data class MetadataTag(val id: Int, val title: String)
|
||||
@Serializable
|
||||
data class MetadataAgeRatings(val value: Int, val title: String)
|
||||
|
||||
@Serializable
|
||||
data class MetadataUserRatings(val value: Int, val title: String)
|
||||
|
||||
@Serializable
|
||||
data class MetadataLanguages(val isoCode: String, val title: String)
|
||||
|
||||
@@ -37,40 +40,3 @@ data class SmartFilter(
|
||||
val name: String,
|
||||
val filter: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MetadataPayload(
|
||||
val forceUseMetadataPayload: Boolean = true,
|
||||
var sorting: Int = 1,
|
||||
var sorting_asc: Boolean = true,
|
||||
var readStatus: ArrayList<String> = arrayListOf("notRead", "inProgress", "read"),
|
||||
var genres_i: ArrayList<Int> = arrayListOf(),
|
||||
var genres_e: ArrayList<Int> = arrayListOf(),
|
||||
var tags_i: ArrayList<Int> = arrayListOf(),
|
||||
var tags_e: ArrayList<Int> = arrayListOf(),
|
||||
var ageRating_i: ArrayList<Int> = arrayListOf(),
|
||||
var ageRating_e: ArrayList<Int> = arrayListOf(),
|
||||
var formats: ArrayList<Int> = arrayListOf(),
|
||||
var collections_i: ArrayList<Int> = arrayListOf(),
|
||||
var collections_e: ArrayList<Int> = arrayListOf(),
|
||||
var userRating: Int = 0,
|
||||
var people: ArrayList<Int> = arrayListOf(),
|
||||
var language_i: ArrayList<String> = arrayListOf(),
|
||||
var language_e: ArrayList<String> = arrayListOf(),
|
||||
var libraries_i: ArrayList<Int> = arrayListOf(),
|
||||
var libraries_e: ArrayList<Int> = arrayListOf(),
|
||||
var pubStatus: ArrayList<Int> = arrayListOf(),
|
||||
var seriesNameQuery: String = "",
|
||||
var releaseYearRangeMin: Int = 0,
|
||||
var releaseYearRangeMax: Int = 0,
|
||||
var peopleWriters: ArrayList<Int> = arrayListOf(),
|
||||
var peoplePenciller: ArrayList<Int> = arrayListOf(),
|
||||
var peopleInker: ArrayList<Int> = arrayListOf(),
|
||||
var peopleColorist: ArrayList<Int> = arrayListOf(),
|
||||
var peopleLetterer: ArrayList<Int> = arrayListOf(),
|
||||
var peopleCoverArtist: ArrayList<Int> = arrayListOf(),
|
||||
var peopleEditor: ArrayList<Int> = arrayListOf(),
|
||||
var peoplePublisher: ArrayList<Int> = arrayListOf(),
|
||||
var peopleCharacter: ArrayList<Int> = arrayListOf(),
|
||||
var peopleTranslator: ArrayList<Int> = arrayListOf(),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user