Add code for Sony collections

This commit is contained in:
Charles Haley 2010-09-01 16:44:12 +01:00
parent 11f7bd06a8
commit 16e5b2f3b0
4 changed files with 111 additions and 35 deletions

View File

@ -91,3 +91,33 @@ save_template_title_series_sorting = 'library_order'
# auto_connect_to_folder = 'C:\\Users\\someone\\Desktop\\testlib' # auto_connect_to_folder = 'C:\\Users\\someone\\Desktop\\testlib'
# auto_connect_to_folder = '/home/dropbox/My Dropbox/someone/library' # 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={}

View File

@ -9,9 +9,10 @@ import os, re, time, sys
from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.book.base import Metadata
from calibre.devices.mime import mime_type_ext from calibre.devices.mime import mime_type_ext
from calibre.devices.interface import BookList as _BookList 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 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): class Book(Metadata):
def __init__(self, prefix, lpath, size=None, other=None): def __init__(self, prefix, lpath, size=None, other=None):
@ -94,11 +95,38 @@ class CollectionsBookList(BookList):
def supports_collections(self): def supports_collections(self):
return True 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): def get_collections(self, collection_attributes):
from calibre.devices.usbms.driver import debug_print from calibre.devices.usbms.driver import debug_print
debug_print('Starting get_collections:', prefs['manage_device_metadata']) debug_print('Starting get_collections:', prefs['manage_device_metadata'])
debug_print('Renaming rules:', tweaks['sony_collection_renaming_rules'])
collections = {} collections = {}
series_categories = set([])
# This map of sets is used to avoid linear searches when testing for # This map of sets is used to avoid linear searches when testing for
# book equality # book equality
collections_lpaths = {} collections_lpaths = {}
@ -124,42 +152,55 @@ class CollectionsBookList(BookList):
# For existing books, modify the collections only if the user # For existing books, modify the collections only if the user
# specified 'on_connect' # specified 'on_connect'
attrs = collection_attributes 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: for attr in attrs:
attr = attr.strip() attr = attr.strip()
val = getattr(book, attr, None) val = meta_vals.get(attr, None)
if not val: continue if not val: continue
if isbytestring(val): if isbytestring(val):
val = val.decode(preferred_encoding, 'replace') val = val.decode(preferred_encoding, 'replace')
if isinstance(val, (list, tuple)): if isinstance(val, (list, tuple)):
val = list(val) val = list(val)
elif isinstance(val, unicode): else:
val = [val] val = [val]
for category in val: for category in val:
# TODO: NEWMETA: format the custom fields 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 \ if attr == 'tags' and len(category) > 1 and \
category[0] == '[' and category[-1] == ']': category[0] == '[' and category[-1] == ']':
continue 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 \ if attr == 'series' or \
('series' in collection_attributes and ('series' in collection_attributes and
getattr(book, 'series', None) == category): meta_vals.get('series', None) == category):
series_categories.add(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
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 # Sort collections
result = {}
for category, books in collections.items(): for category, books in collections.items():
def tgetter(x): books.sort(cmp=lambda x,y:cmp(x[1], y[1]))
return getattr(x, 'title_sort', 'zzzz') result[category] = [x[0] for x in books]
books.sort(cmp=lambda x,y:cmp(tgetter(x), tgetter(y))) return result
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
def rebuild_collections(self, booklist, oncard): def rebuild_collections(self, booklist, oncard):
''' '''

View File

@ -109,7 +109,7 @@ COPYABLE_METADATA_FIELDS = SOCIAL_METADATA_FIELDS.union(
CALIBRE_METADATA_FIELDS) - \ CALIBRE_METADATA_FIELDS) - \
frozenset(['title', 'title_sort', 'authors', frozenset(['title', 'title_sort', 'authors',
'author_sort', 'author_sort_map' 'comments', 'author_sort', 'author_sort_map' 'comments',
'cover_data', 'tags', 'language']) 'cover_data', 'tags', 'language', 'lpath'])
SERIALIZABLE_FIELDS = SOCIAL_METADATA_FIELDS.union( SERIALIZABLE_FIELDS = SOCIAL_METADATA_FIELDS.union(
USER_METADATA_FIELDS).union( USER_METADATA_FIELDS).union(

View File

@ -117,16 +117,18 @@ class Metadata(object):
res[k] = copy.deepcopy(user_metadata[k]) res[k] = copy.deepcopy(user_metadata[k])
return res 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 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 None. field is the key name, not the label. Return a copy if requested,
the user wants to change values in the dict (json does). just in case the user wants to change values in the dict.
''' '''
_data = object.__getattribute__(self, '_data') _data = object.__getattribute__(self, '_data')
_data = _data['user_metadata'] _data = _data['user_metadata']
if field in _data: if field in _data:
if make_copy:
return copy.deepcopy(_data[field]) return copy.deepcopy(_data[field])
return _data[field]
return None return None
@classmethod @classmethod
@ -189,7 +191,7 @@ class Metadata(object):
for x in STANDARD_METADATA_FIELDS: for x in STANDARD_METADATA_FIELDS:
prints('%s:'%x, getattr(self, x, 'None')) prints('%s:'%x, getattr(self, x, 'None'))
for x in self.user_metadata_keys: 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: if meta is not None:
prints(x, meta) prints(x, meta)
prints('--------------') prints('--------------')
@ -220,6 +222,9 @@ class Metadata(object):
self.set_all_user_metadata(other.get_all_user_metadata(make_copy=True)) self.set_all_user_metadata(other.get_all_user_metadata(make_copy=True))
self.comments = getattr(other, 'comments', '') self.comments = getattr(other, 'comments', '')
self.language = getattr(other, 'language', None) self.language = getattr(other, 'language', None)
lpath = getattr(other, 'lpath', None)
if lpath is not None:
self.lpath = lpath
else: else:
for attr in COPYABLE_METADATA_FIELDS: for attr in COPYABLE_METADATA_FIELDS:
if hasattr(other, attr): if hasattr(other, attr):
@ -240,7 +245,7 @@ class Metadata(object):
if getattr(other, 'user_metadata_keys', None): if getattr(other, 'user_metadata_keys', None):
for x in other.user_metadata_keys: 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: if meta is not None:
self.set_user_metadata(x, meta) # get... did the deepcopy self.set_user_metadata(x, meta) # get... did the deepcopy