Upload files to "/"

This commit is contained in:
2025-12-06 03:36:49 +00:00
parent 0ab8c60973
commit 1c668db7f1
4 changed files with 409 additions and 2 deletions

101
README.md
View File

@@ -1,3 +1,100 @@
# EmbedMangaMetadata
# Embed Comic Metadata
A Calibre Plugin to embed calibres metadata into cbz comic archieves
A Calibre Plugin to manage comic metadata in calibre
## Special Notes
Requires calibre version 1.0.0 or later.
## Main Features
- Can embed the metadata to the zip comment or a ComicInfo.xml file inside the archives
- Can read the above metadata formats and import them into calibre
- Can write many additional metadata into custom columns Can automatically convert cbr/zip/rar files to cbz
- Can embed the calibre cover into cbz comics (experimental)
- Can count the number of pages (image files) in a comic
- Can get the size of the images in the comic file
## Usage
### To embed calibres metadata into the comic archive:
- Select the comics that should be updated in the library.
- Click the addon EmbedComicMetadata icon in your toolbar
- (You can select a specific action or open the configuration bei clicking on the small arrow on the icon and selecting the desired option)
### To import the comic archive metadata into calibre:
- Select the comics that should be updated in the library.
- Click the small arrow on the addon EmbedComicMetadata icon in your toolbar
- Click on "Import Metadata from the comic archive into calibre"
### Custom Columns:
You can make custom columns in calibre and populate them with metadata imported with the plugin. In the configuration use the dropdown menu for the columns to select what metadata should be written to what custom column.
The custom columns you make in calibre should be of the following type, depending on the metadata stored in them.
#### Comma seperated text, like tags, shown in the tag browser with "Contains names" checked:
Penciller, Inker, Colorist, Letterer, Cover Artist, Editor
#### Comma seperated text, like tags, shown in the tag browser:
Characters, Teams, Locations, Genre
#### Text, column shown in the tag browser:
Story Arc, Volume, Number of Issues
#### Text, but with a fixed set of permitted values:
* Manga
* Values: No,Yes,YesAndRightToLeft
* Default Value: No
#### Integer:
Number of Issues, Volume
#### Float:
Image Size
#### Series like:
Story Arc
#### Comment:
Comic Vine Link
### Customizing the main menu:
The menu in the toolbar can be custimized to your liking through the options in the configuration.
### Embed Cover:
Use with care, just inserts the calibre cover as "00000000_cover" into the comic archive (previously inserted calibre covers are overwritten).
### Get image size
Tries to get the size of the first non cover and non landscape (in case there are double pages, after 9 pages, take that value) image in the comic file in megapixels.
## Configuration
- **Write metadata in zip comment**: This format is used by calibre, if you import comic files and by ComicbookLovers (default: on)
- **Write metadata in ComicInfo.xml**: This format is used by ComicRack and some other comic readers (default: on)
- **Auto convert cbr to cbz**: If a comic has only the cbr format, convert it to store the metadata (default: on)
- **Also convert rar and zip to cbz**: Expand the behaviour for cbr to rars and zips (default: off)
- **Auto convert while importing to calibre**: As above, but even when importing metadata into calibre (default: off)
- **Delete cbr after conversion**: Deletes the cbr format after the conversion (default: off)
- **Swap names to "LN, FN" when importing metadata**: Does just what it says (default: off)
- **Auto count pages if importing**: Count pages automatically if importing metadata into calibre (default: off)
- **Get the image size if importing**: Get the image size automatically if importing metadata into calibre (default: off)
- **Main Button Action**: You can set, what action should be performed if the big toolbar button is pressed. Needs a calibre restart (default: Embed metadata)
- **Menu Buttons**: The dropdown menu on the icon in the toolbar can be custimized to your liking through these options
## Acknowledgement
The handling of the comic metadata is done by using code from [ComicTagger](https://code.google.com/p/comictagger/)

185
main.py Normal file
View File

@@ -0,0 +1,185 @@
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2015, dloraine'
__docformat__ = 'restructuredtext en'
from functools import partial
from calibre.gui2 import error_dialog, info_dialog
from calibre_plugins.EmbedComicMetadata.config import prefs
from calibre_plugins.EmbedComicMetadata.languages.lang import _L
from calibre_plugins.EmbedComicMetadata.comicmetadata import ComicMetadata
import sys
python3 = sys.version_info[0] > 2
def import_to_calibre(ia, action):
def _import_to_calibre(metadata):
metadata.get_comic_metadata_from_file()
if action == "both" and metadata.comic_metadata:
metadata.import_comic_metadata_to_calibre(metadata.comic_metadata)
elif action == "cix" and metadata.cix_metadata:
metadata.import_comic_metadata_to_calibre(metadata.cix_metadata)
elif action == "cbi" and metadata.cbi_metadata:
metadata.import_comic_metadata_to_calibre(metadata.cbi_metadata)
else:
return False
return True
iterate_over_books(ia, _import_to_calibre,
_L["Updated Calibre Metadata"],
_L['Updated calibre metadata for {} book(s)'],
_L['The following books had no metadata: {}'],
prefs['convert_reading'])
def embed_into_comic(ia, action):
def _embed_into_comic(metadata):
if metadata.format != "cbz":
return False
metadata.overlay_metadata()
if action == "both" or action == "cix":
metadata.embed_cix_metadata()
if action == "both" or action == "cbi":
metadata.embed_cbi_metadata()
metadata.add_updated_comic_to_calibre()
return True
iterate_over_books(ia, _embed_into_comic,
_L["Updated comics"],
_L['Updated the metadata in the files of {} comics'],
_L['The following books were not updated: {}'])
def convert(ia):
iterate_over_books(ia, partial(convert_to_cbz, ia),
_L["Converted files"],
_L['Converted {} book(s) to cbz'],
_L['The following books were not converted: {}'],
False)
def embed_cover(ia):
def _embed_cover(metadata):
if metadata.format != "cbz":
return False
metadata.update_cover()
metadata.add_updated_comic_to_calibre()
return True
iterate_over_books(ia, _embed_cover,
_L["Updated Covers"],
_L['Embeded {} covers'],
_L['The following covers were not embeded: {}'])
def count_pages(ia):
def _count_pages(metadata):
if metadata.format != "cbz":
return False
return metadata.action_count_pages()
iterate_over_books(ia, _count_pages,
_L["Counted pages"],
_L['Counted pages in {} comics'],
_L['The following comics were not counted: {}'])
def remove_metadata(ia):
def _remove_metadata(metadata):
if metadata.format != "cbz":
return False
metadata.remove_embedded_metadata()
metadata.add_updated_comic_to_calibre()
return True
iterate_over_books(ia, _remove_metadata,
_L["Removed metadata"],
_L['Removed metadata in {} comics'],
_L['The following comics did not have metadata removed: {}'])
def get_image_size(ia):
def _get_image_size(metadata):
if metadata.format != "cbz":
return False
return metadata.action_picture_size()
iterate_over_books(ia, _get_image_size,
_L["Updated Calibre Metadata"],
_L['Updated calibre metadata for {} book(s)'],
_L['The following books were not updated: {}'])
def iterate_over_books(ia, func, title, ptext, notptext,
should_convert=None,
convtext=_L["The following comics were converted to cbz: {}"]):
'''
Iterates over all selected books. For each book, it checks if it should be
converted to cbz and then applies func to the book.
After all books are processed, gives a completion message.
'''
processed = []
not_processed = []
converted = []
if should_convert is None:
should_convert = prefs["convert_cbr"]
# iterate through the books
for book_id in get_selected_books(ia):
metadata = ComicMetadata(book_id, ia)
# sanity check
if metadata.format is None:
not_processed.append(metadata.info)
continue
if should_convert and convert_to_cbz(ia, metadata):
converted.append(metadata.info)
if func(metadata):
processed.append(metadata.info)
else:
not_processed.append(metadata.info)
# show a completion message
msg = ptext.format(len(processed))
if should_convert and len(converted) > 0:
msg += '\n' + convtext.format(lst2string(converted))
if len(not_processed) > 0:
msg += '\n' + notptext.format(lst2string(not_processed))
info_dialog(ia.gui, title, msg, show=True)
def get_selected_books(ia):
# Get currently selected books
rows = ia.gui.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0:
return error_dialog(ia.gui, _L['Cannot update metadata'],
_L['No books selected'], show=True)
# Map the rows to book ids
return map(ia.gui.library_view.model().id, rows)
def lst2string(lst):
if python3:
return "\n " + "\n ".join(lst)
return "\n " + "\n ".join(item.encode('utf-8') for item in lst)
def convert_to_cbz(ia, metadata):
if metadata.format == "cbr" or (metadata.format == "rar" and prefs['convert_archives']):
metadata.convert_cbr_to_cbz()
if prefs['delete_cbr']:
ia.gui.current_db.new_api.remove_formats({metadata.book_id: {"cbr", "rar"}})
return True
elif metadata.format == "zip" and prefs['convert_archives']:
metadata.convert_zip_to_cbz()
if prefs['delete_cbr']:
ia.gui.current_db.new_api.remove_formats({metadata.book_id: {"zip"}})
return True
return False

Binary file not shown.

125
ui.py Normal file
View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python2
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2015, dloraine'
__docformat__ = 'restructuredtext en'
try:
from PyQt5.Qt import QMenu, QIcon, QPixmap
except ImportError:
from PyQt4.Qt import QMenu, QIcon, QPixmap
from functools import partial
from calibre.gui2.actions import InterfaceAction
from calibre.gui2 import error_dialog
from calibre_plugins.EmbedComicMetadata.config import prefs
from calibre_plugins.EmbedComicMetadata.languages.lang import _L
from calibre_plugins.EmbedComicMetadata.ini import (
get_configuration, CONFIG_NAME, CONFIG_DESCRIPTION, CONFIG_TRIGGER_FUNC,
CONFIG_TRIGGER_ARG, CONFIG_MENU)
config = get_configuration()
class EmbedComicMetadata(InterfaceAction):
name = 'Embed Comic Metadata'
# Declare the main action associated with this plugin
if prefs["main_import"]:
action_spec = (_L['Import Comic Metadata'], None,
_L['Imports the metadata from the comic to calibre'], None)
else:
action_spec = (_L['Embed Comic Metadata'], None,
_L['Embeds calibres metadata into the comic'], None)
def genesis(self):
# menu
self.menu = QMenu(self.gui)
# Get the icon for this interface action
icon = self.get_icon('images/embed_comic_metadata.png')
# The qaction is automatically created from the action_spec defined
# above
self.qaction.setMenu(self.menu)
self.qaction.setIcon(icon)
self.qaction.triggered.connect(self.main_menu_triggered)
# build menu
self.menu.clear()
self.build_menu()
self.toggle_menu_items()
def build_menu(self):
for item in config[CONFIG_MENU]["UI_Action_Items"]:
if item[CONFIG_NAME] == "seperator":
self.menu.addSeparator()
continue
elif item[CONFIG_TRIGGER_ARG]:
triggerfunc = partial(item[CONFIG_TRIGGER_FUNC], self, item[CONFIG_TRIGGER_ARG])
else:
triggerfunc = partial(item[CONFIG_TRIGGER_FUNC], self)
self.menu_action(item[CONFIG_NAME], item[CONFIG_DESCRIPTION], triggerfunc)
# add configuration entry
self.menu_action("configure", _L["Configure"],
partial(self.interface_action_base_plugin.do_user_config, (self.gui)))
def toggle_menu_items(self):
for item in config[CONFIG_MENU]["Items"]:
action = getattr(self, item[CONFIG_NAME])
action.setVisible(prefs[item[CONFIG_NAME]])
def main_menu_triggered(self):
from calibre_plugins.EmbedComicMetadata.main import embed_into_comic, import_to_calibre
i = prefs["main_import"]
# Check the preferences for what should be done
if (i and prefs['read_cbi'] and prefs['read_cix']) or (
not i and prefs['cbi_embed'] and prefs['cix_embed']):
action = "both"
elif (i and prefs['read_cbi']) or (not i and prefs['cbi_embed']):
action = "cbi"
elif (i and prefs['read_cix']) or (not i and prefs['cix_embed']):
action = "cix"
else:
return error_dialog(self.gui, _L['Cannot update metadata'],
_L['No embed format selected'], show=True)
if i:
import_to_calibre(self, action)
else:
embed_into_comic(self, action)
def apply_settings(self):
# In an actual non trivial plugin, you would probably need to
# do something based on the settings in prefs
prefs
def menu_action(self, name, title, triggerfunc):
action = self.create_menu_action(self.menu, name, title, icon=None,
shortcut=None, description=None,
triggered=triggerfunc, shortcut_name=None)
setattr(self, name, action)
def get_icon(self, icon_name):
import os
from calibre.utils.config import config_dir
# Check to see whether the icon exists as a Calibre resource
# This will enable skinning if the user stores icons within a folder like:
# ...\AppData\Roaming\calibre\resources\images\Plugin Name\
icon_path = os.path.join(config_dir, 'resources', 'images', self.name,
icon_name.replace('images/', ''))
if os.path.exists(icon_path):
pixmap = QPixmap()
pixmap.load(icon_path)
return QIcon(pixmap)
# As we did not find an icon elsewhere, look within our zip resources
return get_icons(icon_name)