View Single Post
Old 09-18-2016, 09:59 AM   #6
sup
Connoisseur
sup began at the beginning.
 
Posts: 95
Karma: 10
Join Date: Sep 2013
Device: Kindle Paperwhite (2012)
Quote:
Originally Posted by siebert View Post
Of course you can! You just need some monkey patch.

Running the following demo recipe with "ebook-convert authorpublisher.recipe authorpublisher.mobi" will create a dummy Mobipocket file which has the author and the publisher set to the values defined in the recipe:

Code:
'''
Demo recipe to demonstrate how to set the author and publisher in an ebook to values defined in the recipe.
'''

__license__   = 'GPL v3'
__copyright__ = '2014, Steffen Siebert <calibre at steffensiebert.de>'
__version__   = '1.0'

import os
import re
import string

import calibre.web.feeds.news

from calibre import fit_image
from calibre.constants import preferred_encoding
from calibre.ebooks.metadata.book.base import Metadata
from calibre.utils.magick import Image
from calibre.web.feeds import templates
from calibre.web.feeds.recipes import BasicNewsRecipe

from lxml.html.builder import HTML, HEAD, DIV, BODY, HR

author = "Recipe author"
publisher = "Recipe publisher"

''' 
Replaces calibre.web.feeds.news.MetaInformation via monkey patching.
Creates a LockedMetadata instead of a Metadata object and sets the author and publisher defined in this receipe. 
'''
def LockedMetaInformation(title, authors=(_('Unknown'),)):
    mi = None
    if hasattr(title, 'title') and hasattr(title, 'authors'):
        title.authors = (author, )
        mi = title
        title = mi.title
        authors = mi.authors
    authors = (author, )
    metadata = LockedMetadata(title, authors, other=mi)
    metadata.__setattr__("publisher", publisher)
    return metadata

'''
Extends calibre.ebooks.metadata.book.base.Metadata.
'''
class LockedMetadata(Metadata):
    '''
    Set fields author, authors and publishers only once.
    '''
    def __setattr__(self, field, val, extra=None):
        if field in ["author", "publisher"] and self.has_key(field):
            return
        elif field == "authors" and self.authors[0] != _('Unknown'):
            return
        Metadata.__setattr__(self, field, val, extra)

class AuthorPublisherRecipe(BasicNewsRecipe):
    title = u'Author Publisher Recipe'

    def __init__(self, options, log, progress_reporter):
        BasicNewsRecipe.__init__(self, options, log, progress_reporter)
        # Monkey patch calibre.web.feeds.news to use LockedMetaInformation.
        # This prevents calibre to set author and publisher to 'calibre'.
        calibre.web.feeds.news.MetaInformation = LockedMetaInformation

    def parse_index(self):
        feed = []
        articles = []
        articles.append({'title': 'demo'})
        feed.append(('feed', articles))
        return feed
        return [('demofeed', [{'title': 'demo'}, ])]
If you run the recipe from within the Calibre GUI, the author in the calibre database will still be set to 'calibre'.

A comment in the Calibre code responsible for this "feature" claims that this is needed for the auto delete process to identify the ebooks to delete, but this is a very lame excuse for messing with the author field instead of introducing a dedicated flag in the database.

Anyway, if you also want to see the actual author in the calibre database, you can install the following plugin by saving it as a file named "__init__.py" in an otherwise empty directory and run "calibre-customize -b ." in that directory:

Code:
import os

from calibre.customize import FileTypePlugin
from calibre.ebooks.metadata.meta import get_metadata, string_to_authors

class FixRecipeAuthorPlugin(FileTypePlugin):
    name                = 'Fix Recipe Author Plugin'
    description         = 'Replace "calibre" with the true authors in calibre database.'
    supported_platforms = ['windows', 'osx', 'linux']
    author              = 'Steffen Siebert <calibre at steffensiebert.de>'
    version             = (1, 0, 0)
    file_types          = set(['epub', 'mobi'])
    on_postimport       = True
    minimum_calibre_version = (0, 7, 53)

    def postimport(self, book_id, book_format, db):
        if db.field_for("authors", book_id) != ('calibre', ):
            # The author isn't set to 'calibre', nothing to do.
            return

        # Open file and extract metadata to get the real authors.
        path = db.format_abspath(book_id, book_format)
        file = open(path, 'r+b')
        mi = get_metadata(file, book_format)
        file.close()

        if not mi.authors:
            # No authors found in metadata, don't change anything.
            return

        # Extract authors from metadata and write them to database.
        authors = []
        for a in mi.authors:
            authors += string_to_authors(a)
        db.set_field("authors", {book_id: authors})
Have fun hacking Calibre!
Thanks, it works as intended. I just wish I could do this with two lines in a recipe instead of twenty:-). (unless that already happened)
sup is offline   Reply With Quote