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

@ -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={}

View File

@ -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):
'''

View File

@ -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(

View File

@ -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