codex 1.4.0a0__py3-none-any.whl → 1.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of codex might be problematic. Click here for more details.
- codex/config_default.yaml +12 -4
- codex/db_functions.py +4 -2
- codex/integrity.py +17 -6
- codex/librarian/covers/coverd.py +3 -11
- codex/librarian/covers/create.py +15 -21
- codex/librarian/covers/tasks.py +2 -16
- codex/librarian/importer/aggregate_metadata.py +75 -41
- codex/librarian/importer/clean_metadata.py +30 -7
- codex/librarian/importer/create_fks.py +154 -55
- codex/librarian/importer/deleted.py +11 -2
- codex/librarian/importer/failed_imports.py +44 -5
- codex/librarian/importer/importerd.py +37 -12
- codex/librarian/importer/link_comics.py +54 -31
- codex/librarian/importer/moved.py +55 -11
- codex/librarian/importer/query_fks.py +210 -48
- codex/librarian/importer/tasks.py +7 -7
- codex/librarian/janitor/cleanup.py +17 -5
- codex/librarian/librariand.py +10 -0
- codex/librarian/watchdog/events.py +11 -14
- codex/librarian/watchdog/observers.py +5 -1
- codex/logger/loggerd.py +7 -3
- codex/logger/logging.py +1 -1
- codex/migrations/0024_comic_gtin_comic_story_arc_number.py +24 -0
- codex/migrations/0025_add_story_arc_number.py +83 -0
- codex/models.py +21 -11
- codex/search/backend.py +1 -1
- codex/search/indexes.py +1 -1
- codex/serializers/browser.py +1 -0
- codex/serializers/metadata.py +5 -1
- codex/serializers/models.py +16 -1
- codex/serializers/opds/v1.py +1 -0
- codex/serializers/opds/v2.py +5 -2
- codex/serializers/reader.py +55 -16
- codex/settings/settings.py +1 -1
- codex/static_root/assets/admin-12749881.ef0f50bac290.js +41 -0
- codex/static_root/assets/admin-12749881.ef0f50bac290.js.br +0 -0
- codex/static_root/assets/admin-12749881.ef0f50bac290.js.gz +0 -0
- codex/static_root/assets/admin-12749881.js +41 -0
- codex/static_root/assets/admin-12749881.js.br +0 -0
- codex/static_root/assets/admin-12749881.js.gz +0 -0
- codex/static_root/assets/admin-beda768d.a614eee46307.css +1 -0
- codex/static_root/assets/admin-beda768d.a614eee46307.css.br +0 -0
- codex/static_root/assets/admin-beda768d.a614eee46307.css.gz +0 -0
- codex/static_root/assets/admin-beda768d.css +1 -0
- codex/static_root/assets/admin-beda768d.css.br +0 -0
- codex/static_root/assets/admin-beda768d.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css +1 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.css +1 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js +1 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.js +1 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.js.gz +0 -0
- codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css +1 -0
- codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css.br +0 -0
- codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css.gz +0 -0
- codex/static_root/assets/browser-7f7d7134.css +1 -0
- codex/static_root/assets/browser-7f7d7134.css.br +0 -0
- codex/static_root/assets/browser-7f7d7134.css.gz +0 -0
- codex/static_root/assets/browser-af622672.d51aca96d64d.js +1 -0
- codex/static_root/assets/browser-af622672.d51aca96d64d.js.br +0 -0
- codex/static_root/assets/browser-af622672.d51aca96d64d.js.gz +0 -0
- codex/static_root/assets/browser-af622672.js +1 -0
- codex/static_root/assets/browser-af622672.js.br +0 -0
- codex/static_root/assets/browser-af622672.js.gz +0 -0
- codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js +1 -0
- codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js.br +0 -0
- codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js.gz +0 -0
- codex/static_root/assets/http-error-5e17b794.js +1 -0
- codex/static_root/assets/http-error-5e17b794.js.br +0 -0
- codex/static_root/assets/http-error-5e17b794.js.gz +0 -0
- codex/static_root/assets/{main-a6ac9581.2fd9e52cbcc3.css → main-0898f4bb.181e0145c642.css} +1 -1
- codex/static_root/assets/main-0898f4bb.181e0145c642.css.br +0 -0
- codex/static_root/assets/{main-a6ac9581.2fd9e52cbcc3.css.gz → main-0898f4bb.181e0145c642.css.gz} +0 -0
- codex/static_root/assets/{main-a6ac9581.css → main-0898f4bb.css} +1 -1
- codex/static_root/assets/main-0898f4bb.css.br +0 -0
- codex/static_root/assets/{main-a6ac9581.css.gz → main-0898f4bb.css.gz} +0 -0
- codex/static_root/assets/main-9e76a4c3.6844a407d14c.js +1 -0
- codex/static_root/assets/main-9e76a4c3.6844a407d14c.js.br +0 -0
- codex/static_root/assets/main-9e76a4c3.6844a407d14c.js.gz +0 -0
- codex/static_root/assets/main-9e76a4c3.js +1 -0
- codex/static_root/assets/main-9e76a4c3.js.br +0 -0
- codex/static_root/assets/main-9e76a4c3.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js +1 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js.br +0 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.js +1 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.js.br +0 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.js.gz +0 -0
- codex/static_root/assets/{metadata-dialog-785c4cfc.694a251cda37.css → metadata-dialog-cb306ffd.cc304996d7bb.css} +1 -1
- codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css.br +0 -0
- codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css.gz +0 -0
- codex/static_root/assets/{metadata-dialog-785c4cfc.css → metadata-dialog-cb306ffd.css} +1 -1
- codex/static_root/assets/metadata-dialog-cb306ffd.css.br +0 -0
- codex/static_root/assets/metadata-dialog-cb306ffd.css.gz +0 -0
- codex/static_root/assets/{page-pdf-abfd509d.3870dab8eaf4.js → page-pdf-157ba97e.613d7c2beb77.js} +61 -51
- codex/static_root/assets/page-pdf-157ba97e.613d7c2beb77.js.br +0 -0
- codex/static_root/assets/page-pdf-157ba97e.613d7c2beb77.js.gz +0 -0
- codex/static_root/assets/{page-pdf-abfd509d.js → page-pdf-157ba97e.js} +61 -51
- codex/static_root/assets/page-pdf-157ba97e.js.br +0 -0
- codex/static_root/assets/page-pdf-157ba97e.js.gz +0 -0
- codex/static_root/assets/reader-36266549.0b2cf1291f27.js +1 -0
- codex/static_root/assets/reader-36266549.0b2cf1291f27.js.br +0 -0
- codex/static_root/assets/reader-36266549.0b2cf1291f27.js.gz +0 -0
- codex/static_root/assets/reader-36266549.js +1 -0
- codex/static_root/assets/reader-36266549.js.br +0 -0
- codex/static_root/assets/reader-36266549.js.gz +0 -0
- codex/static_root/assets/reader-7f004141.506eecc6954b.css +1 -0
- codex/static_root/assets/reader-7f004141.506eecc6954b.css.br +0 -0
- codex/static_root/assets/reader-7f004141.506eecc6954b.css.gz +0 -0
- codex/static_root/assets/reader-7f004141.css +1 -0
- codex/static_root/assets/reader-7f004141.css.br +0 -0
- codex/static_root/assets/reader-7f004141.css.gz +0 -0
- codex/static_root/js/choices-admin.24cecf0a0568.json +1 -0
- codex/static_root/js/choices-admin.24cecf0a0568.json.br +0 -0
- codex/static_root/js/choices-admin.24cecf0a0568.json.gz +0 -0
- codex/static_root/js/choices-admin.json +1 -1
- codex/static_root/js/choices-admin.json.br +0 -0
- codex/static_root/js/choices-admin.json.gz +0 -0
- codex/static_root/js/choices.8c58714cf5b2.json +1 -0
- codex/static_root/js/choices.8c58714cf5b2.json.br +5 -0
- codex/static_root/js/choices.8c58714cf5b2.json.gz +0 -0
- codex/static_root/js/choices.json +1 -1
- codex/static_root/js/choices.json.br +0 -0
- codex/static_root/js/choices.json.gz +0 -0
- codex/static_root/{manifest.64a989215af8.json → manifest.d2f93a519ada.json} +34 -34
- codex/static_root/manifest.d2f93a519ada.json.br +0 -0
- codex/static_root/manifest.d2f93a519ada.json.gz +0 -0
- codex/static_root/manifest.json +34 -34
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/templates/headers-script-globals.html +1 -1
- codex/templates/{opds → opds_v1}/index.xml +3 -1
- codex/templates/{opds/opensearch.xml → opds_v1/opensearch_v1.xml} +1 -1
- codex/templates/search/indexes/codex/comic_text.txt +2 -2
- codex/urls/converters.py +1 -1
- codex/urls/opds/authentication.py +1 -1
- codex/urls/opds/root.py +8 -12
- codex/urls/opds/v1.py +12 -5
- codex/urls/opds/v2.py +2 -2
- codex/views/admin/tasks.py +6 -1
- codex/views/bookmark.py +2 -2
- codex/views/browser/base.py +23 -7
- codex/views/browser/browser.py +66 -56
- codex/views/browser/browser_annotations.py +159 -50
- codex/views/browser/browser_order_by.py +51 -105
- codex/views/browser/choices.py +75 -38
- codex/views/browser/filters/bookmark.py +6 -9
- codex/views/browser/filters/field.py +9 -6
- codex/views/browser/filters/group.py +12 -27
- codex/views/browser/filters/search.py +5 -10
- codex/views/browser/metadata.py +44 -19
- codex/views/download.py +1 -1
- codex/views/frontend.py +2 -3
- codex/views/mixins.py +15 -2
- codex/views/opds/const.py +8 -1
- codex/views/opds/util.py +37 -1
- codex/views/opds/v1/__init__.py +1 -1
- codex/views/opds/v1/data.py +21 -0
- codex/views/opds/v1/entry/__init__.py +1 -0
- codex/views/opds/v1/entry/data.py +23 -0
- codex/views/opds/v1/entry/entry.py +151 -0
- codex/views/opds/v1/entry/links.py +135 -0
- codex/views/opds/v1/facets.py +190 -0
- codex/views/opds/v1/feed.py +199 -0
- codex/views/opds/v1/links.py +198 -0
- codex/views/opds/{opensearch.py → v1/opensearch_v1.py} +3 -3
- codex/views/opds/v2/__init__.py +1 -1
- codex/views/opds/v2/const.py +10 -2
- codex/views/opds/v2/feed.py +82 -21
- codex/views/opds/v2/links.py +1 -1
- codex/views/opds/v2/publications.py +1 -1
- codex/views/opds/v2/top_links.py +1 -1
- codex/views/reader/page.py +6 -7
- codex/views/reader/reader.py +191 -61
- codex/views/session.py +2 -1
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/METADATA +10 -41
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/RECORD +187 -185
- codex/librarian/importer/db_ops.py +0 -248
- codex/pdf.py +0 -115
- codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js +0 -48
- codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js.br +0 -0
- codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js.gz +0 -0
- codex/static_root/assets/admin-73d93dc7.js +0 -48
- codex/static_root/assets/admin-73d93dc7.js.br +0 -0
- codex/static_root/assets/admin-73d93dc7.js.gz +0 -0
- codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css +0 -1
- codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css.br +0 -0
- codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css.gz +0 -0
- codex/static_root/assets/admin-79555229.css +0 -1
- codex/static_root/assets/admin-79555229.css.br +0 -0
- codex/static_root/assets/admin-79555229.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js +0 -1
- codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-64bcc083.js +0 -1
- codex/static_root/assets/admin-drawer-panel-64bcc083.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-64bcc083.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css +0 -1
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css.br +0 -2
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.css +0 -1
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.css.br +0 -2
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.css.gz +0 -0
- codex/static_root/assets/browser-7325db61.css +0 -1
- codex/static_root/assets/browser-7325db61.css.br +0 -0
- codex/static_root/assets/browser-7325db61.css.gz +0 -0
- codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css +0 -1
- codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css.br +0 -0
- codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css.gz +0 -0
- codex/static_root/assets/browser-d2caeed7.2262000a6d55.js +0 -1
- codex/static_root/assets/browser-d2caeed7.2262000a6d55.js.br +0 -0
- codex/static_root/assets/browser-d2caeed7.2262000a6d55.js.gz +0 -0
- codex/static_root/assets/browser-d2caeed7.js +0 -1
- codex/static_root/assets/browser-d2caeed7.js.br +0 -0
- codex/static_root/assets/browser-d2caeed7.js.gz +0 -0
- codex/static_root/assets/http-error-0221c37d.480d5066da92.js +0 -1
- codex/static_root/assets/http-error-0221c37d.480d5066da92.js.br +0 -0
- codex/static_root/assets/http-error-0221c37d.480d5066da92.js.gz +0 -0
- codex/static_root/assets/http-error-0221c37d.js +0 -1
- codex/static_root/assets/http-error-0221c37d.js.br +0 -0
- codex/static_root/assets/http-error-0221c37d.js.gz +0 -0
- codex/static_root/assets/main-a6ac9581.2fd9e52cbcc3.css.br +0 -0
- codex/static_root/assets/main-a6ac9581.css.br +0 -0
- codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js +0 -1
- codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js.br +0 -0
- codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js.gz +0 -0
- codex/static_root/assets/main-e33dcfb0.js +0 -1
- codex/static_root/assets/main-e33dcfb0.js.br +0 -0
- codex/static_root/assets/main-e33dcfb0.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-785c4cfc.694a251cda37.css.br +0 -0
- codex/static_root/assets/metadata-dialog-785c4cfc.694a251cda37.css.gz +0 -0
- codex/static_root/assets/metadata-dialog-785c4cfc.css.br +0 -0
- codex/static_root/assets/metadata-dialog-785c4cfc.css.gz +0 -0
- codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js +0 -1
- codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js.br +0 -0
- codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-8b0e8aaa.js +0 -1
- codex/static_root/assets/metadata-dialog-8b0e8aaa.js.br +0 -0
- codex/static_root/assets/metadata-dialog-8b0e8aaa.js.gz +0 -0
- codex/static_root/assets/page-pdf-abfd509d.3870dab8eaf4.js.br +0 -0
- codex/static_root/assets/page-pdf-abfd509d.3870dab8eaf4.js.gz +0 -0
- codex/static_root/assets/page-pdf-abfd509d.js.br +0 -0
- codex/static_root/assets/page-pdf-abfd509d.js.gz +0 -0
- codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css +0 -1
- codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css.br +0 -0
- codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css.gz +0 -0
- codex/static_root/assets/reader-a8b8f766.css +0 -1
- codex/static_root/assets/reader-a8b8f766.css.br +0 -0
- codex/static_root/assets/reader-a8b8f766.css.gz +0 -0
- codex/static_root/assets/reader-fe9345d2.759c31f82998.js +0 -1
- codex/static_root/assets/reader-fe9345d2.759c31f82998.js.br +0 -0
- codex/static_root/assets/reader-fe9345d2.759c31f82998.js.gz +0 -0
- codex/static_root/assets/reader-fe9345d2.js +0 -1
- codex/static_root/assets/reader-fe9345d2.js.br +0 -0
- codex/static_root/assets/reader-fe9345d2.js.gz +0 -0
- codex/static_root/js/choices-admin.3d958ea7f83b.json +0 -1
- codex/static_root/js/choices-admin.3d958ea7f83b.json.br +0 -0
- codex/static_root/js/choices-admin.3d958ea7f83b.json.gz +0 -0
- codex/static_root/js/choices.6bfc2a3d293f.json +0 -1
- codex/static_root/js/choices.6bfc2a3d293f.json.br +0 -0
- codex/static_root/js/choices.6bfc2a3d293f.json.gz +0 -0
- codex/static_root/manifest.64a989215af8.json.br +0 -0
- codex/static_root/manifest.64a989215af8.json.gz +0 -0
- codex/urls/opds/opensearch.py +0 -18
- codex/views/opds/v1/browser.py +0 -346
- codex/views/opds/v1/entry.py +0 -278
- codex/views/opds/v1/start.py +0 -28
- codex/views/opds/v1/util.py +0 -162
- codex/views/opds/v2/start.py +0 -28
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/LICENSE +0 -0
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/WHEEL +0 -0
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/entry_points.txt +0 -0
codex/views/opds/v1/entry.py
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
"""OPDS Entry."""
|
|
2
|
-
import json
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from datetime import datetime, timezone
|
|
5
|
-
from urllib.parse import quote_plus, urlencode
|
|
6
|
-
|
|
7
|
-
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
8
|
-
from django.urls import reverse
|
|
9
|
-
|
|
10
|
-
from codex.logger.logging import get_logger
|
|
11
|
-
from codex.models import Comic
|
|
12
|
-
from codex.views.opds.const import (
|
|
13
|
-
AUTHOR_ROLES,
|
|
14
|
-
BLANK_TITLE,
|
|
15
|
-
MimeType,
|
|
16
|
-
Rel,
|
|
17
|
-
)
|
|
18
|
-
from codex.views.opds.util import (
|
|
19
|
-
get_creator_people,
|
|
20
|
-
get_m2m_objects,
|
|
21
|
-
update_href_query_params,
|
|
22
|
-
)
|
|
23
|
-
from codex.views.opds.v1.util import (
|
|
24
|
-
OPDSLink,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
LOG = get_logger(__name__)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@dataclass
|
|
31
|
-
class OPDS1EntryObject:
|
|
32
|
-
"""Fake entry db object for top link & facet entries."""
|
|
33
|
-
|
|
34
|
-
query_params: dict
|
|
35
|
-
group: str = ""
|
|
36
|
-
pk: int = 0
|
|
37
|
-
name: str = ""
|
|
38
|
-
summary: str = ""
|
|
39
|
-
fake: bool = True
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class OPDS1Entry:
|
|
43
|
-
"""An OPDS entry object."""
|
|
44
|
-
|
|
45
|
-
_DATE_FORMAT_BASE = "%Y-%m-%dT%H:%M:%S"
|
|
46
|
-
DATE_FORMAT_MS = _DATE_FORMAT_BASE + ".%f%z"
|
|
47
|
-
DATE_FORMAT = _DATE_FORMAT_BASE + "%z"
|
|
48
|
-
DATE_FORMATS = (DATE_FORMAT_MS, DATE_FORMAT)
|
|
49
|
-
|
|
50
|
-
def __init__(self, obj, query_params, extra_data):
|
|
51
|
-
"""Initialize params."""
|
|
52
|
-
self.obj = obj
|
|
53
|
-
self.fake = isinstance(self.obj, OPDS1EntryObject)
|
|
54
|
-
self.query_params = query_params
|
|
55
|
-
self.acquision_groups, self.issue_max, self.metadata = extra_data
|
|
56
|
-
|
|
57
|
-
@property
|
|
58
|
-
def id_tag(self):
|
|
59
|
-
"""GUID is a nav url."""
|
|
60
|
-
# Id top links by query params but not regular entries.
|
|
61
|
-
return self._nav_href(metadata=self.metadata)
|
|
62
|
-
|
|
63
|
-
@property
|
|
64
|
-
def title(self):
|
|
65
|
-
"""Compute the item title."""
|
|
66
|
-
result = ""
|
|
67
|
-
try:
|
|
68
|
-
parts = []
|
|
69
|
-
if not self.fake:
|
|
70
|
-
group = self.obj.group
|
|
71
|
-
if group == "i":
|
|
72
|
-
parts.append(self.obj.publisher_name)
|
|
73
|
-
elif group == "v":
|
|
74
|
-
parts.append(self.obj.series_name)
|
|
75
|
-
elif group == "c":
|
|
76
|
-
title = Comic.get_title(self.obj, issue_max=self.issue_max)
|
|
77
|
-
parts.append(title)
|
|
78
|
-
|
|
79
|
-
if name := self.obj.name:
|
|
80
|
-
parts.append(name)
|
|
81
|
-
|
|
82
|
-
result = " ".join(filter(None, parts))
|
|
83
|
-
except Exception as exc:
|
|
84
|
-
LOG.exception(exc)
|
|
85
|
-
|
|
86
|
-
if not result:
|
|
87
|
-
result = BLANK_TITLE
|
|
88
|
-
return result
|
|
89
|
-
|
|
90
|
-
@property
|
|
91
|
-
def issued(self):
|
|
92
|
-
"""Return the published date."""
|
|
93
|
-
if self.obj.group == "c":
|
|
94
|
-
return self.obj.date
|
|
95
|
-
return None
|
|
96
|
-
|
|
97
|
-
@property
|
|
98
|
-
def publisher(self):
|
|
99
|
-
"""Return the publisher."""
|
|
100
|
-
return self.obj.publisher_name
|
|
101
|
-
|
|
102
|
-
def _get_datefield(self, key):
|
|
103
|
-
result = None
|
|
104
|
-
if not self.fake and (value := getattr(self.obj, key, None)):
|
|
105
|
-
for date_format in self.DATE_FORMATS:
|
|
106
|
-
try:
|
|
107
|
-
if isinstance(value, str):
|
|
108
|
-
result = datetime.strptime(value, date_format).astimezone(
|
|
109
|
-
timezone.utc
|
|
110
|
-
)
|
|
111
|
-
if isinstance(value, datetime):
|
|
112
|
-
result = value.astimezone(timezone.utc).strftime(date_format)
|
|
113
|
-
break
|
|
114
|
-
except ValueError:
|
|
115
|
-
pass
|
|
116
|
-
return result
|
|
117
|
-
|
|
118
|
-
@property
|
|
119
|
-
def updated(self):
|
|
120
|
-
"""When the entry was last updated."""
|
|
121
|
-
return self._get_datefield("updated_at")
|
|
122
|
-
|
|
123
|
-
@property
|
|
124
|
-
def published(self):
|
|
125
|
-
"""When the entry was created."""
|
|
126
|
-
return self._get_datefield("created_at")
|
|
127
|
-
|
|
128
|
-
@property
|
|
129
|
-
def language(self):
|
|
130
|
-
"""Return the entry language."""
|
|
131
|
-
return self.obj.language
|
|
132
|
-
|
|
133
|
-
@property
|
|
134
|
-
def summary(self):
|
|
135
|
-
"""Return a child count or comic summary."""
|
|
136
|
-
if self.obj.group == "c":
|
|
137
|
-
desc = self.obj.summary
|
|
138
|
-
else:
|
|
139
|
-
children = self.obj.child_count
|
|
140
|
-
desc = f"{children} issues"
|
|
141
|
-
return desc
|
|
142
|
-
|
|
143
|
-
def _thumb_link(self):
|
|
144
|
-
if self.fake:
|
|
145
|
-
return None
|
|
146
|
-
cover_pk = self.obj.cover_pk
|
|
147
|
-
if cover_pk:
|
|
148
|
-
kwargs = {"pk": cover_pk}
|
|
149
|
-
href = reverse("opds:bin:cover", kwargs=kwargs)
|
|
150
|
-
elif cover_pk == 0:
|
|
151
|
-
href = staticfiles_storage.url("img/missing_cover.webp")
|
|
152
|
-
else:
|
|
153
|
-
return None
|
|
154
|
-
return OPDSLink(Rel.THUMBNAIL, href, "image/webp")
|
|
155
|
-
|
|
156
|
-
def _image_link(self):
|
|
157
|
-
if self.fake:
|
|
158
|
-
return None
|
|
159
|
-
cover_pk = self.obj.cover_pk
|
|
160
|
-
if cover_pk:
|
|
161
|
-
kwargs = {"pk": cover_pk, "page": 0}
|
|
162
|
-
href = reverse("opds:bin:page", kwargs=kwargs)
|
|
163
|
-
mime_type = "image/jpeg"
|
|
164
|
-
elif cover_pk == 0:
|
|
165
|
-
href = staticfiles_storage.url("img/missing_cover.webp")
|
|
166
|
-
mime_type = "image/webp"
|
|
167
|
-
else:
|
|
168
|
-
return None
|
|
169
|
-
return OPDSLink(Rel.IMAGE, href, mime_type)
|
|
170
|
-
|
|
171
|
-
def _nav_href(self, metadata=False):
|
|
172
|
-
kwargs = {"group": self.obj.group, "pk": self.obj.pk, "page": 1}
|
|
173
|
-
href = reverse("opds:v1:browser", kwargs=kwargs)
|
|
174
|
-
qps = {} if self.fake or self.obj.group == "c" else self.query_params
|
|
175
|
-
|
|
176
|
-
if metadata:
|
|
177
|
-
qps.update({"opdsMetadata": 1})
|
|
178
|
-
return update_href_query_params(href, self.query_params, qps)
|
|
179
|
-
|
|
180
|
-
def _nav_link(self, metadata=False):
|
|
181
|
-
group = self.obj.group
|
|
182
|
-
|
|
183
|
-
if group in self.acquision_groups:
|
|
184
|
-
mime_type = MimeType.ENTRY_CATALOG if metadata else MimeType.ACQUISITION
|
|
185
|
-
else:
|
|
186
|
-
mime_type = MimeType.NAV
|
|
187
|
-
|
|
188
|
-
href = self._nav_href(metadata)
|
|
189
|
-
thr_count = 0 if self.fake else self.obj.child_count
|
|
190
|
-
rel = Rel.ALTERNATE if metadata else "subsection"
|
|
191
|
-
|
|
192
|
-
return OPDSLink(rel, href, mime_type, thr_count=thr_count)
|
|
193
|
-
|
|
194
|
-
def _download_link(self):
|
|
195
|
-
pk = self.obj.pk
|
|
196
|
-
if not pk:
|
|
197
|
-
return None
|
|
198
|
-
fn = Comic.get_filename(self.obj)
|
|
199
|
-
fn = quote_plus(fn)
|
|
200
|
-
kwargs = {"pk": pk, "filename": fn}
|
|
201
|
-
href = reverse("opds:bin:download", kwargs=kwargs)
|
|
202
|
-
return OPDSLink(Rel.ACQUISITION, href, MimeType.DOWNLOAD, length=self.obj.size)
|
|
203
|
-
|
|
204
|
-
def _stream_link(self):
|
|
205
|
-
pk = self.obj.pk
|
|
206
|
-
if not pk:
|
|
207
|
-
return None
|
|
208
|
-
base_url = reverse("opds:v1:start")
|
|
209
|
-
href = f"{base_url}c/{pk}/"
|
|
210
|
-
href += "{pageNumber}/page.jpg?bookmark=1"
|
|
211
|
-
count = self.obj.page_count
|
|
212
|
-
page = self.obj.page
|
|
213
|
-
bookmark_updated_at = self.obj.bookmark_updated_at
|
|
214
|
-
return OPDSLink(
|
|
215
|
-
Rel.STREAM,
|
|
216
|
-
href,
|
|
217
|
-
MimeType.STREAM,
|
|
218
|
-
pse_count=count,
|
|
219
|
-
pse_last_read=page,
|
|
220
|
-
pse_last_read_date=bookmark_updated_at,
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
@property
|
|
224
|
-
def links(self):
|
|
225
|
-
"""Create all entry links."""
|
|
226
|
-
result = []
|
|
227
|
-
if thumb := self._thumb_link():
|
|
228
|
-
result += [thumb]
|
|
229
|
-
if image := self._image_link():
|
|
230
|
-
result += [image]
|
|
231
|
-
|
|
232
|
-
if self.obj.group == "c" and not self.fake:
|
|
233
|
-
if download := self._download_link():
|
|
234
|
-
result += [download]
|
|
235
|
-
if stream := self._stream_link():
|
|
236
|
-
result += [stream]
|
|
237
|
-
if not self.metadata and (metadata := self._nav_link(metadata=True)):
|
|
238
|
-
result += [metadata]
|
|
239
|
-
elif nav := self._nav_link():
|
|
240
|
-
result += [nav]
|
|
241
|
-
|
|
242
|
-
return result
|
|
243
|
-
|
|
244
|
-
@staticmethod
|
|
245
|
-
def _add_url_to_obj(objs, filter_key):
|
|
246
|
-
"""Add filter urls to objects."""
|
|
247
|
-
kwargs = {"group": "s", "pk": 0, "page": 1}
|
|
248
|
-
url_base = reverse("opds:v1:browser", kwargs=kwargs)
|
|
249
|
-
result = []
|
|
250
|
-
for obj in objs:
|
|
251
|
-
qp = {"filters": json.dumps({filter_key: [obj.pk]})}
|
|
252
|
-
qp = urlencode(qp)
|
|
253
|
-
obj.url = url_base + "?" + qp
|
|
254
|
-
result.append(obj)
|
|
255
|
-
return result
|
|
256
|
-
|
|
257
|
-
@property
|
|
258
|
-
def authors(self):
|
|
259
|
-
"""Get Author names."""
|
|
260
|
-
if not self.metadata:
|
|
261
|
-
return []
|
|
262
|
-
people = get_creator_people(self.obj.pk, AUTHOR_ROLES)
|
|
263
|
-
return self._add_url_to_obj(people, "creators")
|
|
264
|
-
|
|
265
|
-
@property
|
|
266
|
-
def contributors(self):
|
|
267
|
-
"""Get Contributor names."""
|
|
268
|
-
if not self.metadata:
|
|
269
|
-
return []
|
|
270
|
-
people = get_creator_people(self.obj.pk, AUTHOR_ROLES, exclude=True)
|
|
271
|
-
return self._add_url_to_obj(people, "creators")
|
|
272
|
-
|
|
273
|
-
@property
|
|
274
|
-
def category_groups(self):
|
|
275
|
-
"""Get Category labels."""
|
|
276
|
-
if not self.metadata:
|
|
277
|
-
return {}
|
|
278
|
-
return get_m2m_objects(self.obj.pk)
|
codex/views/opds/v1/start.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""OPDS start view."""
|
|
2
|
-
from django.http.response import HttpResponseRedirect
|
|
3
|
-
from django.urls import reverse
|
|
4
|
-
|
|
5
|
-
from codex.serializers.choices import DEFAULTS
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def opds_1_start_view(request):
|
|
9
|
-
"""Redirect to start view, forwarding query strings and auth."""
|
|
10
|
-
kwargs = DEFAULTS["route"]
|
|
11
|
-
url = reverse("opds:v1:browser", kwargs=kwargs)
|
|
12
|
-
|
|
13
|
-
# Forward the query string.
|
|
14
|
-
path = request.get_full_path()
|
|
15
|
-
if path:
|
|
16
|
-
parts = path.split("?")
|
|
17
|
-
if len(parts) >= 2: # noqa PLR2004
|
|
18
|
-
parts[0] = url
|
|
19
|
-
url = "?".join(parts)
|
|
20
|
-
|
|
21
|
-
response = HttpResponseRedirect(url)
|
|
22
|
-
|
|
23
|
-
# Forward authorization.
|
|
24
|
-
auth_header = request.META.get("HTTP_AUTHORIZATION")
|
|
25
|
-
if auth_header:
|
|
26
|
-
response["HTTP_AUTHORIZATION"] = auth_header
|
|
27
|
-
|
|
28
|
-
return response
|
codex/views/opds/v1/util.py
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
"""OPDS 1 Utility classes."""
|
|
2
|
-
from collections import defaultdict
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from typing import Optional, Union
|
|
6
|
-
|
|
7
|
-
from comicbox.metadata.comic_json import json
|
|
8
|
-
|
|
9
|
-
from codex.views.opds.const import MimeType, Rel
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class OpdsNs:
|
|
13
|
-
"""xml namespaces."""
|
|
14
|
-
|
|
15
|
-
CATALOG = "http://opds-spec.org/2010/catalog"
|
|
16
|
-
ACQUISITION = "http://opds-spec.org/2010/acquisition"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class UserAgents:
|
|
20
|
-
"""Control whether to hack in facets with nav links."""
|
|
21
|
-
|
|
22
|
-
CLIENT_REORDERS = ("Chunky",)
|
|
23
|
-
FACET_SUPPORT = ("yar",) # kybooks
|
|
24
|
-
# NO_FACET_SUPPORT "Panels", "Chunky", "PocketBook"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class TopRoutes:
|
|
28
|
-
"""Routes for top groups."""
|
|
29
|
-
|
|
30
|
-
PUBLISHER = {"group": "p", "pk": 0}
|
|
31
|
-
SERIES = {"group": "s", "pk": 0}
|
|
32
|
-
FOLDER = {"group": "f", "pk": 0}
|
|
33
|
-
ROOT = {"group": "r", "pk": 0}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@dataclass
|
|
37
|
-
class FacetGroup:
|
|
38
|
-
"""An opds:facetGroup."""
|
|
39
|
-
|
|
40
|
-
title_prefix: str
|
|
41
|
-
query_param: str
|
|
42
|
-
glyph: str
|
|
43
|
-
facets: tuple
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@dataclass
|
|
47
|
-
class Facet:
|
|
48
|
-
"""An OPDS facet."""
|
|
49
|
-
|
|
50
|
-
value: str
|
|
51
|
-
title: str
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@dataclass
|
|
55
|
-
class TopLink:
|
|
56
|
-
"""A non standard root link when facets are unsupported."""
|
|
57
|
-
|
|
58
|
-
kwargs: dict
|
|
59
|
-
rel: str
|
|
60
|
-
mime_type: str
|
|
61
|
-
query_params: defaultdict[str, Union[str, bool]]
|
|
62
|
-
glyph: str
|
|
63
|
-
title: str
|
|
64
|
-
desc: str
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class TopLinks:
|
|
68
|
-
"""Top link definitions."""
|
|
69
|
-
|
|
70
|
-
START = TopLink(
|
|
71
|
-
TopRoutes.ROOT,
|
|
72
|
-
Rel.START,
|
|
73
|
-
MimeType.NAV,
|
|
74
|
-
defaultdict(),
|
|
75
|
-
"⌂",
|
|
76
|
-
"Start of the catalog",
|
|
77
|
-
"",
|
|
78
|
-
)
|
|
79
|
-
ALL = (START,)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class RootTopLinks:
|
|
83
|
-
"""Top Links that only appear at the root."""
|
|
84
|
-
|
|
85
|
-
NEW = TopLink(
|
|
86
|
-
TopRoutes.SERIES,
|
|
87
|
-
Rel.SORT_NEW,
|
|
88
|
-
MimeType.ACQUISITION,
|
|
89
|
-
defaultdict(None, {"orderBy": "created_at", "orderReverse": True}),
|
|
90
|
-
"📥",
|
|
91
|
-
"Recently Added",
|
|
92
|
-
"",
|
|
93
|
-
)
|
|
94
|
-
FEATURED = TopLink(
|
|
95
|
-
TopRoutes.SERIES,
|
|
96
|
-
Rel.FEATURED,
|
|
97
|
-
MimeType.NAV,
|
|
98
|
-
defaultdict(
|
|
99
|
-
None, {"orderBy": "date", "filters": json.dumps({"bookmark": "UNREAD"})}
|
|
100
|
-
),
|
|
101
|
-
"📚",
|
|
102
|
-
"Oldest Unread",
|
|
103
|
-
"Unread issues, oldest published first",
|
|
104
|
-
)
|
|
105
|
-
ALL = (NEW, FEATURED)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class FacetGroups:
|
|
109
|
-
"""Facet Group definitions."""
|
|
110
|
-
|
|
111
|
-
ORDER_BY = FacetGroup(
|
|
112
|
-
"Order By",
|
|
113
|
-
"orderBy",
|
|
114
|
-
"➠",
|
|
115
|
-
(
|
|
116
|
-
Facet("date", "Date"),
|
|
117
|
-
Facet("sort_name", "Name"),
|
|
118
|
-
),
|
|
119
|
-
)
|
|
120
|
-
ORDER_REVERSE = FacetGroup(
|
|
121
|
-
"Order",
|
|
122
|
-
"orderReverse",
|
|
123
|
-
"⇕",
|
|
124
|
-
(Facet("false", "Ascending"), Facet("true", "Descending")),
|
|
125
|
-
)
|
|
126
|
-
ALL = (ORDER_BY, ORDER_REVERSE)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
class RootFacetGroups:
|
|
130
|
-
"""Facet Groups that only appear at the root."""
|
|
131
|
-
|
|
132
|
-
TOP_GROUP = FacetGroup(
|
|
133
|
-
"",
|
|
134
|
-
"topGroup",
|
|
135
|
-
"⊙",
|
|
136
|
-
(Facet("p", "Publishers View"), Facet("s", "Series View")),
|
|
137
|
-
)
|
|
138
|
-
ALL = (TOP_GROUP,)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
DEFAULT_FACETS = {
|
|
142
|
-
"topGroup": "p",
|
|
143
|
-
"orderBy": "sort_name",
|
|
144
|
-
"orderReverse": "false",
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
@dataclass
|
|
149
|
-
class OPDSLink:
|
|
150
|
-
"""An OPDS Link."""
|
|
151
|
-
|
|
152
|
-
rel: str
|
|
153
|
-
href: str
|
|
154
|
-
mime_type: str
|
|
155
|
-
title: str = ""
|
|
156
|
-
length: int = 0
|
|
157
|
-
facet_group: str = ""
|
|
158
|
-
facet_active: bool = False
|
|
159
|
-
thr_count: int = 0
|
|
160
|
-
pse_count: int = 0
|
|
161
|
-
pse_last_read: int = 0
|
|
162
|
-
pse_last_read_date: Optional[datetime] = None
|
codex/views/opds/v2/start.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""OPDS 2 start view."""
|
|
2
|
-
from django.http.response import HttpResponseRedirect
|
|
3
|
-
from django.urls import reverse
|
|
4
|
-
|
|
5
|
-
from codex.serializers.choices import DEFAULTS
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def opds_2_start_view(request):
|
|
9
|
-
"""Redirect to start view, forwarding query strings and auth."""
|
|
10
|
-
kwargs = DEFAULTS["route"]
|
|
11
|
-
url = reverse("opds:v2:feed", kwargs=kwargs)
|
|
12
|
-
|
|
13
|
-
# Forward the query string.
|
|
14
|
-
path = request.get_full_path()
|
|
15
|
-
if path:
|
|
16
|
-
parts = path.split("?")
|
|
17
|
-
if len(parts) >= 2: # noqa PLR2004
|
|
18
|
-
parts[0] = url
|
|
19
|
-
url = "?".join(parts)
|
|
20
|
-
|
|
21
|
-
response = HttpResponseRedirect(url)
|
|
22
|
-
|
|
23
|
-
# Forward authorization.
|
|
24
|
-
auth_header = request.META.get("HTTP_AUTHORIZATION")
|
|
25
|
-
if auth_header:
|
|
26
|
-
response["HTTP_AUTHORIZATION"] = auth_header
|
|
27
|
-
|
|
28
|
-
return response
|
|
File without changes
|
|
File without changes
|
|
File without changes
|