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
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
"""Wrap jobs in statuses."""
|
|
2
|
-
from itertools import chain
|
|
3
|
-
|
|
4
|
-
from codex.librarian.importer.aggregate_metadata import AggregateMetadataMixin
|
|
5
|
-
from codex.librarian.importer.deleted import DeletedMixin
|
|
6
|
-
from codex.librarian.importer.failed_imports import FailedImportsMixin
|
|
7
|
-
from codex.librarian.importer.moved import MovedMixin
|
|
8
|
-
from codex.librarian.importer.status import ImportStatusTypes
|
|
9
|
-
from codex.librarian.importer.update_comics import UpdateComicsMixin
|
|
10
|
-
from codex.models import Comic, Creator
|
|
11
|
-
from codex.status import Status
|
|
12
|
-
|
|
13
|
-
_CREATOR_FK_NAMES = ("role", "person")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ApplyDBOpsMixin(
|
|
17
|
-
AggregateMetadataMixin,
|
|
18
|
-
DeletedMixin,
|
|
19
|
-
UpdateComicsMixin,
|
|
20
|
-
FailedImportsMixin,
|
|
21
|
-
MovedMixin,
|
|
22
|
-
):
|
|
23
|
-
"""Apply db ops, wrapped in statuses."""
|
|
24
|
-
|
|
25
|
-
def read_metadata(self, library_path, all_paths, metadata):
|
|
26
|
-
"""Aggregate Metadata."""
|
|
27
|
-
return self.get_aggregate_metadata(all_paths, library_path, metadata)
|
|
28
|
-
|
|
29
|
-
def move_and_modify_dirs(self, library, task):
|
|
30
|
-
"""Move files and dirs and modify dirs."""
|
|
31
|
-
changed = 0
|
|
32
|
-
changed += self.bulk_folders_moved(task.dirs_moved, library)
|
|
33
|
-
task.dirs_moved = None
|
|
34
|
-
|
|
35
|
-
changed += self.bulk_comics_moved(task.files_moved, library)
|
|
36
|
-
task.files_moved = None
|
|
37
|
-
|
|
38
|
-
changed += self.bulk_folders_modified(task.dirs_modified, library)
|
|
39
|
-
task.dirs_modified = None
|
|
40
|
-
|
|
41
|
-
return changed
|
|
42
|
-
|
|
43
|
-
@staticmethod
|
|
44
|
-
def _get_query_fks_totals(fks):
|
|
45
|
-
"""Get the query foreign keys totals."""
|
|
46
|
-
fks_total = 0
|
|
47
|
-
for key, objs in fks.items():
|
|
48
|
-
if key == "group_trees":
|
|
49
|
-
for groups in objs.values():
|
|
50
|
-
fks_total += len(groups)
|
|
51
|
-
else:
|
|
52
|
-
fks_total += len(objs)
|
|
53
|
-
return fks_total
|
|
54
|
-
|
|
55
|
-
def _query_one_simple_model(self, fk_field, names, create_fks, status):
|
|
56
|
-
"""Batch query one simple model name."""
|
|
57
|
-
base_cls = Creator if fk_field in _CREATOR_FK_NAMES else Comic
|
|
58
|
-
fk_data = create_fks, base_cls, fk_field, "name"
|
|
59
|
-
status.complete += self.query_missing_simple_models(
|
|
60
|
-
names,
|
|
61
|
-
fk_data,
|
|
62
|
-
status=status,
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
def query_all_missing_fks(self, library_path, fks):
|
|
66
|
-
"""Get objects to create by querying existing objects for the proposed fks."""
|
|
67
|
-
create_creators = set()
|
|
68
|
-
create_groups = {}
|
|
69
|
-
update_groups = {}
|
|
70
|
-
create_folder_paths = set()
|
|
71
|
-
create_fks = {}
|
|
72
|
-
self.log.debug(f"Querying existing foreign keys for comics in {library_path}")
|
|
73
|
-
fks_total = self._get_query_fks_totals(fks)
|
|
74
|
-
status = Status(ImportStatusTypes.QUERY_MISSING_FKS, 0, fks_total)
|
|
75
|
-
try:
|
|
76
|
-
self.status_controller.start(status)
|
|
77
|
-
|
|
78
|
-
self.query_missing_creators( # type: ignore
|
|
79
|
-
fks.pop("creators", {}),
|
|
80
|
-
create_creators,
|
|
81
|
-
status=status,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
create_and_update_groups = {
|
|
85
|
-
"create_groups": create_groups,
|
|
86
|
-
"update_groups": update_groups,
|
|
87
|
-
}
|
|
88
|
-
for group_class, groups in fks.pop("group_trees", {}).items():
|
|
89
|
-
self.query_missing_group(
|
|
90
|
-
groups,
|
|
91
|
-
group_class,
|
|
92
|
-
create_and_update_groups,
|
|
93
|
-
status=status,
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
self.query_missing_folder_paths(
|
|
97
|
-
fks.pop("comic_paths", ()),
|
|
98
|
-
library_path,
|
|
99
|
-
create_folder_paths,
|
|
100
|
-
status=status,
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
for fk_field in sorted(fks.keys()):
|
|
104
|
-
self._query_one_simple_model(
|
|
105
|
-
fk_field, fks.pop(fk_field), create_fks, status
|
|
106
|
-
)
|
|
107
|
-
finally:
|
|
108
|
-
self.status_controller.finish(status)
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
create_groups,
|
|
112
|
-
update_groups,
|
|
113
|
-
create_folder_paths,
|
|
114
|
-
create_fks,
|
|
115
|
-
create_creators,
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
@staticmethod
|
|
119
|
-
def _get_create_fks_totals(create_data):
|
|
120
|
-
(
|
|
121
|
-
create_groups,
|
|
122
|
-
update_groups,
|
|
123
|
-
create_folder_paths,
|
|
124
|
-
create_fks,
|
|
125
|
-
create_creators,
|
|
126
|
-
) = create_data
|
|
127
|
-
total_fks = 0
|
|
128
|
-
for data_group in chain(
|
|
129
|
-
create_groups.values(), update_groups.values(), create_fks.values()
|
|
130
|
-
):
|
|
131
|
-
total_fks += len(data_group)
|
|
132
|
-
total_fks += len(create_folder_paths) + len(create_creators)
|
|
133
|
-
return total_fks
|
|
134
|
-
|
|
135
|
-
def create_all_fks(self, library, create_data):
|
|
136
|
-
"""Bulk create all foreign keys."""
|
|
137
|
-
total_fks = self._get_create_fks_totals(create_data)
|
|
138
|
-
status = Status(ImportStatusTypes.CREATE_FKS, 0, total_fks)
|
|
139
|
-
try:
|
|
140
|
-
self.status_controller.start(status)
|
|
141
|
-
(
|
|
142
|
-
create_groups,
|
|
143
|
-
update_groups,
|
|
144
|
-
create_folder_paths,
|
|
145
|
-
create_fks,
|
|
146
|
-
create_creators,
|
|
147
|
-
) = create_data
|
|
148
|
-
|
|
149
|
-
for group_class, group_tree_counts in create_groups.items():
|
|
150
|
-
status.complete += self.bulk_group_creator( # type: ignore
|
|
151
|
-
group_tree_counts,
|
|
152
|
-
group_class,
|
|
153
|
-
status=status,
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
for group_class, group_tree_counts in update_groups.items():
|
|
157
|
-
status.complete += self.bulk_group_updater( # type: ignore
|
|
158
|
-
group_tree_counts,
|
|
159
|
-
group_class,
|
|
160
|
-
status=status,
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
status.complete += self.bulk_folders_create( # type: ignore
|
|
164
|
-
sorted(create_folder_paths),
|
|
165
|
-
library,
|
|
166
|
-
status=status,
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
for named_class, names in create_fks.items():
|
|
170
|
-
status.complete += self.bulk_create_named_models(
|
|
171
|
-
names,
|
|
172
|
-
named_class,
|
|
173
|
-
status=status,
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
# This must happen after creator_fks created by create_named_models
|
|
177
|
-
status.complete += self.bulk_create_creators(
|
|
178
|
-
create_creators,
|
|
179
|
-
status=status,
|
|
180
|
-
)
|
|
181
|
-
finally:
|
|
182
|
-
self.status_controller.finish(status)
|
|
183
|
-
return status.complete
|
|
184
|
-
|
|
185
|
-
def create_comic_relations(self, library, fks):
|
|
186
|
-
"""Query all foreign keys to determine what needs creating, then create them."""
|
|
187
|
-
if not fks:
|
|
188
|
-
return 0
|
|
189
|
-
create_data = self.query_all_missing_fks(library.path, fks)
|
|
190
|
-
return self.create_all_fks(library, create_data)
|
|
191
|
-
|
|
192
|
-
def update_create_and_link_comics(
|
|
193
|
-
self, library, modified_paths, created_paths, metadata
|
|
194
|
-
):
|
|
195
|
-
"""Update, create and link comics."""
|
|
196
|
-
imported_count = self.bulk_update_comics(
|
|
197
|
-
modified_paths,
|
|
198
|
-
library,
|
|
199
|
-
created_paths,
|
|
200
|
-
metadata["mds"],
|
|
201
|
-
)
|
|
202
|
-
imported_count += self.bulk_create_comics(
|
|
203
|
-
created_paths, library, metadata["mds"]
|
|
204
|
-
)
|
|
205
|
-
self.bulk_query_and_link_comic_m2m_fields(metadata["m2m_mds"])
|
|
206
|
-
|
|
207
|
-
return imported_count
|
|
208
|
-
|
|
209
|
-
def fail_imports(self, library, failed_imports):
|
|
210
|
-
"""Handle failed imports."""
|
|
211
|
-
created_count = 0
|
|
212
|
-
try:
|
|
213
|
-
fis = {"update_fis": {}, "create_fis": {}, "delete_fi_paths": set()}
|
|
214
|
-
status = Status(ImportStatusTypes.FAILED_IMPORTS, 0, len(failed_imports))
|
|
215
|
-
status.total = self.query_failed_imports(
|
|
216
|
-
failed_imports,
|
|
217
|
-
library,
|
|
218
|
-
fis,
|
|
219
|
-
status=status,
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
self.bulk_update_failed_imports(
|
|
223
|
-
fis["update_fis"],
|
|
224
|
-
library,
|
|
225
|
-
status=status,
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
created_count = self.bulk_create_failed_imports(
|
|
229
|
-
fis["create_fis"],
|
|
230
|
-
library,
|
|
231
|
-
status=status,
|
|
232
|
-
)
|
|
233
|
-
|
|
234
|
-
self.bulk_cleanup_failed_imports(
|
|
235
|
-
fis["delete_fi_paths"], library, status=status
|
|
236
|
-
)
|
|
237
|
-
except Exception:
|
|
238
|
-
self.log.exception("Processing failed imports")
|
|
239
|
-
return bool(created_count)
|
|
240
|
-
|
|
241
|
-
def delete(self, library, task):
|
|
242
|
-
"""Delete files and folders."""
|
|
243
|
-
count = self.bulk_folders_deleted(task.dirs_deleted, library)
|
|
244
|
-
task.dirs_deleted = None
|
|
245
|
-
|
|
246
|
-
count += self.bulk_comics_deleted(task.files_deleted, library)
|
|
247
|
-
task.files_deleted = None
|
|
248
|
-
return count
|
codex/pdf.py
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"""Mimic comicbox.ComicArchive functions for PDFs."""
|
|
2
|
-
from io import BytesIO
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Union
|
|
5
|
-
|
|
6
|
-
import fitz
|
|
7
|
-
from comicbox.metadata.filename import FilenameMetadata
|
|
8
|
-
from filetype import guess
|
|
9
|
-
|
|
10
|
-
from codex.logger.logging import get_logger
|
|
11
|
-
|
|
12
|
-
LOG = get_logger(__name__)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class PDF:
|
|
16
|
-
"""PDF class."""
|
|
17
|
-
|
|
18
|
-
COVER_PAGE_INDEX = 1
|
|
19
|
-
_SUFFIX = ".pdf"
|
|
20
|
-
_FILE_TYPE = "PDF"
|
|
21
|
-
MIME_TYPE = "application/pdf"
|
|
22
|
-
_METADATA_KEY_MAP = {
|
|
23
|
-
"tags": "keywords",
|
|
24
|
-
"name": "title",
|
|
25
|
-
"": "subject",
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
@classmethod
|
|
29
|
-
def is_pdf(cls, path):
|
|
30
|
-
"""Is the path a pdf."""
|
|
31
|
-
if Path(path).suffix.lower() == cls._SUFFIX:
|
|
32
|
-
return True
|
|
33
|
-
kind = guess(path)
|
|
34
|
-
return kind and kind.mime == cls.MIME_TYPE
|
|
35
|
-
|
|
36
|
-
def __init__(self, path: Union[Path, str], **_kwargs):
|
|
37
|
-
"""Initialize."""
|
|
38
|
-
self._path: Path = Path(path)
|
|
39
|
-
self._doc = None
|
|
40
|
-
self._metadata: dict = {}
|
|
41
|
-
|
|
42
|
-
def __enter__(self):
|
|
43
|
-
"""Context enter."""
|
|
44
|
-
return self
|
|
45
|
-
|
|
46
|
-
def __exit__(self, *_args):
|
|
47
|
-
"""Context exit."""
|
|
48
|
-
self.close()
|
|
49
|
-
|
|
50
|
-
def _get_doc(self):
|
|
51
|
-
if not self._doc:
|
|
52
|
-
self._doc = fitz.Document(self._path)
|
|
53
|
-
return self._doc
|
|
54
|
-
|
|
55
|
-
def _set_metadata_key(self, comicbox_key, pdf_key):
|
|
56
|
-
metadata = self._get_doc().metadata
|
|
57
|
-
if not metadata:
|
|
58
|
-
return
|
|
59
|
-
value = metadata.get(pdf_key)
|
|
60
|
-
if value:
|
|
61
|
-
if comicbox_key == "writer":
|
|
62
|
-
if "creators" not in self._metadata:
|
|
63
|
-
self._metadata["creators"] = []
|
|
64
|
-
creator = {
|
|
65
|
-
"role": comicbox_key,
|
|
66
|
-
"person": value,
|
|
67
|
-
}
|
|
68
|
-
self._metadata["creators"].append(creator)
|
|
69
|
-
else:
|
|
70
|
-
self._metadata[comicbox_key] = value
|
|
71
|
-
|
|
72
|
-
def get_file_type(self):
|
|
73
|
-
"""Return the file type like comicbox."""
|
|
74
|
-
return self._FILE_TYPE
|
|
75
|
-
|
|
76
|
-
def get_metadata(self) -> dict:
|
|
77
|
-
"""Get metadata from pdf."""
|
|
78
|
-
if not self._metadata:
|
|
79
|
-
try:
|
|
80
|
-
self._metadata = FilenameMetadata(
|
|
81
|
-
path=self._path, string=self._path.name
|
|
82
|
-
).metadata
|
|
83
|
-
except Exception as exc:
|
|
84
|
-
LOG.warning(f"Error reading filename metadata for {self._path}: {exc}")
|
|
85
|
-
try:
|
|
86
|
-
doc = self._get_doc()
|
|
87
|
-
self._metadata["page_count"] = doc.page_count
|
|
88
|
-
for comicbox_key, pdf_key in self._METADATA_KEY_MAP.items():
|
|
89
|
-
self._set_metadata_key(comicbox_key, pdf_key)
|
|
90
|
-
except Exception as exc:
|
|
91
|
-
LOG.warning(f"Error reading metadata for {self._path}: {exc}")
|
|
92
|
-
self._metadata["page_count"] = 100
|
|
93
|
-
|
|
94
|
-
return self._metadata
|
|
95
|
-
|
|
96
|
-
def get_page_by_index(self, index) -> bytes:
|
|
97
|
-
"""Get the page bytestream by index."""
|
|
98
|
-
doc = self._get_doc()
|
|
99
|
-
doc.select((index,))
|
|
100
|
-
buffer = BytesIO()
|
|
101
|
-
doc.save(buffer)
|
|
102
|
-
self._doc = None
|
|
103
|
-
return buffer.getvalue()
|
|
104
|
-
|
|
105
|
-
def get_cover_image(self) -> bytes:
|
|
106
|
-
"""Get the first page as a image data."""
|
|
107
|
-
# image_data = b""
|
|
108
|
-
doc = self._get_doc()
|
|
109
|
-
page = doc.load_page(0)
|
|
110
|
-
pix = page.get_pixmap()
|
|
111
|
-
return pix.tobytes(output="ppm")
|
|
112
|
-
|
|
113
|
-
def close(self):
|
|
114
|
-
"""Get rid of the reader."""
|
|
115
|
-
self._reader = None
|