mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add code for Sony collections
This commit is contained in:
parent
11f7bd06a8
commit
16e5b2f3b0
@ -90,4 +90,34 @@ save_template_title_series_sorting = 'library_order'
|
||||
# Examples:
|
||||
# auto_connect_to_folder = 'C:\\Users\\someone\\Desktop\\testlib'
|
||||
# auto_connect_to_folder = '/home/dropbox/My Dropbox/someone/library'
|
||||
auto_connect_to_folder = ''
|
||||
auto_connect_to_folder = ''
|
||||
|
||||
# Specify renaming rules for sony collections. Collections on Sonys are named
|
||||
# depending upon whether the field is standard or custom. A collection derived
|
||||
# from a standard field is named for the value in that field. For example, if
|
||||
# the standard 'series' column contains the name 'Darkover', then the series
|
||||
# will be named 'Darkover'. A collection derived from a custom field will have
|
||||
# the name of the field added to the value. For example, if a custom series
|
||||
# column named 'My Series' contains the name 'Darkover', then the collection
|
||||
# will be named 'Darkover (My Series)'. If two books have fields that generate
|
||||
# the same collection name, then both books will be in that collection. This
|
||||
# tweak lets you specify for a standard or custom field the value to be put
|
||||
# inside the parentheses. You can use it to add a parenthetical description to a
|
||||
# standard field, for example 'Foo (Tag)' instead of the 'Foo'. You can also use
|
||||
# it to force multiple fields to end up in the same collection. For example, you
|
||||
# could force the values in 'series', '#my_series_1', and '#my_series_2' to
|
||||
# appear in collections named 'some_value (Series)', thereby merging all of the
|
||||
# fields into one set of collections. The syntax of this tweak is
|
||||
# {'field_lookup_name':'name_to_use', 'lookup_name':'name', ...}
|
||||
# Example 1: I want three series columns to be merged into one set of
|
||||
# collections. If the column lookup names are 'series', '#series_1' and
|
||||
# '#series_2', and if I want nothing in the parenthesis, then the value to use
|
||||
# in the tweak value would be:
|
||||
# sony_collection_renaming_rules={'series':'', '#series_1':'', '#series_2':''}
|
||||
# Example 2: I want the word '(Series)' to appear on collections made from
|
||||
# series, and the word '(Tag)' to appear on collections made from tags. Use:
|
||||
# sony_collection_renaming_rules={'series':'Series', 'tags':'Tag'}
|
||||
# Example 3: I want 'series' and '#myseries' to be merged, and for the
|
||||
# collection name to have '(Series)' appended. The renaming rule is:
|
||||
# sony_collection_renaming_rules={'series':'Series', '#myseries':'Series'}
|
||||
sony_collection_renaming_rules={}
|
@ -9,9 +9,10 @@ import os, re, time, sys
|
||||
from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.devices.mime import mime_type_ext
|
||||
from calibre.devices.interface import BookList as _BookList
|
||||
from calibre.constants import filesystem_encoding, preferred_encoding
|
||||
from calibre.constants import preferred_encoding
|
||||
from calibre import isbytestring
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.utils.config import prefs, tweaks
|
||||
from calibre.utils.date import format_date
|
||||
|
||||
class Book(Metadata):
|
||||
def __init__(self, prefix, lpath, size=None, other=None):
|
||||
@ -94,11 +95,38 @@ class CollectionsBookList(BookList):
|
||||
def supports_collections(self):
|
||||
return True
|
||||
|
||||
def compute_category_name(self, attr, category, cust_field_meta):
|
||||
renames = tweaks['sony_collection_renaming_rules']
|
||||
attr_name = renames.get(attr, None)
|
||||
if attr_name is None:
|
||||
if attr in cust_field_meta:
|
||||
attr_name = '(%s)'%cust_field_meta[attr]['name']
|
||||
else:
|
||||
attr_name = ''
|
||||
elif attr_name != '':
|
||||
attr_name = '(%s)'%attr_name
|
||||
|
||||
if attr not in cust_field_meta:
|
||||
cat_name = '%s %s'%(category, attr_name)
|
||||
else:
|
||||
fm = cust_field_meta[attr]
|
||||
if fm['datatype'] == 'bool':
|
||||
if category:
|
||||
cat_name = '%s %s'%(_('Yes'), attr_name)
|
||||
else:
|
||||
cat_name = '%s %s'%(_('No'), attr_name)
|
||||
elif fm['datatype'] == 'datetime':
|
||||
cat_name = '%s %s'%(format_date(category,
|
||||
fm['display'].get('date_format','dd MMM yyyy')), attr_name)
|
||||
else:
|
||||
cat_name = '%s %s'%(category, attr_name)
|
||||
return cat_name.strip()
|
||||
|
||||
def get_collections(self, collection_attributes):
|
||||
from calibre.devices.usbms.driver import debug_print
|
||||
debug_print('Starting get_collections:', prefs['manage_device_metadata'])
|
||||
debug_print('Renaming rules:', tweaks['sony_collection_renaming_rules'])
|
||||
collections = {}
|
||||
series_categories = set([])
|
||||
# This map of sets is used to avoid linear searches when testing for
|
||||
# book equality
|
||||
collections_lpaths = {}
|
||||
@ -124,42 +152,55 @@ class CollectionsBookList(BookList):
|
||||
# For existing books, modify the collections only if the user
|
||||
# specified 'on_connect'
|
||||
attrs = collection_attributes
|
||||
meta_vals = book.get_all_non_none_attributes()
|
||||
cust_field_meta = book.get_all_user_metadata(make_copy=False)
|
||||
for attr in attrs:
|
||||
attr = attr.strip()
|
||||
val = getattr(book, attr, None)
|
||||
val = meta_vals.get(attr, None)
|
||||
if not val: continue
|
||||
if isbytestring(val):
|
||||
val = val.decode(preferred_encoding, 'replace')
|
||||
if isinstance(val, (list, tuple)):
|
||||
val = list(val)
|
||||
elif isinstance(val, unicode):
|
||||
else:
|
||||
val = [val]
|
||||
for category in val:
|
||||
# TODO: NEWMETA: format the custom fields
|
||||
if attr == 'tags' and len(category) > 1 and \
|
||||
category[0] == '[' and category[-1] == ']':
|
||||
is_series = False
|
||||
if attr in cust_field_meta: # is a custom field
|
||||
fm = cust_field_meta[attr]
|
||||
if fm['datatype'] == 'text' and len(category) > 1 and \
|
||||
category[0] == '[' and category[-1] == ']':
|
||||
continue
|
||||
if fm['datatype'] == 'series':
|
||||
is_series = True
|
||||
else: # is a standard field
|
||||
if attr == 'tags' and len(category) > 1 and \
|
||||
category[0] == '[' and category[-1] == ']':
|
||||
continue
|
||||
if attr == 'series' or \
|
||||
('series' in collection_attributes and
|
||||
meta_vals.get('series', None) == category):
|
||||
is_series = True
|
||||
cat_name = self.compute_category_name(attr, category,
|
||||
cust_field_meta)
|
||||
if cat_name not in collections:
|
||||
collections[cat_name] = []
|
||||
collections_lpaths[cat_name] = set()
|
||||
if lpath in collections_lpaths[cat_name]:
|
||||
continue
|
||||
if category not in collections:
|
||||
collections[category] = []
|
||||
collections_lpaths[category] = set()
|
||||
if lpath not in collections_lpaths[category]:
|
||||
collections_lpaths[category].add(lpath)
|
||||
collections[category].append(book)
|
||||
if attr == 'series' or \
|
||||
('series' in collection_attributes and
|
||||
getattr(book, 'series', None) == category):
|
||||
series_categories.add(category)
|
||||
collections_lpaths[cat_name].add(lpath)
|
||||
if is_series:
|
||||
collections[cat_name].append(
|
||||
(book, meta_vals.get(attr+'_index', sys.maxint)))
|
||||
else:
|
||||
collections[cat_name].append(
|
||||
(book, meta_vals.get('title_sort', 'zzzz')))
|
||||
# Sort collections
|
||||
result = {}
|
||||
for category, books in collections.items():
|
||||
def tgetter(x):
|
||||
return getattr(x, 'title_sort', 'zzzz')
|
||||
books.sort(cmp=lambda x,y:cmp(tgetter(x), tgetter(y)))
|
||||
if category in series_categories:
|
||||
# Ensures books are sub sorted by title
|
||||
def getter(x):
|
||||
return getattr(x, 'series_index', sys.maxint)
|
||||
books.sort(cmp=lambda x,y:cmp(getter(x), getter(y)))
|
||||
return collections
|
||||
books.sort(cmp=lambda x,y:cmp(x[1], y[1]))
|
||||
result[category] = [x[0] for x in books]
|
||||
return result
|
||||
|
||||
def rebuild_collections(self, booklist, oncard):
|
||||
'''
|
||||
|
@ -109,7 +109,7 @@ COPYABLE_METADATA_FIELDS = SOCIAL_METADATA_FIELDS.union(
|
||||
CALIBRE_METADATA_FIELDS) - \
|
||||
frozenset(['title', 'title_sort', 'authors',
|
||||
'author_sort', 'author_sort_map' 'comments',
|
||||
'cover_data', 'tags', 'language'])
|
||||
'cover_data', 'tags', 'language', 'lpath'])
|
||||
|
||||
SERIALIZABLE_FIELDS = SOCIAL_METADATA_FIELDS.union(
|
||||
USER_METADATA_FIELDS).union(
|
||||
|
@ -117,16 +117,18 @@ class Metadata(object):
|
||||
res[k] = copy.deepcopy(user_metadata[k])
|
||||
return res
|
||||
|
||||
def get_user_metadata(self, field):
|
||||
def get_user_metadata(self, field, make_copy):
|
||||
'''
|
||||
return field metadata from the object if it is there. Otherwise return
|
||||
None. field is the key name, not the label. Return a copy, just in case
|
||||
the user wants to change values in the dict (json does).
|
||||
None. field is the key name, not the label. Return a copy if requested,
|
||||
just in case the user wants to change values in the dict.
|
||||
'''
|
||||
_data = object.__getattribute__(self, '_data')
|
||||
_data = _data['user_metadata']
|
||||
if field in _data:
|
||||
return copy.deepcopy(_data[field])
|
||||
if make_copy:
|
||||
return copy.deepcopy(_data[field])
|
||||
return _data[field]
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
@ -189,7 +191,7 @@ class Metadata(object):
|
||||
for x in STANDARD_METADATA_FIELDS:
|
||||
prints('%s:'%x, getattr(self, x, 'None'))
|
||||
for x in self.user_metadata_keys:
|
||||
meta = self.get_user_metadata(x)
|
||||
meta = self.get_user_metadata(x, make_copy=False)
|
||||
if meta is not None:
|
||||
prints(x, meta)
|
||||
prints('--------------')
|
||||
@ -220,6 +222,9 @@ class Metadata(object):
|
||||
self.set_all_user_metadata(other.get_all_user_metadata(make_copy=True))
|
||||
self.comments = getattr(other, 'comments', '')
|
||||
self.language = getattr(other, 'language', None)
|
||||
lpath = getattr(other, 'lpath', None)
|
||||
if lpath is not None:
|
||||
self.lpath = lpath
|
||||
else:
|
||||
for attr in COPYABLE_METADATA_FIELDS:
|
||||
if hasattr(other, attr):
|
||||
@ -240,7 +245,7 @@ class Metadata(object):
|
||||
|
||||
if getattr(other, 'user_metadata_keys', None):
|
||||
for x in other.user_metadata_keys:
|
||||
meta = other.get_user_metadata(x)
|
||||
meta = other.get_user_metadata(x, make_copy=True)
|
||||
if meta is not None:
|
||||
self.set_user_metadata(x, meta) # get... did the deepcopy
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user