mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Working JSON metadata along side Sony metadata
This commit is contained in:
parent
cd6c46dba5
commit
d1c040d546
@ -387,6 +387,9 @@ class BookList(list):
|
|||||||
__getslice__ = None
|
__getslice__ = None
|
||||||
__setslice__ = None
|
__setslice__ = None
|
||||||
|
|
||||||
|
def __init__(self, oncard, prefix):
|
||||||
|
pass
|
||||||
|
|
||||||
def supports_tags(self):
|
def supports_tags(self):
|
||||||
''' Return True if the the device supports tags (collections) for this book list. '''
|
''' Return True if the the device supports tags (collections) for this book list. '''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@ -399,3 +402,17 @@ class BookList(list):
|
|||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def add_book(self, book, collections=None):
|
||||||
|
'''
|
||||||
|
Add the book to the booklist. Intent is to maintain any device-internal
|
||||||
|
metadata
|
||||||
|
'''
|
||||||
|
if book not in self:
|
||||||
|
self.append(book)
|
||||||
|
|
||||||
|
def remove_book(self, book):
|
||||||
|
'''
|
||||||
|
Remove a book from the booklist. Correct any device metadata at the
|
||||||
|
same time
|
||||||
|
'''
|
||||||
|
self.remove(book)
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
MEDIA_XML = 'database/cache/media.xml'
|
||||||
|
|
||||||
|
CACHE_XML = 'Sony Reader/database/cache.xml'
|
||||||
|
@ -5,13 +5,14 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
import re, time, functools
|
import re, time, functools
|
||||||
from uuid import uuid4 as _uuid
|
from uuid import uuid4 as _uuid
|
||||||
import xml.dom.minidom as dom
|
import xml.dom.minidom as dom
|
||||||
from base64 import b64decode as decode
|
|
||||||
from base64 import b64encode as encode
|
from base64 import b64encode as encode
|
||||||
|
|
||||||
|
|
||||||
from calibre.devices.interface import BookList as _BookList
|
from calibre.devices.interface import BookList as _BookList
|
||||||
from calibre.devices import strftime as _strftime
|
from calibre.devices import strftime as _strftime
|
||||||
from calibre.devices import strptime
|
from calibre.devices.usbms.books import Book as _Book
|
||||||
|
from calibre.devices.prs505 import MEDIA_XML
|
||||||
|
from calibre.devices.prs505 import CACHE_XML
|
||||||
|
|
||||||
strftime = functools.partial(_strftime, zone=time.gmtime)
|
strftime = functools.partial(_strftime, zone=time.gmtime)
|
||||||
|
|
||||||
@ -50,62 +51,7 @@ class book_metadata_field(object):
|
|||||||
obj.elem.setAttribute(self.attr, val)
|
obj.elem.setAttribute(self.attr, val)
|
||||||
|
|
||||||
|
|
||||||
class Book(object):
|
class Book(_Book):
|
||||||
""" Provides a view onto the XML element that represents a book """
|
|
||||||
|
|
||||||
title = book_metadata_field("title")
|
|
||||||
authors = book_metadata_field("author", \
|
|
||||||
formatter=lambda x: [x if x and x.strip() else _('Unknown')])
|
|
||||||
mime = book_metadata_field("mime")
|
|
||||||
rpath = book_metadata_field("path")
|
|
||||||
id = book_metadata_field("id", formatter=int)
|
|
||||||
sourceid = book_metadata_field("sourceid", formatter=int)
|
|
||||||
size = book_metadata_field("size", formatter=lambda x : int(float(x)))
|
|
||||||
# When setting this attribute you must use an epoch
|
|
||||||
datetime = book_metadata_field("date", formatter=strptime, setter=strftime)
|
|
||||||
|
|
||||||
@dynamic_property
|
|
||||||
def title_sorter(self):
|
|
||||||
doc = '''String to sort the title. If absent, title is returned'''
|
|
||||||
def fget(self):
|
|
||||||
src = self.elem.getAttribute('titleSorter').strip()
|
|
||||||
if not src:
|
|
||||||
src = self.title
|
|
||||||
return src
|
|
||||||
def fset(self, val):
|
|
||||||
self.elem.setAttribute('titleSorter', sortable_title(unicode(val)))
|
|
||||||
return property(doc=doc, fget=fget, fset=fset)
|
|
||||||
|
|
||||||
@dynamic_property
|
|
||||||
def thumbnail(self):
|
|
||||||
doc = \
|
|
||||||
"""
|
|
||||||
The thumbnail. Should be a height 68 image.
|
|
||||||
Setting is not supported.
|
|
||||||
"""
|
|
||||||
def fget(self):
|
|
||||||
th = self.elem.getElementsByTagName(self.prefix + "thumbnail")
|
|
||||||
if not len(th):
|
|
||||||
th = self.elem.getElementsByTagName("cache:thumbnail")
|
|
||||||
if len(th):
|
|
||||||
for n in th[0].childNodes:
|
|
||||||
if n.nodeType == n.ELEMENT_NODE:
|
|
||||||
th = n
|
|
||||||
break
|
|
||||||
rc = ""
|
|
||||||
for node in th.childNodes:
|
|
||||||
if node.nodeType == node.TEXT_NODE:
|
|
||||||
rc += node.data
|
|
||||||
return decode(rc)
|
|
||||||
return property(fget=fget, doc=doc)
|
|
||||||
|
|
||||||
@dynamic_property
|
|
||||||
def path(self):
|
|
||||||
doc = """ Absolute path to book on device. Setting not supported. """
|
|
||||||
def fget(self):
|
|
||||||
return self.mountpath + self.rpath
|
|
||||||
return property(fget=fget, doc=doc)
|
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def db_id(self):
|
def db_id(self):
|
||||||
doc = '''The database id in the application database that this file corresponds to'''
|
doc = '''The database id in the application database that this file corresponds to'''
|
||||||
@ -115,42 +61,26 @@ class Book(object):
|
|||||||
return int(match.group(1))
|
return int(match.group(1))
|
||||||
return property(fget=fget, doc=doc)
|
return property(fget=fget, doc=doc)
|
||||||
|
|
||||||
def __init__(self, node, mountpath, tags, prefix=""):
|
|
||||||
self.elem = node
|
|
||||||
self.prefix = prefix
|
|
||||||
self.tags = tags
|
|
||||||
self.mountpath = mountpath
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
""" Return a utf-8 encoded string with title author and path information """
|
|
||||||
return self.title.encode('utf-8') + " by " + \
|
|
||||||
self.authors.encode('utf-8') + " at " + self.path.encode('utf-8')
|
|
||||||
|
|
||||||
|
|
||||||
class BookList(_BookList):
|
class BookList(_BookList):
|
||||||
|
|
||||||
def __init__(self, xml_file, mountpath, report_progress=None):
|
def __init__(self, oncard, prefix):
|
||||||
_BookList.__init__(self)
|
_BookList.__init__(self, oncard, prefix)
|
||||||
|
if prefix is None:
|
||||||
|
return
|
||||||
|
db = CACHE_XML if oncard else MEDIA_XML
|
||||||
|
xml_file = open(prefix + db, 'rb')
|
||||||
xml_file.seek(0)
|
xml_file.seek(0)
|
||||||
self.document = dom.parse(xml_file)
|
self.document = dom.parse(xml_file)
|
||||||
self.root_element = self.document.documentElement
|
self.root_element = self.document.documentElement
|
||||||
self.mountpath = mountpath
|
self.mountpath = prefix
|
||||||
records = self.root_element.getElementsByTagName('records')
|
records = self.root_element.getElementsByTagName('records')
|
||||||
self.tag_order = {}
|
|
||||||
|
|
||||||
if records:
|
if records:
|
||||||
self.prefix = 'xs1:'
|
self.prefix = 'xs1:'
|
||||||
self.root_element = records[0]
|
self.root_element = records[0]
|
||||||
else:
|
else:
|
||||||
self.prefix = ''
|
self.prefix = ''
|
||||||
|
self.tag_order = {}
|
||||||
nodes = self.root_element.childNodes
|
|
||||||
for i, book in enumerate(nodes):
|
|
||||||
if report_progress:
|
|
||||||
report_progress((i+1) / float(len(nodes)), _('Getting list of books on device...'))
|
|
||||||
if hasattr(book, 'tagName') and book.tagName.endswith('text'):
|
|
||||||
tags = [i.getAttribute('title') for i in self.get_playlists(book.getAttribute('id'))]
|
|
||||||
self.append(Book(book, mountpath, tags, prefix=self.prefix))
|
|
||||||
|
|
||||||
def max_id(self):
|
def max_id(self):
|
||||||
max = 0
|
max = 0
|
||||||
@ -180,32 +110,32 @@ class BookList(_BookList):
|
|||||||
return child
|
return child
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add_book(self, mi, name, collections, size, ctime):
|
def add_book(self, book, collections):
|
||||||
|
if book in self:
|
||||||
|
return
|
||||||
""" Add a node into the DOM tree, representing a book """
|
""" Add a node into the DOM tree, representing a book """
|
||||||
book = self.book_by_path(name)
|
|
||||||
if book is not None:
|
|
||||||
self.remove_book(name)
|
|
||||||
|
|
||||||
node = self.document.createElement(self.prefix + "text")
|
node = self.document.createElement(self.prefix + "text")
|
||||||
mime = MIME_MAP.get(name.rpartition('.')[-1].lower(), MIME_MAP['epub'])
|
mime = MIME_MAP.get(book.lpath.rpartition('.')[-1].lower(), MIME_MAP['epub'])
|
||||||
cid = self.max_id()+1
|
cid = self.max_id()+1
|
||||||
|
book.sony_id = cid
|
||||||
|
self.append(book)
|
||||||
try:
|
try:
|
||||||
sourceid = str(self[0].sourceid) if len(self) else '1'
|
sourceid = str(self[0].sourceid) if len(self) else '1'
|
||||||
except:
|
except:
|
||||||
sourceid = '1'
|
sourceid = '1'
|
||||||
attrs = {
|
attrs = {
|
||||||
"title" : mi.title,
|
"title" : book.title,
|
||||||
'titleSorter' : sortable_title(mi.title),
|
'titleSorter' : sortable_title(book.title),
|
||||||
"author" : mi.format_authors() if mi.format_authors() else _('Unknown'),
|
"author" : book.format_authors() if book.format_authors() else _('Unknown'),
|
||||||
"page":"0", "part":"0", "scale":"0", \
|
"page":"0", "part":"0", "scale":"0", \
|
||||||
"sourceid":sourceid, "id":str(cid), "date":"", \
|
"sourceid":sourceid, "id":str(cid), "date":"", \
|
||||||
"mime":mime, "path":name, "size":str(size)
|
"mime":mime, "path":book.lpath, "size":str(book.size)
|
||||||
}
|
}
|
||||||
for attr in attrs.keys():
|
for attr in attrs.keys():
|
||||||
node.setAttributeNode(self.document.createAttribute(attr))
|
node.setAttributeNode(self.document.createAttribute(attr))
|
||||||
node.setAttribute(attr, attrs[attr])
|
node.setAttribute(attr, attrs[attr])
|
||||||
try:
|
try:
|
||||||
w, h, data = mi.thumbnail
|
w, h, data = book.thumbnail
|
||||||
except:
|
except:
|
||||||
w, h, data = None, None, None
|
w, h, data = None, None, None
|
||||||
|
|
||||||
@ -218,14 +148,11 @@ class BookList(_BookList):
|
|||||||
th.appendChild(jpeg)
|
th.appendChild(jpeg)
|
||||||
node.appendChild(th)
|
node.appendChild(th)
|
||||||
self.root_element.appendChild(node)
|
self.root_element.appendChild(node)
|
||||||
book = Book(node, self.mountpath, [], prefix=self.prefix)
|
|
||||||
book.datetime = ctime
|
|
||||||
self.append(book)
|
|
||||||
|
|
||||||
tags = []
|
tags = []
|
||||||
for item in collections:
|
for item in collections:
|
||||||
item = item.strip()
|
item = item.strip()
|
||||||
mitem = getattr(mi, item, None)
|
mitem = getattr(book, item, None)
|
||||||
titems = []
|
titems = []
|
||||||
if mitem:
|
if mitem:
|
||||||
if isinstance(mitem, list):
|
if isinstance(mitem, list):
|
||||||
@ -241,37 +168,34 @@ class BookList(_BookList):
|
|||||||
tags.extend(titems)
|
tags.extend(titems)
|
||||||
if tags:
|
if tags:
|
||||||
tags = list(set(tags))
|
tags = list(set(tags))
|
||||||
if hasattr(mi, 'tag_order'):
|
if hasattr(book, 'tag_order'):
|
||||||
self.tag_order.update(mi.tag_order)
|
self.tag_order.update(book.tag_order)
|
||||||
self.set_tags(book, tags)
|
self.set_playlists(cid, tags)
|
||||||
|
|
||||||
def _delete_book(self, node):
|
def _delete_node(self, node):
|
||||||
nid = node.getAttribute('id')
|
nid = node.getAttribute('id')
|
||||||
self.remove_from_playlists(nid)
|
self.remove_from_playlists(nid)
|
||||||
node.parentNode.removeChild(node)
|
node.parentNode.removeChild(node)
|
||||||
node.unlink()
|
node.unlink()
|
||||||
|
|
||||||
def delete_book(self, cid):
|
def delete_node(self, lpath):
|
||||||
'''
|
'''
|
||||||
Remove DOM node corresponding to book with C{id == cid}.
|
Remove DOM node corresponding to book with lpath.
|
||||||
Also remove book from any collections it is part of.
|
Also remove book from any collections it is part of.
|
||||||
'''
|
'''
|
||||||
for book in self:
|
for child in self.root_element.childNodes:
|
||||||
if str(book.id) == str(cid):
|
if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("id"):
|
||||||
self.remove(book)
|
if child.getAttribute('path') == lpath:
|
||||||
self._delete_book(book.elem)
|
self._delete_node(child)
|
||||||
break
|
break
|
||||||
|
|
||||||
def remove_book(self, path):
|
def remove_book(self, book):
|
||||||
'''
|
'''
|
||||||
Remove DOM node corresponding to book with C{path == path}.
|
Remove DOM node corresponding to book with C{path == path}.
|
||||||
Also remove book from any collections it is part of.
|
Also remove book from any collections it is part of.
|
||||||
'''
|
'''
|
||||||
for book in self:
|
self.remove(book)
|
||||||
if path.endswith(book.rpath):
|
self.delete_node(book.lpath)
|
||||||
self.remove(book)
|
|
||||||
self._delete_book(book.elem)
|
|
||||||
break
|
|
||||||
|
|
||||||
def playlists(self):
|
def playlists(self):
|
||||||
ans = []
|
ans = []
|
||||||
@ -343,11 +267,6 @@ class BookList(_BookList):
|
|||||||
pli.parentNode.removeChild(pli)
|
pli.parentNode.removeChild(pli)
|
||||||
pli.unlink()
|
pli.unlink()
|
||||||
|
|
||||||
def set_tags(self, book, tags):
|
|
||||||
tags = [t for t in tags if t]
|
|
||||||
book.tags = tags
|
|
||||||
self.set_playlists(book.id, tags)
|
|
||||||
|
|
||||||
def set_playlists(self, id, collections):
|
def set_playlists(self, id, collections):
|
||||||
self.remove_from_playlists(id)
|
self.remove_from_playlists(id)
|
||||||
for collection in set(collections):
|
for collection in set(collections):
|
||||||
@ -358,15 +277,6 @@ class BookList(_BookList):
|
|||||||
item.setAttribute('id', str(id))
|
item.setAttribute('id', str(id))
|
||||||
coll.appendChild(item)
|
coll.appendChild(item)
|
||||||
|
|
||||||
def get_playlists(self, bookid):
|
|
||||||
ans = []
|
|
||||||
for pl in self.playlists():
|
|
||||||
for item in pl.childNodes:
|
|
||||||
if hasattr(item, 'tagName') and item.tagName.endswith('item'):
|
|
||||||
if item.getAttribute('id') == str(bookid):
|
|
||||||
ans.append(pl)
|
|
||||||
return ans
|
|
||||||
|
|
||||||
def next_id(self):
|
def next_id(self):
|
||||||
return self.document.documentElement.getAttribute('nextID')
|
return self.document.documentElement.getAttribute('nextID')
|
||||||
|
|
||||||
@ -378,27 +288,41 @@ class BookList(_BookList):
|
|||||||
src = self.document.toxml('utf-8') + '\n'
|
src = self.document.toxml('utf-8') + '\n'
|
||||||
stream.write(src.replace("'", '''))
|
stream.write(src.replace("'", '''))
|
||||||
|
|
||||||
def book_by_id(self, id):
|
|
||||||
for book in self:
|
|
||||||
if str(book.id) == str(id):
|
|
||||||
return book
|
|
||||||
|
|
||||||
def reorder_playlists(self):
|
def reorder_playlists(self):
|
||||||
|
sony_id_cache = {}
|
||||||
|
for child in self.root_element.childNodes:
|
||||||
|
if child.nodeType == child.ELEMENT_NODE and child.hasAttribute("id"):
|
||||||
|
sony_id_cache[child.getAttribute('id')] = child.getAttribute('path')
|
||||||
|
|
||||||
|
books_lpath_cache = {}
|
||||||
|
for book in self:
|
||||||
|
books_lpath_cache[book.lpath] = book
|
||||||
|
|
||||||
for title in self.tag_order.keys():
|
for title in self.tag_order.keys():
|
||||||
pl = self.playlist_by_title(title)
|
pl = self.playlist_by_title(title)
|
||||||
if not pl:
|
if not pl:
|
||||||
continue
|
continue
|
||||||
db_ids = [i.getAttribute('id') for i in pl.childNodes if hasattr(i, 'getAttribute')]
|
# make a list of the ids
|
||||||
pl_book_ids = [getattr(self.book_by_id(i), 'db_id', None) for i in db_ids]
|
sony_ids = [id.getAttribute('id') \
|
||||||
|
for id in pl.childNodes if hasattr(id, 'getAttribute')]
|
||||||
|
# convert IDs in playlist to a list of lpaths
|
||||||
|
sony_paths = [sony_id_cache[id] for id in sony_ids]
|
||||||
|
# create list of books containing lpaths
|
||||||
|
books = [books_lpath_cache.get(p, None) for p in sony_paths]
|
||||||
|
# create dict of db_id -> sony_id
|
||||||
imap = {}
|
imap = {}
|
||||||
for i, j in zip(pl_book_ids, db_ids):
|
for book, sony_id in zip(books, sony_ids):
|
||||||
imap[i] = j
|
if book is not None:
|
||||||
pl_book_ids = [i for i in pl_book_ids if i is not None]
|
imap[book.application_id] = sony_id
|
||||||
ordered_ids = [i for i in self.tag_order[title] if i in pl_book_ids]
|
# filter the list, removing books not on device but on playlist
|
||||||
|
books = [i for i in books if i is not None]
|
||||||
|
# filter the order specification to the books we have
|
||||||
|
ordered_ids = [db_id for db_id in self.tag_order[title] if db_id in imap]
|
||||||
|
|
||||||
|
# rewrite the playlist in the correct order
|
||||||
if len(ordered_ids) < len(pl.childNodes):
|
if len(ordered_ids) < len(pl.childNodes):
|
||||||
continue
|
continue
|
||||||
children = [i for i in pl.childNodes if hasattr(i, 'getAttribute')]
|
children = [i for i in pl.childNodes if hasattr(i, 'getAttribute')]
|
||||||
for child in children:
|
for child in children:
|
||||||
pl.removeChild(child)
|
pl.removeChild(child)
|
||||||
child.unlink()
|
child.unlink()
|
||||||
@ -439,7 +363,6 @@ def fix_ids(main, carda, cardb):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
item.parentNode.removeChild(item)
|
item.parentNode.removeChild(item)
|
||||||
item.unlink()
|
item.unlink()
|
||||||
|
|
||||||
db.reorder_playlists()
|
db.reorder_playlists()
|
||||||
|
|
||||||
regen_ids(main)
|
regen_ids(main)
|
||||||
|
@ -11,15 +11,14 @@ Device driver for the SONY PRS-505
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
from itertools import cycle
|
|
||||||
|
|
||||||
from calibre.devices.usbms.cli import CLI
|
from calibre.devices.usbms.driver import USBMS
|
||||||
from calibre.devices.usbms.device import Device
|
|
||||||
from calibre.devices.prs505.books import BookList, fix_ids
|
from calibre.devices.prs505.books import BookList, fix_ids
|
||||||
|
from calibre.devices.prs505 import MEDIA_XML
|
||||||
|
from calibre.devices.prs505 import CACHE_XML
|
||||||
from calibre import __appname__
|
from calibre import __appname__
|
||||||
|
|
||||||
class PRS505(CLI, Device):
|
class PRS505(USBMS):
|
||||||
|
|
||||||
name = 'PRS-300/505 Device Interface'
|
name = 'PRS-300/505 Device Interface'
|
||||||
gui_name = 'SONY Reader'
|
gui_name = 'SONY Reader'
|
||||||
@ -46,9 +45,6 @@ class PRS505(CLI, Device):
|
|||||||
MAIN_MEMORY_VOLUME_LABEL = 'Sony Reader Main Memory'
|
MAIN_MEMORY_VOLUME_LABEL = 'Sony Reader Main Memory'
|
||||||
STORAGE_CARD_VOLUME_LABEL = 'Sony Reader Storage Card'
|
STORAGE_CARD_VOLUME_LABEL = 'Sony Reader Storage Card'
|
||||||
|
|
||||||
MEDIA_XML = 'database/cache/media.xml'
|
|
||||||
CACHE_XML = 'Sony Reader/database/cache.xml'
|
|
||||||
|
|
||||||
CARD_PATH_PREFIX = __appname__
|
CARD_PATH_PREFIX = __appname__
|
||||||
|
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
@ -60,67 +56,18 @@ class PRS505(CLI, Device):
|
|||||||
'series, tags, authors'
|
'series, tags, authors'
|
||||||
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(['series', 'tags'])
|
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(['series', 'tags'])
|
||||||
|
|
||||||
|
METADATA_CACHE = "database/cache/metadata.calibre"
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
USBMS.initialize(self)
|
||||||
|
self.booklist_class = BookList
|
||||||
|
|
||||||
def windows_filter_pnp_id(self, pnp_id):
|
def windows_filter_pnp_id(self, pnp_id):
|
||||||
return '_LAUNCHER' in pnp_id
|
return '_LAUNCHER' in pnp_id
|
||||||
|
|
||||||
def open(self):
|
|
||||||
self.report_progress = lambda x, y: x
|
|
||||||
Device.open(self)
|
|
||||||
|
|
||||||
def write_cache(prefix):
|
|
||||||
try:
|
|
||||||
cachep = os.path.join(prefix, *(self.CACHE_XML.split('/')))
|
|
||||||
if not os.path.exists(cachep):
|
|
||||||
dname = os.path.dirname(cachep)
|
|
||||||
if not os.path.exists(dname):
|
|
||||||
try:
|
|
||||||
os.makedirs(dname, mode=0777)
|
|
||||||
except:
|
|
||||||
time.sleep(5)
|
|
||||||
os.makedirs(dname, mode=0777)
|
|
||||||
with open(cachep, 'wb') as f:
|
|
||||||
f.write(u'''<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<cache xmlns="http://www.kinoma.com/FskCache/1">
|
|
||||||
</cache>
|
|
||||||
'''.encode('utf8'))
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
if self._card_a_prefix is not None:
|
|
||||||
if not write_cache(self._card_a_prefix):
|
|
||||||
self._card_a_prefix = None
|
|
||||||
if self._card_b_prefix is not None:
|
|
||||||
if not write_cache(self._card_b_prefix):
|
|
||||||
self._card_b_prefix = None
|
|
||||||
|
|
||||||
def get_device_information(self, end_session=True):
|
def get_device_information(self, end_session=True):
|
||||||
return (self.gui_name, '', '', '')
|
return (self.gui_name, '', '', '')
|
||||||
|
|
||||||
def books(self, oncard=None, end_session=True):
|
|
||||||
if oncard == 'carda' and not self._card_a_prefix:
|
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
|
||||||
return []
|
|
||||||
elif oncard == 'cardb' and not self._card_b_prefix:
|
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
|
||||||
return []
|
|
||||||
elif oncard and oncard != 'carda' and oncard != 'cardb':
|
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
|
||||||
return []
|
|
||||||
|
|
||||||
db = self.__class__.CACHE_XML if oncard else self.__class__.MEDIA_XML
|
|
||||||
prefix = self._card_a_prefix if oncard == 'carda' else self._card_b_prefix if oncard == 'cardb' else self._main_prefix
|
|
||||||
bl = BookList(open(prefix + db, 'rb'), prefix, self.report_progress)
|
|
||||||
paths = bl.purge_corrupted_files()
|
|
||||||
for path in paths:
|
|
||||||
path = os.path.join(prefix, path)
|
|
||||||
if os.path.exists(path):
|
|
||||||
os.unlink(path)
|
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
|
||||||
return bl
|
|
||||||
|
|
||||||
def filename_callback(self, fname, mi):
|
def filename_callback(self, fname, mi):
|
||||||
if getattr(mi, 'application_id', None) is not None:
|
if getattr(mi, 'application_id', None) is not None:
|
||||||
base = fname.rpartition('.')[0]
|
base = fname.rpartition('.')[0]
|
||||||
@ -129,90 +76,17 @@ class PRS505(CLI, Device):
|
|||||||
fname = base + suffix + '.' + fname.rpartition('.')[-1]
|
fname = base + suffix + '.' + fname.rpartition('.')[-1]
|
||||||
return fname
|
return fname
|
||||||
|
|
||||||
def upload_books(self, files, names, on_card=None, end_session=True,
|
|
||||||
metadata=None):
|
|
||||||
|
|
||||||
path = self._sanity_check(on_card, files)
|
|
||||||
|
|
||||||
paths, ctimes, sizes = [], [], []
|
|
||||||
names = iter(names)
|
|
||||||
metadata = iter(metadata)
|
|
||||||
for i, infile in enumerate(files):
|
|
||||||
mdata, fname = metadata.next(), names.next()
|
|
||||||
filepath = self.create_upload_path(path, mdata, fname)
|
|
||||||
|
|
||||||
paths.append(filepath)
|
|
||||||
self.put_file(infile, paths[-1], replace_file=True)
|
|
||||||
ctimes.append(os.path.getctime(paths[-1]))
|
|
||||||
sizes.append(os.stat(paths[-1]).st_size)
|
|
||||||
|
|
||||||
self.report_progress((i+1) / float(len(files)), _('Transferring books to device...'))
|
|
||||||
|
|
||||||
self.report_progress(1.0, _('Transferring books to device...'))
|
|
||||||
|
|
||||||
return zip(paths, sizes, ctimes, cycle([on_card]))
|
|
||||||
|
|
||||||
def add_books_to_metadata(self, locations, metadata, booklists):
|
|
||||||
if not locations or not metadata:
|
|
||||||
return
|
|
||||||
|
|
||||||
metadata = iter(metadata)
|
|
||||||
for location in locations:
|
|
||||||
info = metadata.next()
|
|
||||||
path = location[0]
|
|
||||||
oncard = location[3]
|
|
||||||
blist = 2 if oncard == 'cardb' else 1 if oncard == 'carda' else 0
|
|
||||||
|
|
||||||
if self._main_prefix and path.startswith(self._main_prefix):
|
|
||||||
name = path.replace(self._main_prefix, '')
|
|
||||||
elif self._card_a_prefix and path.startswith(self._card_a_prefix):
|
|
||||||
name = path.replace(self._card_a_prefix, '')
|
|
||||||
elif self._card_b_prefix and path.startswith(self._card_b_prefix):
|
|
||||||
name = path.replace(self._card_b_prefix, '')
|
|
||||||
|
|
||||||
name = name.replace('\\', '/')
|
|
||||||
name = name.replace('//', '/')
|
|
||||||
if name.startswith('/'):
|
|
||||||
name = name[1:]
|
|
||||||
|
|
||||||
opts = self.settings()
|
|
||||||
collections = opts.extra_customization.split(',') if opts.extra_customization else []
|
|
||||||
booklist = booklists[blist]
|
|
||||||
if not hasattr(booklist, 'add_book'):
|
|
||||||
raise ValueError(('Incorrect upload location %s. Did you choose the'
|
|
||||||
' correct card A or B, to send books to?')%oncard)
|
|
||||||
booklist.add_book(info, name, collections, *location[1:-1])
|
|
||||||
fix_ids(*booklists)
|
|
||||||
|
|
||||||
def delete_books(self, paths, end_session=True):
|
|
||||||
for i, path in enumerate(paths):
|
|
||||||
self.report_progress((i+1) / float(len(paths)), _('Removing books from device...'))
|
|
||||||
if os.path.exists(path):
|
|
||||||
os.unlink(path)
|
|
||||||
try:
|
|
||||||
os.removedirs(os.path.dirname(path))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
self.report_progress(1.0, _('Removing books from device...'))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def remove_books_from_metadata(cls, paths, booklists):
|
|
||||||
for path in paths:
|
|
||||||
for bl in booklists:
|
|
||||||
if hasattr(bl, 'remove_book'):
|
|
||||||
bl.remove_book(path)
|
|
||||||
fix_ids(*booklists)
|
|
||||||
|
|
||||||
def sync_booklists(self, booklists, end_session=True):
|
def sync_booklists(self, booklists, end_session=True):
|
||||||
|
print 'in sync_booklists'
|
||||||
fix_ids(*booklists)
|
fix_ids(*booklists)
|
||||||
if not os.path.exists(self._main_prefix):
|
if not os.path.exists(self._main_prefix):
|
||||||
os.makedirs(self._main_prefix)
|
os.makedirs(self._main_prefix)
|
||||||
with open(self._main_prefix + self.__class__.MEDIA_XML, 'wb') as f:
|
with open(self._main_prefix + MEDIA_XML, 'wb') as f:
|
||||||
booklists[0].write(f)
|
booklists[0].write(f)
|
||||||
|
|
||||||
def write_card_prefix(prefix, listid):
|
def write_card_prefix(prefix, listid):
|
||||||
if prefix is not None and hasattr(booklists[listid], 'write'):
|
if prefix is not None and hasattr(booklists[listid], 'write'):
|
||||||
tgt = os.path.join(prefix, *(self.CACHE_XML.split('/')))
|
tgt = os.path.join(prefix, *(CACHE_XML.split('/')))
|
||||||
base = os.path.dirname(tgt)
|
base = os.path.dirname(tgt)
|
||||||
if not os.path.exists(base):
|
if not os.path.exists(base):
|
||||||
os.makedirs(base)
|
os.makedirs(base)
|
||||||
@ -221,8 +95,7 @@ class PRS505(CLI, Device):
|
|||||||
write_card_prefix(self._card_a_prefix, 1)
|
write_card_prefix(self._card_a_prefix, 1)
|
||||||
write_card_prefix(self._card_b_prefix, 2)
|
write_card_prefix(self._card_b_prefix, 2)
|
||||||
|
|
||||||
self.report_progress(1.0, _('Sending metadata to device...'))
|
USBMS.sync_booklists(self, booklists, end_session)
|
||||||
|
|
||||||
|
|
||||||
class PRS700(PRS505):
|
class PRS700(PRS505):
|
||||||
|
|
||||||
@ -241,5 +114,3 @@ class PRS700(PRS505):
|
|||||||
OSX_MAIN_MEM = re.compile(r'Sony PRS-((700/[^:]+)|((6|9)00)) Media')
|
OSX_MAIN_MEM = re.compile(r'Sony PRS-((700/[^:]+)|((6|9)00)) Media')
|
||||||
OSX_CARD_A_MEM = re.compile(r'Sony PRS-((700/[^:]+:)|((6|9)00 ))MS Media')
|
OSX_CARD_A_MEM = re.compile(r'Sony PRS-((700/[^:]+:)|((6|9)00 ))MS Media')
|
||||||
OSX_CARD_B_MEM = re.compile(r'Sony PRS-((700/[^:]+:)|((6|9)00 ))SD Media')
|
OSX_CARD_B_MEM = re.compile(r'Sony PRS-((700/[^:]+:)|((6|9)00 ))SD Media')
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +33,9 @@ class Book(MetaInformation):
|
|||||||
self.path = os.path.join(prefix, lpath)
|
self.path = os.path.join(prefix, lpath)
|
||||||
if os.sep == '\\':
|
if os.sep == '\\':
|
||||||
self.path = self.path.replace('/', '\\')
|
self.path = self.path.replace('/', '\\')
|
||||||
self.lpath = lpath
|
self.lpath = lpath.replace('\\', '/')
|
||||||
|
else:
|
||||||
|
self.lpath = lpath
|
||||||
self.mime = mime_type_ext(path_to_ext(lpath))
|
self.mime = mime_type_ext(path_to_ext(lpath))
|
||||||
self.size = os.stat(self.path).st_size if size == None else size
|
self.size = os.stat(self.path).st_size if size == None else size
|
||||||
self.db_id = None
|
self.db_id = None
|
||||||
|
@ -31,32 +31,36 @@ class USBMS(CLI, Device):
|
|||||||
CAN_SET_METADATA = True
|
CAN_SET_METADATA = True
|
||||||
METADATA_CACHE = 'metadata.calibre'
|
METADATA_CACHE = 'metadata.calibre'
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
Device.initialize(self)
|
||||||
|
self.booklist_class = BookList
|
||||||
|
|
||||||
def get_device_information(self, end_session=True):
|
def get_device_information(self, end_session=True):
|
||||||
self.report_progress(1.0, _('Get device information...'))
|
self.report_progress(1.0, _('Get device information...'))
|
||||||
return (self.get_gui_name(), '', '', '')
|
return (self.get_gui_name(), '', '', '')
|
||||||
|
|
||||||
def books(self, oncard=None, end_session=True):
|
def books(self, oncard=None, end_session=True):
|
||||||
from calibre.ebooks.metadata.meta import path_to_ext
|
from calibre.ebooks.metadata.meta import path_to_ext
|
||||||
bl = BookList()
|
|
||||||
metadata = BookList()
|
|
||||||
need_sync = False
|
|
||||||
|
|
||||||
if oncard == 'carda' and not self._card_a_prefix:
|
if oncard == 'carda' and not self._card_a_prefix:
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
self.report_progress(1.0, _('Getting list of books on device...'))
|
||||||
return bl
|
return []
|
||||||
elif oncard == 'cardb' and not self._card_b_prefix:
|
elif oncard == 'cardb' and not self._card_b_prefix:
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
self.report_progress(1.0, _('Getting list of books on device...'))
|
||||||
return bl
|
return []
|
||||||
elif oncard and oncard != 'carda' and oncard != 'cardb':
|
elif oncard and oncard != 'carda' and oncard != 'cardb':
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
self.report_progress(1.0, _('Getting list of books on device...'))
|
||||||
return bl
|
return []
|
||||||
|
|
||||||
prefix = self._card_a_prefix if oncard == 'carda' else self._card_b_prefix if oncard == 'cardb' else self._main_prefix
|
prefix = self._card_a_prefix if oncard == 'carda' else self._card_b_prefix if oncard == 'cardb' else self._main_prefix
|
||||||
|
metadata = self.booklist_class(oncard, prefix)
|
||||||
|
|
||||||
ebook_dirs = self.EBOOK_DIR_CARD_A if oncard == 'carda' else \
|
ebook_dirs = self.EBOOK_DIR_CARD_A if oncard == 'carda' else \
|
||||||
self.EBOOK_DIR_CARD_B if oncard == 'cardb' else \
|
self.EBOOK_DIR_CARD_B if oncard == 'cardb' else \
|
||||||
self.get_main_ebook_dir()
|
self.get_main_ebook_dir()
|
||||||
|
|
||||||
bl, need_sync = self.parse_metadata_cache(prefix, self.METADATA_CACHE)
|
bl, need_sync = self.parse_metadata_cache(prefix, self.METADATA_CACHE,
|
||||||
|
self.booklist_class(oncard, prefix))
|
||||||
|
|
||||||
# make a dict cache of paths so the lookup in the loop below is faster.
|
# make a dict cache of paths so the lookup in the loop below is faster.
|
||||||
bl_cache = {}
|
bl_cache = {}
|
||||||
@ -109,7 +113,6 @@ class USBMS(CLI, Device):
|
|||||||
# find on the device. If need_sync is True then there were either items
|
# find on the device. If need_sync is True then there were either items
|
||||||
# on the device that were not in bl or some of the items were changed.
|
# on the device that were not in bl or some of the items were changed.
|
||||||
if self.count_found_in_bl != len(bl) or need_sync:
|
if self.count_found_in_bl != len(bl) or need_sync:
|
||||||
print 'resync'
|
|
||||||
if oncard == 'cardb':
|
if oncard == 'cardb':
|
||||||
self.sync_booklists((None, None, metadata))
|
self.sync_booklists((None, None, metadata))
|
||||||
elif oncard == 'carda':
|
elif oncard == 'carda':
|
||||||
@ -122,7 +125,6 @@ class USBMS(CLI, Device):
|
|||||||
|
|
||||||
def upload_books(self, files, names, on_card=None, end_session=True,
|
def upload_books(self, files, names, on_card=None, end_session=True,
|
||||||
metadata=None):
|
metadata=None):
|
||||||
|
|
||||||
path = self._sanity_check(on_card, files)
|
path = self._sanity_check(on_card, files)
|
||||||
|
|
||||||
paths = []
|
paths = []
|
||||||
@ -145,7 +147,6 @@ class USBMS(CLI, Device):
|
|||||||
self.report_progress((i+1) / float(len(files)), _('Transferring books to device...'))
|
self.report_progress((i+1) / float(len(files)), _('Transferring books to device...'))
|
||||||
|
|
||||||
self.report_progress(1.0, _('Transferring books to device...'))
|
self.report_progress(1.0, _('Transferring books to device...'))
|
||||||
|
|
||||||
return zip(paths, cycle([on_card]))
|
return zip(paths, cycle([on_card]))
|
||||||
|
|
||||||
def upload_cover(self, path, filename, metadata):
|
def upload_cover(self, path, filename, metadata):
|
||||||
@ -174,11 +175,10 @@ class USBMS(CLI, Device):
|
|||||||
lpath = path.partition(prefix)[2]
|
lpath = path.partition(prefix)[2]
|
||||||
if lpath.startswith(os.sep):
|
if lpath.startswith(os.sep):
|
||||||
lpath = lpath[len(os.sep):]
|
lpath = lpath[len(os.sep):]
|
||||||
lpath = lpath.replace('\\', '/')
|
|
||||||
book = Book(prefix, lpath, other=info)
|
book = Book(prefix, lpath, other=info)
|
||||||
|
opts = self.settings()
|
||||||
if book not in booklists[blist]:
|
collections = opts.extra_customization.split(',') if opts.extra_customization else []
|
||||||
booklists[blist].append(book)
|
booklists[blist].add_book(book, collections, *location[1:-1])
|
||||||
|
|
||||||
self.report_progress(1.0, _('Adding books to device metadata listing...'))
|
self.report_progress(1.0, _('Adding books to device metadata listing...'))
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ class USBMS(CLI, Device):
|
|||||||
for bl in booklists:
|
for bl in booklists:
|
||||||
for book in bl:
|
for book in bl:
|
||||||
if path.endswith(book.path):
|
if path.endswith(book.path):
|
||||||
bl.remove(book)
|
bl.remove_book(book)
|
||||||
self.report_progress(1.0, _('Removing books from device metadata listing...'))
|
self.report_progress(1.0, _('Removing books from device metadata listing...'))
|
||||||
|
|
||||||
def sync_booklists(self, booklists, end_session=True):
|
def sync_booklists(self, booklists, end_session=True):
|
||||||
@ -217,7 +217,7 @@ class USBMS(CLI, Device):
|
|||||||
os.makedirs(self._main_prefix)
|
os.makedirs(self._main_prefix)
|
||||||
|
|
||||||
def write_prefix(prefix, listid):
|
def write_prefix(prefix, listid):
|
||||||
if prefix is not None and isinstance(booklists[listid], BookList):
|
if prefix is not None and isinstance(booklists[listid], self.booklist_class):
|
||||||
if not os.path.exists(prefix):
|
if not os.path.exists(prefix):
|
||||||
os.makedirs(prefix)
|
os.makedirs(prefix)
|
||||||
js = [item.to_json() for item in booklists[listid]]
|
js = [item.to_json() for item in booklists[listid]]
|
||||||
@ -230,9 +230,8 @@ class USBMS(CLI, Device):
|
|||||||
self.report_progress(1.0, _('Sending metadata to device...'))
|
self.report_progress(1.0, _('Sending metadata to device...'))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse_metadata_cache(cls, prefix, name):
|
def parse_metadata_cache(cls, prefix, name, bl):
|
||||||
js = []
|
js = []
|
||||||
bl = BookList()
|
|
||||||
need_sync = False
|
need_sync = False
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(prefix, name), 'rb') as f:
|
with open(os.path.join(prefix, name), 'rb') as f:
|
||||||
@ -249,7 +248,7 @@ class USBMS(CLI, Device):
|
|||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
bl = BookList()
|
bl = []
|
||||||
return bl, need_sync
|
return bl, need_sync
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -254,11 +254,11 @@ class MetaInformation(object):
|
|||||||
setattr(self, x, getattr(mi, x, None))
|
setattr(self, x, getattr(mi, x, None))
|
||||||
|
|
||||||
def print_all_attributes(self):
|
def print_all_attributes(self):
|
||||||
for x in ('author_sort', 'title_sort', 'comments', 'category', 'publisher',
|
for x in ('author', 'author_sort', 'title_sort', 'comments', 'category', 'publisher',
|
||||||
'series', 'series_index', 'rating', 'isbn', 'language',
|
'series', 'series_index', 'tags', 'rating', 'isbn', 'language',
|
||||||
'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
|
'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
|
||||||
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate',
|
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate',
|
||||||
'rights', 'publication_type', 'uuid',
|
'rights', 'publication_type', 'uuid', 'tag_order',
|
||||||
):
|
):
|
||||||
prints(x, getattr(self, x, 'None'))
|
prints(x, getattr(self, x, 'None'))
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ class MetaInformation(object):
|
|||||||
'isbn', 'application_id', 'manifest', 'spine', 'toc',
|
'isbn', 'application_id', 'manifest', 'spine', 'toc',
|
||||||
'cover', 'language', 'guide', 'book_producer',
|
'cover', 'language', 'guide', 'book_producer',
|
||||||
'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', 'rights',
|
'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', 'rights',
|
||||||
'publication_type', 'uuid',):
|
'publication_type', 'uuid', 'tag_order'):
|
||||||
if hasattr(mi, attr):
|
if hasattr(mi, attr):
|
||||||
val = getattr(mi, attr)
|
val = getattr(mi, attr)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
|
@ -821,7 +821,9 @@ class DeviceGUI(object):
|
|||||||
|
|
||||||
def sync_to_device(self, on_card, delete_from_library,
|
def sync_to_device(self, on_card, delete_from_library,
|
||||||
specific_format=None, send_ids=None, do_auto_convert=True):
|
specific_format=None, send_ids=None, do_auto_convert=True):
|
||||||
ids = [self.library_view.model().id(r) for r in self.library_view.selectionModel().selectedRows()] if send_ids is None else send_ids
|
ids = [self.library_view.model().id(r) \
|
||||||
|
for r in self.library_view.selectionModel().selectedRows()] \
|
||||||
|
if send_ids is None else send_ids
|
||||||
if not self.device_manager or not ids or len(ids) == 0:
|
if not self.device_manager or not ids or len(ids) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -842,8 +844,7 @@ class DeviceGUI(object):
|
|||||||
ids = iter(ids)
|
ids = iter(ids)
|
||||||
for mi in metadata:
|
for mi in metadata:
|
||||||
if mi.cover and os.access(mi.cover, os.R_OK):
|
if mi.cover and os.access(mi.cover, os.R_OK):
|
||||||
mi.thumbnail = self.cover_to_thumbnail(open(mi.cover,
|
mi.thumbnail = self.cover_to_thumbnail(open(mi.cover, 'rb').read())
|
||||||
'rb').read())
|
|
||||||
imetadata = iter(metadata)
|
imetadata = iter(metadata)
|
||||||
|
|
||||||
files = [getattr(f, 'name', None) for f in _files]
|
files = [getattr(f, 'name', None) for f in _files]
|
||||||
@ -890,7 +891,9 @@ class DeviceGUI(object):
|
|||||||
bad.append(self.library_view.model().db.title(id, index_is_id=True))
|
bad.append(self.library_view.model().db.title(id, index_is_id=True))
|
||||||
|
|
||||||
if auto != []:
|
if auto != []:
|
||||||
format = specific_format if specific_format in list(set(settings.format_map).intersection(set(available_output_formats()))) else None
|
format = specific_format if specific_format in \
|
||||||
|
list(set(settings.format_map).intersection(set(available_output_formats()))) \
|
||||||
|
else None
|
||||||
if not format:
|
if not format:
|
||||||
for fmt in settings.format_map:
|
for fmt in settings.format_map:
|
||||||
if fmt in list(set(settings.format_map).intersection(set(available_output_formats()))):
|
if fmt in list(set(settings.format_map).intersection(set(available_output_formats()))):
|
||||||
@ -1039,7 +1042,8 @@ class DeviceGUI(object):
|
|||||||
loc[i] = True
|
loc[i] = True
|
||||||
break
|
break
|
||||||
if mi.authors and \
|
if mi.authors and \
|
||||||
re.sub('(?u)\W|[_]', '', mi.authors.lower()) in cache['authors']:
|
re.sub('(?u)\W|[_]', '', authors_to_string(mi.authors).lower()) \
|
||||||
|
in cache['authors']:
|
||||||
loc[i] = True
|
loc[i] = True
|
||||||
break
|
break
|
||||||
return loc
|
return loc
|
||||||
|
@ -17,7 +17,7 @@ from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, pyqtSignal, \
|
|||||||
SIGNAL, QObject, QSize, QModelIndex, QDate
|
SIGNAL, QObject, QSize, QModelIndex, QDate
|
||||||
|
|
||||||
from calibre import strftime
|
from calibre import strftime
|
||||||
from calibre.ebooks.metadata import fmt_sidx, authors_to_string
|
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
|
||||||
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
|
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
|
||||||
from calibre.gui2 import NONE, TableView, config, error_dialog, UNDEFINED_QDATE
|
from calibre.gui2 import NONE, TableView, config, error_dialog, UNDEFINED_QDATE
|
||||||
from calibre.gui2.dialogs.comments_dialog import CommentsDialog
|
from calibre.gui2.dialogs.comments_dialog import CommentsDialog
|
||||||
@ -1378,7 +1378,10 @@ class DeviceBooksModel(BooksModel):
|
|||||||
def libcmp(x, y):
|
def libcmp(x, y):
|
||||||
x, y = self.db[x].in_library, self.db[y].in_library
|
x, y = self.db[x].in_library, self.db[y].in_library
|
||||||
return cmp(x, y)
|
return cmp(x, y)
|
||||||
fcmp = strcmp('title_sorter') if col == 0 else strcmp('authors') if col == 1 else \
|
def authorcmp(x, y):
|
||||||
|
x, y = authors_to_string(self.db[x].authors), authors_to_string(self.db[y].authors)
|
||||||
|
return cmp(x, y)
|
||||||
|
fcmp = strcmp('title_sorter') if col == 0 else authorcmp if col == 1 else \
|
||||||
sizecmp if col == 2 else datecmp if col == 3 else tagscmp if col == 4 else libcmp
|
sizecmp if col == 2 else datecmp if col == 3 else tagscmp if col == 4 else libcmp
|
||||||
self.map.sort(cmp=fcmp, reverse=descending)
|
self.map.sort(cmp=fcmp, reverse=descending)
|
||||||
if len(self.map) == len(self.db):
|
if len(self.map) == len(self.db):
|
||||||
@ -1446,9 +1449,9 @@ class DeviceBooksModel(BooksModel):
|
|||||||
au = self.db[self.map[row]].authors
|
au = self.db[self.map[row]].authors
|
||||||
if not au:
|
if not au:
|
||||||
au = self.unknown
|
au = self.unknown
|
||||||
if role == Qt.EditRole:
|
# if role == Qt.EditRole:
|
||||||
return QVariant(authors_to_string(au))
|
# return QVariant(au)
|
||||||
return QVariant(" & ".join(au))
|
return QVariant(authors_to_string(au))
|
||||||
elif col == 2:
|
elif col == 2:
|
||||||
size = self.db[self.map[row]].size
|
size = self.db[self.map[row]].size
|
||||||
return QVariant(BooksView.human_readable(size))
|
return QVariant(BooksView.human_readable(size))
|
||||||
@ -1501,7 +1504,7 @@ class DeviceBooksModel(BooksModel):
|
|||||||
self.db[idx].title = val
|
self.db[idx].title = val
|
||||||
self.db[idx].title_sorter = val
|
self.db[idx].title_sorter = val
|
||||||
elif col == 1:
|
elif col == 1:
|
||||||
self.db[idx].authors = val
|
self.db[idx].authors = string_to_authors(val)
|
||||||
elif col == 4:
|
elif col == 4:
|
||||||
tags = [i.strip() for i in val.split(',')]
|
tags = [i.strip() for i in val.split(',')]
|
||||||
tags = [t for t in tags if t]
|
tags = [t for t in tags if t]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user