mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Make series index a float and add a column for publish date
This commit is contained in:
parent
597fd045a9
commit
3585977bb3
@ -42,6 +42,31 @@ def title_sort(title):
|
|||||||
title = title.replace(prep, '') + ', ' + prep
|
title = title.replace(prep, '') + ', ' + prep
|
||||||
return title.strip()
|
return title.strip()
|
||||||
|
|
||||||
|
coding = zip(
|
||||||
|
[1000,900,500,400,100,90,50,40,10,9,5,4,1],
|
||||||
|
["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def roman(num):
|
||||||
|
if num <= 0 or num >= 4000 or int(num) != num:
|
||||||
|
return str(num)
|
||||||
|
result = []
|
||||||
|
for d, r in coding:
|
||||||
|
while num >= d:
|
||||||
|
result.append(r)
|
||||||
|
num -= d
|
||||||
|
return ''.join(result)
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_sidx(i, fmt='%.2f', use_roman=False):
|
||||||
|
if i is None:
|
||||||
|
i = 1
|
||||||
|
if int(i) == i:
|
||||||
|
return roman(i) if use_roman else '%d'%i
|
||||||
|
return fmt%i
|
||||||
|
|
||||||
|
|
||||||
class Resource(object):
|
class Resource(object):
|
||||||
'''
|
'''
|
||||||
@ -187,7 +212,8 @@ class MetaInformation(object):
|
|||||||
'publisher', 'series', 'series_index', 'rating',
|
'publisher', 'series', 'series_index', 'rating',
|
||||||
'isbn', 'tags', 'cover_data', 'application_id', 'guide',
|
'isbn', 'tags', 'cover_data', 'application_id', 'guide',
|
||||||
'manifest', 'spine', 'toc', 'cover', 'language',
|
'manifest', 'spine', 'toc', 'cover', 'language',
|
||||||
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc'):
|
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc',
|
||||||
|
'pubdate'):
|
||||||
if hasattr(mi, attr):
|
if hasattr(mi, attr):
|
||||||
setattr(ans, attr, getattr(mi, attr))
|
setattr(ans, attr, getattr(mi, attr))
|
||||||
|
|
||||||
@ -212,7 +238,7 @@ class MetaInformation(object):
|
|||||||
for x in ('author_sort', 'title_sort', 'comments', 'category', 'publisher',
|
for x in ('author_sort', 'title_sort', 'comments', 'category', 'publisher',
|
||||||
'series', 'series_index', 'rating', 'isbn', 'language',
|
'series', 'series_index', 'rating', 'isbn', 'language',
|
||||||
'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
|
'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
|
||||||
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc'
|
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate'
|
||||||
):
|
):
|
||||||
setattr(self, x, getattr(mi, x, None))
|
setattr(self, x, getattr(mi, x, None))
|
||||||
|
|
||||||
@ -231,7 +257,7 @@ class MetaInformation(object):
|
|||||||
'publisher', 'series', 'series_index', 'rating',
|
'publisher', 'series', 'series_index', 'rating',
|
||||||
'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'):
|
'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate'):
|
||||||
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:
|
||||||
@ -262,8 +288,8 @@ class MetaInformation(object):
|
|||||||
try:
|
try:
|
||||||
x = float(self.series_index)
|
x = float(self.series_index)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
x = 1.0
|
x = 1
|
||||||
return '%d'%x if int(x) == x else '%.2f'%x
|
return fmt_sidx(x)
|
||||||
|
|
||||||
def authors_from_string(self, raw):
|
def authors_from_string(self, raw):
|
||||||
self.authors = string_to_authors(raw)
|
self.authors = string_to_authors(raw)
|
||||||
@ -299,6 +325,8 @@ class MetaInformation(object):
|
|||||||
fmt('Rating', self.rating)
|
fmt('Rating', self.rating)
|
||||||
if self.timestamp is not None:
|
if self.timestamp is not None:
|
||||||
fmt('Timestamp', self.timestamp.isoformat(' '))
|
fmt('Timestamp', self.timestamp.isoformat(' '))
|
||||||
|
if self.pubdate is not None:
|
||||||
|
fmt('Published', self.pubdate.isoformat(' '))
|
||||||
if self.lccn:
|
if self.lccn:
|
||||||
fmt('LCCN', unicode(self.lccn))
|
fmt('LCCN', unicode(self.lccn))
|
||||||
if self.lcc:
|
if self.lcc:
|
||||||
@ -327,6 +355,8 @@ class MetaInformation(object):
|
|||||||
ans += [(_('Language'), unicode(self.language))]
|
ans += [(_('Language'), unicode(self.language))]
|
||||||
if self.timestamp is not None:
|
if self.timestamp is not None:
|
||||||
ans += [(_('Timestamp'), unicode(self.timestamp.isoformat(' ')))]
|
ans += [(_('Timestamp'), unicode(self.timestamp.isoformat(' ')))]
|
||||||
|
if self.pubdate is not None:
|
||||||
|
ans += [(_('Published'), unicode(self.pubdate.isoformat(' ')))]
|
||||||
for i, x in enumerate(ans):
|
for i, x in enumerate(ans):
|
||||||
ans[i] = u'<tr><td><b>%s</b></td><td>%s</td></tr>'%x
|
ans[i] = u'<tr><td><b>%s</b></td><td>%s</td></tr>'%x
|
||||||
return u'<table>%s</table>'%u'\n'.join(ans)
|
return u'<table>%s</table>'%u'\n'.join(ans)
|
||||||
|
@ -5,7 +5,7 @@ __copyright__ = '2008, Anatoly Shipitsin <norguhtar at gmail.com>'
|
|||||||
|
|
||||||
'''Read meta information from fb2 files'''
|
'''Read meta information from fb2 files'''
|
||||||
|
|
||||||
import sys, os, mimetypes
|
import mimetypes
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
||||||
@ -42,7 +42,7 @@ def get_metadata(stream):
|
|||||||
if series:
|
if series:
|
||||||
mi.series = series.get('name', None)
|
mi.series = series.get('name', None)
|
||||||
try:
|
try:
|
||||||
mi.series_index = int(series.get('number', None))
|
mi.series_index = float(series.get('number', None))
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
pass
|
pass
|
||||||
if cdata:
|
if cdata:
|
||||||
|
@ -145,7 +145,7 @@ def metadata_from_filename(name, pat=None):
|
|||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
si = match.group('series_index')
|
si = match.group('series_index')
|
||||||
mi.series_index = int(si)
|
mi.series_index = float(si)
|
||||||
except (IndexError, ValueError, TypeError):
|
except (IndexError, ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
|
@ -2,8 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
'''Read/Write metadata from Open Packaging Format (.opf) files.'''
|
'''Read/Write metadata from Open Packaging Format (.opf) files.'''
|
||||||
|
|
||||||
import sys, re, os, glob
|
import re, os
|
||||||
import cStringIO
|
|
||||||
import uuid
|
import uuid
|
||||||
from urllib import unquote, quote
|
from urllib import unquote, quote
|
||||||
|
|
||||||
@ -374,7 +373,7 @@ class OPF(MetaInformation):
|
|||||||
s = self.metadata.find('series-index')
|
s = self.metadata.find('series-index')
|
||||||
if s and s.string:
|
if s and s.string:
|
||||||
try:
|
try:
|
||||||
return int(str(s.string).strip())
|
return float(str(s.string).strip())
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
return None
|
return None
|
||||||
|
@ -9,15 +9,16 @@
|
|||||||
<dc:creator opf:role="aut" py:for="i, author in enumerate(mi.authors)" py:attrs="{'opf:file-as':mi.author_sort} if mi.author_sort and i == 0 else {}">${author}</dc:creator>
|
<dc:creator opf:role="aut" py:for="i, author in enumerate(mi.authors)" py:attrs="{'opf:file-as':mi.author_sort} if mi.author_sort and i == 0 else {}">${author}</dc:creator>
|
||||||
<dc:contributor opf:role="bkp" py:with="attrs={'opf:file-as':__appname__}" py:attrs="attrs">${'%s (%s)'%(__appname__, __version__)} [http://${__appname__}.kovidgoyal.net]</dc:contributor>
|
<dc:contributor opf:role="bkp" py:with="attrs={'opf:file-as':__appname__}" py:attrs="attrs">${'%s (%s)'%(__appname__, __version__)} [http://${__appname__}.kovidgoyal.net]</dc:contributor>
|
||||||
<dc:identifier opf:scheme="${__appname__}" id="${__appname__}_id">${mi.application_id}</dc:identifier>
|
<dc:identifier opf:scheme="${__appname__}" id="${__appname__}_id">${mi.application_id}</dc:identifier>
|
||||||
<dc:date py:if="getattr(mi, 'timestamp', None) is not None">${mi.timestamp.isoformat()}</dc:date>
|
<dc:date py:if="getattr(mi, 'pubdate', None) is not None">${mi.pubdate.isoformat()}</dc:date>
|
||||||
<dc:language>${mi.language if mi.language else 'UND'}</dc:language>
|
<dc:language>${mi.language if mi.language else 'UND'}</dc:language>
|
||||||
<dc:type py:if="getattr(mi, 'category', False)">${mi.category}</dc:type>
|
<dc:type py:if="getattr(mi, 'category', False)">${mi.category}</dc:type>
|
||||||
<dc:description py:if="mi.comments">${mi.comments}</dc:description>
|
<dc:description py:if="mi.comments">${mi.comments}</dc:description>
|
||||||
<dc:publisher py:if="mi.publisher">${mi.publisher}</dc:publisher>
|
<dc:publisher py:if="mi.publisher">${mi.publisher}</dc:publisher>
|
||||||
<dc:identifier opf:scheme="ISBN" py:if="mi.isbn">${mi.isbn}</dc:identifier>
|
<dc:identifier opf:scheme="ISBN" py:if="mi.isbn">${mi.isbn}</dc:identifier>
|
||||||
<meta py:if="mi.series is not None" name="calibre:series" content="${mi.series}"/>
|
<meta py:if="mi.series is not None" name="calibre:series" content="${mi.series}"/>
|
||||||
<meta py:if="mi.series_index is not None" name="calibre:series_index" content="${mi.series_index}"/>
|
<meta py:if="mi.series_index is not None" name="calibre:series_index" content="${mi.format_series_index()}"/>
|
||||||
<meta py:if="mi.rating is not None" name="calibre:rating" content="${mi.rating}"/>
|
<meta py:if="mi.rating is not None" name="calibre:rating" content="${mi.rating}"/>
|
||||||
|
<meta py:if="mi.timestamp is not None" name="calibre:timestamp" content="${mi.timestamp.isoformat()}"/>
|
||||||
<py:for each="tag in mi.tags">
|
<py:for each="tag in mi.tags">
|
||||||
<dc:subject py:if="mi.tags is not None">${tag}</dc:subject>
|
<dc:subject py:if="mi.tags is not None">${tag}</dc:subject>
|
||||||
</py:for>
|
</py:for>
|
||||||
|
@ -442,9 +442,10 @@ class OPF(object):
|
|||||||
comments = MetadataField('description')
|
comments = MetadataField('description')
|
||||||
category = MetadataField('category')
|
category = MetadataField('category')
|
||||||
series = MetadataField('series', is_dc=False)
|
series = MetadataField('series', is_dc=False)
|
||||||
series_index = MetadataField('series_index', is_dc=False, formatter=int, none_is=1)
|
series_index = MetadataField('series_index', is_dc=False, formatter=float, none_is=1)
|
||||||
rating = MetadataField('rating', is_dc=False, formatter=int)
|
rating = MetadataField('rating', is_dc=False, formatter=int)
|
||||||
timestamp = MetadataField('date', formatter=parser.parse)
|
pubdate = MetadataField('date', formatter=parser.parse)
|
||||||
|
timestamp = MetadataField('timestamp', is_dc=False, formatter=parser.parse)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, stream, basedir=os.getcwdu(), unquote_urls=True):
|
def __init__(self, stream, basedir=os.getcwdu(), unquote_urls=True):
|
||||||
|
@ -65,7 +65,7 @@ class Jacket(object):
|
|||||||
comments = comments.replace('\r\n', '\n').replace('\n\n', '<br/><br/>')
|
comments = comments.replace('\r\n', '\n').replace('\n\n', '<br/><br/>')
|
||||||
series = '<b>Series: </b>' + mi.series if mi.series else ''
|
series = '<b>Series: </b>' + mi.series if mi.series else ''
|
||||||
if series and mi.series_index is not None:
|
if series and mi.series_index is not None:
|
||||||
series += ' [%s]'%mi.series_index
|
series += ' [%s]'%mi.format_series_index()
|
||||||
tags = mi.tags
|
tags = mi.tags
|
||||||
if not tags:
|
if not tags:
|
||||||
try:
|
try:
|
||||||
|
@ -58,7 +58,7 @@ class MergeMetadata(object):
|
|||||||
m.add('creator', mi.book_producer, role='bkp')
|
m.add('creator', mi.book_producer, role='bkp')
|
||||||
if mi.series_index is not None:
|
if mi.series_index is not None:
|
||||||
m.clear('series_index')
|
m.clear('series_index')
|
||||||
m.add('series_index', '%.2f'%mi.series_index)
|
m.add('series_index', mi.format_series_index())
|
||||||
if mi.rating is not None:
|
if mi.rating is not None:
|
||||||
m.clear('rating')
|
m.clear('rating')
|
||||||
m.add('rating', '%.2f'%mi.rating)
|
m.add('rating', '%.2f'%mi.rating)
|
||||||
|
@ -19,7 +19,8 @@ from calibre.ebooks.metadata import MetaInformation
|
|||||||
|
|
||||||
NONE = QVariant() #: Null value to return from the data function of item models
|
NONE = QVariant() #: Null value to return from the data function of item models
|
||||||
|
|
||||||
ALL_COLUMNS = ['title', 'authors', 'size', 'timestamp', 'rating', 'publisher', 'tags', 'series']
|
ALL_COLUMNS = ['title', 'authors', 'size', 'timestamp', 'rating', 'publisher',
|
||||||
|
'tags', 'series', 'pubdate']
|
||||||
|
|
||||||
def _config():
|
def _config():
|
||||||
c = Config('gui', 'preferences for the calibre GUI')
|
c = Config('gui', 'preferences for the calibre GUI')
|
||||||
|
@ -119,7 +119,8 @@ class Widget(QWidget):
|
|||||||
elif isinstance(g, XPathEdit):
|
elif isinstance(g, XPathEdit):
|
||||||
g.edit.setText(val if val else '')
|
g.edit.setText(val if val else '')
|
||||||
else:
|
else:
|
||||||
raise Exception('Can\'t set value %s in %s'%(repr(val), type(g)))
|
raise Exception('Can\'t set value %s in %s'%(repr(val),
|
||||||
|
unicode(g.objectName())))
|
||||||
self.post_set_value(g, val)
|
self.post_set_value(g, val)
|
||||||
|
|
||||||
def set_help(self, msg):
|
def set_help(self, msg):
|
||||||
|
@ -83,7 +83,7 @@ class MetadataWidget(Widget, Ui_Form):
|
|||||||
comments = unicode(self.comment.toPlainText()).strip()
|
comments = unicode(self.comment.toPlainText()).strip()
|
||||||
if comments:
|
if comments:
|
||||||
mi.comments = comments
|
mi.comments = comments
|
||||||
mi.series_index = int(self.series_index.value())
|
mi.series_index = float(self.series_index.value())
|
||||||
if self.series.currentIndex() > -1:
|
if self.series.currentIndex() > -1:
|
||||||
mi.series = unicode(self.series.currentText()).strip()
|
mi.series = unicode(self.series.currentText()).strip()
|
||||||
tags = [t.strip() for t in unicode(self.tags.text()).strip().split(',')]
|
tags = [t.strip() for t in unicode(self.tags.text()).strip().split(',')]
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<ui version="4.0" >
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
<class>Form</class>
|
<class>Form</class>
|
||||||
<widget class="QWidget" name="Form" >
|
<widget class="QWidget" name="Form">
|
||||||
<property name="geometry" >
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
@ -9,59 +10,89 @@
|
|||||||
<height>500</height>
|
<height>500</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle">
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_4" >
|
<widget class="QGroupBox" name="groupBox_4">
|
||||||
<property name="title" >
|
<property name="title">
|
||||||
<string>Book Cover</string>
|
<string>Book Cover</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="_2" >
|
<layout class="QGridLayout" name="_2">
|
||||||
<item row="1" column="0" >
|
<item row="0" column="0">
|
||||||
<layout class="QVBoxLayout" name="_4" >
|
<layout class="QHBoxLayout" name="_3">
|
||||||
<property name="spacing" >
|
<item>
|
||||||
|
<widget class="ImageView" name="cover">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap">
|
||||||
|
<pixmap resource="../../../../../../calibre/gui2/images.qrc">:/images/book.svg</pixmap>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_prefer_metadata_cover">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use cover from &source file</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<layout class="QVBoxLayout" name="_4">
|
||||||
|
<property name="spacing">
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="margin" >
|
<property name="margin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_5" >
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>Change &cover image:</string>
|
<string>Change &cover image:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>cover_path</cstring>
|
<cstring>cover_path</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="_5" >
|
<layout class="QHBoxLayout" name="_5">
|
||||||
<property name="spacing" >
|
<property name="spacing">
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="margin" >
|
<property name="margin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="cover_path" >
|
<widget class="QLineEdit" name="cover_path">
|
||||||
<property name="readOnly" >
|
<property name="readOnly">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="cover_button" >
|
<widget class="QToolButton" name="cover_button">
|
||||||
<property name="toolTip" >
|
<property name="toolTip">
|
||||||
<string>Browse for an image to use as the cover of this book.</string>
|
<string>Browse for an image to use as the cover of this book.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon" >
|
<property name="icon">
|
||||||
<iconset resource="../../../../../../calibre/gui2/images.qrc" >
|
<iconset resource="../../../../../../calibre/gui2/images.qrc">
|
||||||
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
|
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -70,243 +101,204 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" >
|
|
||||||
<widget class="QCheckBox" name="opt_prefer_metadata_cover" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>Use cover from &source file</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0" >
|
|
||||||
<layout class="QHBoxLayout" name="_3" >
|
|
||||||
<item>
|
|
||||||
<widget class="ImageView" name="cover" >
|
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="pixmap" >
|
|
||||||
<pixmap resource="../../../../../../calibre/gui2/images.qrc" >:/images/book.svg</pixmap>
|
|
||||||
</property>
|
|
||||||
<property name="scaledContents" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="alignment" >
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
<zorder>opt_prefer_metadata_cover</zorder>
|
<zorder>opt_prefer_metadata_cover</zorder>
|
||||||
<zorder></zorder>
|
<zorder></zorder>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="_7" >
|
<layout class="QGridLayout" name="_7">
|
||||||
<item row="0" column="0" >
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label" >
|
<widget class="QLabel" name="label">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>&Title: </string>
|
<string>&Title: </string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment" >
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>title</cstring>
|
<cstring>title</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1" >
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="title" >
|
<widget class="QLineEdit" name="title">
|
||||||
<property name="toolTip" >
|
<property name="toolTip">
|
||||||
<string>Change the title of this book</string>
|
<string>Change the title of this book</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" >
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_2" >
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>&Author(s): </string>
|
<string>&Author(s): </string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment" >
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>author</cstring>
|
<cstring>author</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1" >
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="author" >
|
<widget class="QLineEdit" name="author">
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
<horstretch>1</horstretch>
|
<horstretch>1</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip" >
|
<property name="toolTip">
|
||||||
<string>Change the author(s) of this book. Multiple authors should be separated by an &. If the author name contains an &, use && to represent it.</string>
|
<string>Change the author(s) of this book. Multiple authors should be separated by an &. If the author name contains an &, use && to represent it.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" >
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label_6" >
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>Author So&rt:</string>
|
<string>Author So&rt:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment" >
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>author_sort</cstring>
|
<cstring>author_sort</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1" >
|
<item row="2" column="1">
|
||||||
<widget class="QLineEdit" name="author_sort" >
|
<widget class="QLineEdit" name="author_sort">
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip" >
|
<property name="toolTip">
|
||||||
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string>
|
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" >
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_3" >
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>&Publisher: </string>
|
<string>&Publisher: </string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment" >
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>publisher</cstring>
|
<cstring>publisher</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1" >
|
<item row="3" column="1">
|
||||||
<widget class="QLineEdit" name="publisher" >
|
<widget class="QLineEdit" name="publisher">
|
||||||
<property name="toolTip" >
|
<property name="toolTip">
|
||||||
<string>Change the publisher of this book</string>
|
<string>Change the publisher of this book</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" >
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_4" >
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>Ta&gs: </string>
|
<string>Ta&gs: </string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment" >
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>tags</cstring>
|
<cstring>tags</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1" >
|
<item row="4" column="1">
|
||||||
<widget class="QLineEdit" name="tags" >
|
<widget class="QLineEdit" name="tags">
|
||||||
<property name="toolTip" >
|
<property name="toolTip">
|
||||||
<string>Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.</string>
|
<string>Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0" >
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_7" >
|
<widget class="QLabel" name="label_7">
|
||||||
<property name="text" >
|
<property name="text">
|
||||||
<string>&Series:</string>
|
<string>&Series:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat" >
|
<property name="textFormat">
|
||||||
<enum>Qt::PlainText</enum>
|
<enum>Qt::PlainText</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment" >
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
<property name="buddy">
|
||||||
<cstring>series</cstring>
|
<cstring>series</cstring>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1" >
|
<item row="5" column="1">
|
||||||
<widget class="QComboBox" name="series" >
|
<widget class="QComboBox" name="series">
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
<horstretch>10</horstretch>
|
<horstretch>10</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip" >
|
<property name="toolTip">
|
||||||
<string>List of known series. You can add new series.</string>
|
<string>List of known series. You can add new series.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="whatsThis" >
|
<property name="whatsThis">
|
||||||
<string>List of known series. You can add new series.</string>
|
<string>List of known series. You can add new series.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="editable" >
|
<property name="editable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="insertPolicy" >
|
<property name="insertPolicy">
|
||||||
<enum>QComboBox::InsertAlphabetically</enum>
|
<enum>QComboBox::InsertAlphabetically</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeAdjustPolicy" >
|
<property name="sizeAdjustPolicy">
|
||||||
<enum>QComboBox::AdjustToContents</enum>
|
<enum>QComboBox::AdjustToContents</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1" >
|
<item row="6" column="1">
|
||||||
<widget class="QSpinBox" name="series_index" >
|
<widget class="QDoubleSpinBox" name="series_index">
|
||||||
<property name="enabled" >
|
<property name="prefix">
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip" >
|
|
||||||
<string>Series index.</string>
|
|
||||||
</property>
|
|
||||||
<property name="whatsThis" >
|
|
||||||
<string>Series index.</string>
|
|
||||||
</property>
|
|
||||||
<property name="prefix" >
|
|
||||||
<string>Book </string>
|
<string>Book </string>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum" >
|
<property name="maximum">
|
||||||
<number>1</number>
|
<double>9999.989999999999782</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum" >
|
<property name="value">
|
||||||
<number>10000</number>
|
<double>1.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_2" >
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy">
|
||||||
<sizepolicy vsizetype="Minimum" hsizetype="Minimum" >
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize" >
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>16777215</width>
|
<width>16777215</width>
|
||||||
<height>200</height>
|
<height>200</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="title" >
|
<property name="title">
|
||||||
<string>Comments</string>
|
<string>Comments</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="_8" >
|
<layout class="QGridLayout" name="_8">
|
||||||
<item row="0" column="0" >
|
<item row="0" column="0">
|
||||||
<widget class="QTextEdit" name="comment" >
|
<widget class="QTextEdit" name="comment">
|
||||||
<property name="maximumSize" >
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>16777215</width>
|
<width>16777215</width>
|
||||||
<height>180</height>
|
<height>180</height>
|
||||||
@ -329,8 +321,8 @@
|
|||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../../../calibre/gui2/images.qrc" />
|
<include location="../../../../../../calibre/gui2/images.qrc"/>
|
||||||
<include location="../images.qrc" />
|
<include location="../images.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -177,7 +177,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1" colspan="2">
|
<item row="3" column="1">
|
||||||
<widget class="QSpinBox" name="rating">
|
<widget class="QSpinBox" name="rating">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Rating of this book. 0-5 stars</string>
|
<string>Rating of this book. 0-5 stars</string>
|
||||||
@ -309,28 +309,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="1" colspan="2">
|
|
||||||
<widget class="QSpinBox" name="series_index">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Series index.</string>
|
|
||||||
</property>
|
|
||||||
<property name="whatsThis">
|
|
||||||
<string>Series index.</string>
|
|
||||||
</property>
|
|
||||||
<property name="prefix">
|
|
||||||
<string>Book </string>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>10000</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0">
|
<item row="8" column="0">
|
||||||
<widget class="QLabel" name="label_9">
|
<widget class="QLabel" name="label_9">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -357,6 +335,19 @@
|
|||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="authors"/>
|
<widget class="QLineEdit" name="authors"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="series_index">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="prefix">
|
||||||
|
<string>Book </string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>9999.989999999999782</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -640,7 +631,6 @@
|
|||||||
<tabstop>series</tabstop>
|
<tabstop>series</tabstop>
|
||||||
<tabstop>tag_editor_button</tabstop>
|
<tabstop>tag_editor_button</tabstop>
|
||||||
<tabstop>remove_series_button</tabstop>
|
<tabstop>remove_series_button</tabstop>
|
||||||
<tabstop>series_index</tabstop>
|
|
||||||
<tabstop>isbn</tabstop>
|
<tabstop>isbn</tabstop>
|
||||||
<tabstop>comments</tabstop>
|
<tabstop>comments</tabstop>
|
||||||
<tabstop>fetch_metadata_button</tabstop>
|
<tabstop>fetch_metadata_button</tabstop>
|
||||||
|
@ -20,7 +20,7 @@ from calibre.gui2 import NONE, TableView, qstring_to_unicode, config, \
|
|||||||
error_dialog
|
error_dialog
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
|
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
|
||||||
from calibre.ebooks.metadata import string_to_authors
|
from calibre.ebooks.metadata import string_to_authors, fmt_sidx
|
||||||
|
|
||||||
class LibraryDelegate(QItemDelegate):
|
class LibraryDelegate(QItemDelegate):
|
||||||
COLOR = QColor("blue")
|
COLOR = QColor("blue")
|
||||||
@ -98,40 +98,38 @@ class DateDelegate(QStyledItemDelegate):
|
|||||||
qde.setCalendarPopup(True)
|
qde.setCalendarPopup(True)
|
||||||
return qde
|
return qde
|
||||||
|
|
||||||
class BooksModel(QAbstractTableModel):
|
class PubDateDelegate(QStyledItemDelegate):
|
||||||
coding = zip(
|
|
||||||
[1000,900,500,400,100,90,50,40,10,9,5,4,1],
|
|
||||||
["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
def displayText(self, val, locale):
|
||||||
|
return val.toDate().toString('MMM yyyy')
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
qde = QStyledItemDelegate.createEditor(self, parent, option, index)
|
||||||
|
qde.setDisplayFormat('MM yyyy')
|
||||||
|
qde.setMinimumDate(QDate(101,1,1))
|
||||||
|
qde.setCalendarPopup(True)
|
||||||
|
return qde
|
||||||
|
|
||||||
|
|
||||||
|
class BooksModel(QAbstractTableModel):
|
||||||
headers = {
|
headers = {
|
||||||
'title' : _("Title"),
|
'title' : _("Title"),
|
||||||
'authors' : _("Author(s)"),
|
'authors' : _("Author(s)"),
|
||||||
'size' : _("Size (MB)"),
|
'size' : _("Size (MB)"),
|
||||||
'timestamp' : _("Date"),
|
'timestamp' : _("Date"),
|
||||||
|
'pubdate' : _('Published'),
|
||||||
'rating' : _('Rating'),
|
'rating' : _('Rating'),
|
||||||
'publisher' : _("Publisher"),
|
'publisher' : _("Publisher"),
|
||||||
'tags' : _("Tags"),
|
'tags' : _("Tags"),
|
||||||
'series' : _("Series"),
|
'series' : _("Series"),
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def roman(cls, num):
|
|
||||||
if num <= 0 or num >= 4000 or int(num) != num:
|
|
||||||
return str(num)
|
|
||||||
result = []
|
|
||||||
for d, r in cls.coding:
|
|
||||||
while num >= d:
|
|
||||||
result.append(r)
|
|
||||||
num -= d
|
|
||||||
return ''.join(result)
|
|
||||||
|
|
||||||
def __init__(self, parent=None, buffer=40):
|
def __init__(self, parent=None, buffer=40):
|
||||||
QAbstractTableModel.__init__(self, parent)
|
QAbstractTableModel.__init__(self, parent)
|
||||||
self.db = None
|
self.db = None
|
||||||
self.column_map = config['column_map']
|
self.column_map = config['column_map']
|
||||||
self.editable_cols = ['title', 'authors', 'rating', 'publisher',
|
self.editable_cols = ['title', 'authors', 'rating', 'publisher',
|
||||||
'tags', 'series', 'timestamp']
|
'tags', 'series', 'timestamp', 'pubdate']
|
||||||
self.default_image = QImage(':/images/book.svg')
|
self.default_image = QImage(':/images/book.svg')
|
||||||
self.sorted_on = ('timestamp', Qt.AscendingOrder)
|
self.sorted_on = ('timestamp', Qt.AscendingOrder)
|
||||||
self.last_search = '' # The last search performed on this model
|
self.last_search = '' # The last search performed on this model
|
||||||
@ -157,8 +155,12 @@ class BooksModel(QAbstractTableModel):
|
|||||||
tidx = self.column_map.index('timestamp')
|
tidx = self.column_map.index('timestamp')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
tidx = -1
|
tidx = -1
|
||||||
|
try:
|
||||||
|
pidx = self.column_map.index('pubdate')
|
||||||
|
except ValueError:
|
||||||
|
pidx = -1
|
||||||
|
|
||||||
self.emit(SIGNAL('columns_sorted(int,int)'), idx, tidx)
|
self.emit(SIGNAL('columns_sorted(int,int,int)'), idx, tidx, pidx)
|
||||||
|
|
||||||
|
|
||||||
def set_database(self, db):
|
def set_database(self, db):
|
||||||
@ -186,8 +188,8 @@ class BooksModel(QAbstractTableModel):
|
|||||||
self.db = None
|
self.db = None
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=False):
|
def add_books(self, paths, formats, metadata, add_duplicates=False):
|
||||||
ret = self.db.add_books(paths, formats, metadata, uris,
|
ret = self.db.add_books(paths, formats, metadata,
|
||||||
add_duplicates=add_duplicates)
|
add_duplicates=add_duplicates)
|
||||||
self.count_changed()
|
self.count_changed()
|
||||||
return ret
|
return ret
|
||||||
@ -313,7 +315,7 @@ class BooksModel(QAbstractTableModel):
|
|||||||
series = self.db.series(idx)
|
series = self.db.series(idx)
|
||||||
if series:
|
if series:
|
||||||
sidx = self.db.series_index(idx)
|
sidx = self.db.series_index(idx)
|
||||||
sidx = self.__class__.roman(sidx) if self.use_roman_numbers else str(sidx)
|
sidx = fmt_sidx(sidx, use_roman = self.use_roman_numbers)
|
||||||
data[_('Series')] = _('Book <font face="serif">%s</font> of %s.')%(sidx, series)
|
data[_('Series')] = _('Book <font face="serif">%s</font> of %s.')%(sidx, series)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@ -492,6 +494,7 @@ class BooksModel(QAbstractTableModel):
|
|||||||
ridx = FIELD_MAP['rating']
|
ridx = FIELD_MAP['rating']
|
||||||
pidx = FIELD_MAP['publisher']
|
pidx = FIELD_MAP['publisher']
|
||||||
tmdx = FIELD_MAP['timestamp']
|
tmdx = FIELD_MAP['timestamp']
|
||||||
|
pddx = FIELD_MAP['pubdate']
|
||||||
srdx = FIELD_MAP['series']
|
srdx = FIELD_MAP['series']
|
||||||
tgdx = FIELD_MAP['tags']
|
tgdx = FIELD_MAP['tags']
|
||||||
siix = FIELD_MAP['series_index']
|
siix = FIELD_MAP['series_index']
|
||||||
@ -508,6 +511,12 @@ class BooksModel(QAbstractTableModel):
|
|||||||
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
|
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
|
||||||
return QDate(dt.year, dt.month, dt.day)
|
return QDate(dt.year, dt.month, dt.day)
|
||||||
|
|
||||||
|
def pubdate(r):
|
||||||
|
dt = self.db.data[r][pddx]
|
||||||
|
if dt:
|
||||||
|
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
|
||||||
|
return QDate(dt.year, dt.month, dt.day)
|
||||||
|
|
||||||
def rating(r):
|
def rating(r):
|
||||||
r = self.db.data[r][ridx]
|
r = self.db.data[r][ridx]
|
||||||
r = r/2 if r else 0
|
r = r/2 if r else 0
|
||||||
@ -526,8 +535,8 @@ class BooksModel(QAbstractTableModel):
|
|||||||
def series(r):
|
def series(r):
|
||||||
series = self.db.data[r][srdx]
|
series = self.db.data[r][srdx]
|
||||||
if series:
|
if series:
|
||||||
return series + ' [%d]'%self.db.data[r][siix]
|
idx = fmt_sidx(self.db.data[r][siix])
|
||||||
|
return series + ' [%s]'%idx
|
||||||
def size(r):
|
def size(r):
|
||||||
size = self.db.data[r][sidx]
|
size = self.db.data[r][sidx]
|
||||||
if size:
|
if size:
|
||||||
@ -538,6 +547,7 @@ class BooksModel(QAbstractTableModel):
|
|||||||
'authors' : authors,
|
'authors' : authors,
|
||||||
'size' : size,
|
'size' : size,
|
||||||
'timestamp': timestamp,
|
'timestamp': timestamp,
|
||||||
|
'pubdate' : pubdate,
|
||||||
'rating' : rating,
|
'rating' : rating,
|
||||||
'publisher': publisher,
|
'publisher': publisher,
|
||||||
'tags' : tags,
|
'tags' : tags,
|
||||||
@ -577,7 +587,7 @@ class BooksModel(QAbstractTableModel):
|
|||||||
if column not in self.editable_cols:
|
if column not in self.editable_cols:
|
||||||
return False
|
return False
|
||||||
val = int(value.toInt()[0]) if column == 'rating' else \
|
val = int(value.toInt()[0]) if column == 'rating' else \
|
||||||
value.toDate() if column == 'timestamp' else \
|
value.toDate() if column in ('timestamp', 'pubdate') else \
|
||||||
unicode(value.toString())
|
unicode(value.toString())
|
||||||
id = self.db.id(row)
|
id = self.db.id(row)
|
||||||
if column == 'rating':
|
if column == 'rating':
|
||||||
@ -585,10 +595,10 @@ class BooksModel(QAbstractTableModel):
|
|||||||
val *= 2
|
val *= 2
|
||||||
self.db.set_rating(id, val)
|
self.db.set_rating(id, val)
|
||||||
elif column == 'series':
|
elif column == 'series':
|
||||||
pat = re.compile(r'\[(\d+)\]')
|
pat = re.compile(r'\[([.0-9]+)\]')
|
||||||
match = pat.search(val)
|
match = pat.search(val)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
self.db.set_series_index(id, int(match.group(1)))
|
self.db.set_series_index(id, float(match.group(1)))
|
||||||
val = pat.sub('', val)
|
val = pat.sub('', val)
|
||||||
val = val.strip()
|
val = val.strip()
|
||||||
if val:
|
if val:
|
||||||
@ -598,6 +608,11 @@ class BooksModel(QAbstractTableModel):
|
|||||||
return False
|
return False
|
||||||
dt = datetime(val.year(), val.month(), val.day()) + timedelta(seconds=time.timezone) - timedelta(hours=time.daylight)
|
dt = datetime(val.year(), val.month(), val.day()) + timedelta(seconds=time.timezone) - timedelta(hours=time.daylight)
|
||||||
self.db.set_timestamp(id, dt)
|
self.db.set_timestamp(id, dt)
|
||||||
|
elif column == 'pubdate':
|
||||||
|
if val.isNull() or not val.isValid():
|
||||||
|
return False
|
||||||
|
dt = datetime(val.year(), val.month(), val.day()) + timedelta(seconds=time.timezone) - timedelta(hours=time.daylight)
|
||||||
|
self.db.set_pubdate(id, dt)
|
||||||
else:
|
else:
|
||||||
self.db.set(row, column, val)
|
self.db.set(row, column, val)
|
||||||
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
|
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
|
||||||
@ -625,29 +640,35 @@ class BooksView(TableView):
|
|||||||
TableView.__init__(self, parent)
|
TableView.__init__(self, parent)
|
||||||
self.rating_delegate = LibraryDelegate(self)
|
self.rating_delegate = LibraryDelegate(self)
|
||||||
self.timestamp_delegate = DateDelegate(self)
|
self.timestamp_delegate = DateDelegate(self)
|
||||||
|
self.pubdate_delegate = PubDateDelegate(self)
|
||||||
self.display_parent = parent
|
self.display_parent = parent
|
||||||
self._model = modelcls(self)
|
self._model = modelcls(self)
|
||||||
self.setModel(self._model)
|
self.setModel(self._model)
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
try:
|
try:
|
||||||
self.columns_sorted(self._model.column_map.index('rating'),
|
cm = self._model.column_map
|
||||||
self._model.column_map.index('timestamp'))
|
self.columns_sorted(cm.index('rating') if 'rating' in cm else -1,
|
||||||
|
cm.index('timestamp') if 'timestamp' in cm else -1,
|
||||||
|
cm.index('pubdate') if 'pubdate' in cm else -1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
QObject.connect(self.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
QObject.connect(self.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
||||||
self._model.current_changed)
|
self._model.current_changed)
|
||||||
self.connect(self._model, SIGNAL('columns_sorted(int, int)'), self.columns_sorted, Qt.QueuedConnection)
|
self.connect(self._model, SIGNAL('columns_sorted(int,int,int)'),
|
||||||
|
self.columns_sorted, Qt.QueuedConnection)
|
||||||
|
|
||||||
def columns_sorted(self, rating_col, timestamp_col):
|
def columns_sorted(self, rating_col, timestamp_col, pubdate_col):
|
||||||
for i in range(self.model().columnCount(None)):
|
for i in range(self.model().columnCount(None)):
|
||||||
if self.itemDelegateForColumn(i) in (self.rating_delegate,
|
if self.itemDelegateForColumn(i) in (self.rating_delegate,
|
||||||
self.timestamp_delegate):
|
self.timestamp_delegate, self.pubdate_delegate):
|
||||||
self.setItemDelegateForColumn(i, self.itemDelegate())
|
self.setItemDelegateForColumn(i, self.itemDelegate())
|
||||||
if rating_col > -1:
|
if rating_col > -1:
|
||||||
self.setItemDelegateForColumn(rating_col, self.rating_delegate)
|
self.setItemDelegateForColumn(rating_col, self.rating_delegate)
|
||||||
if timestamp_col > -1:
|
if timestamp_col > -1:
|
||||||
self.setItemDelegateForColumn(timestamp_col, self.timestamp_delegate)
|
self.setItemDelegateForColumn(timestamp_col, self.timestamp_delegate)
|
||||||
|
if pubdate_col > -1:
|
||||||
|
self.setItemDelegateForColumn(pubdate_col, self.pubdate_delegate)
|
||||||
|
|
||||||
def set_context_menu(self, edit_metadata, send_to_device, convert, view,
|
def set_context_menu(self, edit_metadata, send_to_device, convert, view,
|
||||||
save, open_folder, book_details, similar_menu=None):
|
save, open_folder, book_details, similar_menu=None):
|
||||||
|
@ -991,9 +991,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
|||||||
else:
|
else:
|
||||||
ans = self.conn.get('SELECT series_index FROM books WHERE id=?', (index,), all=False)
|
ans = self.conn.get('SELECT series_index FROM books WHERE id=?', (index,), all=False)
|
||||||
try:
|
try:
|
||||||
return int(ans)
|
return float(ans)
|
||||||
except:
|
except:
|
||||||
return 1
|
return 1.0
|
||||||
|
|
||||||
def books_in_series(self, series_id):
|
def books_in_series(self, series_id):
|
||||||
'''
|
'''
|
||||||
|
@ -50,7 +50,8 @@ copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
|||||||
|
|
||||||
FIELD_MAP = {'id':0, 'title':1, 'authors':2, 'publisher':3, 'rating':4, 'timestamp':5,
|
FIELD_MAP = {'id':0, 'title':1, 'authors':2, 'publisher':3, 'rating':4, 'timestamp':5,
|
||||||
'size':6, 'tags':7, 'comments':8, 'series':9, 'series_index':10,
|
'size':6, 'tags':7, 'comments':8, 'series':9, 'series_index':10,
|
||||||
'sort':11, 'author_sort':12, 'formats':13, 'isbn':14, 'path':15}
|
'sort':11, 'author_sort':12, 'formats':13, 'isbn':14, 'path':15,
|
||||||
|
'lccn':16, 'pubdate':17, 'flags':18}
|
||||||
INDEX_MAP = dict(zip(FIELD_MAP.values(), FIELD_MAP.keys()))
|
INDEX_MAP = dict(zip(FIELD_MAP.values(), FIELD_MAP.keys()))
|
||||||
|
|
||||||
|
|
||||||
@ -472,6 +473,53 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
FROM books;
|
FROM books;
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
def upgrade_version_4(self):
|
||||||
|
'Rationalize books table'
|
||||||
|
self.conn.executescript('''
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
CREATE TEMPORARY TABLE
|
||||||
|
books_backup(id,title,sort,timestamp,series_index,author_sort,isbn,path);
|
||||||
|
INSERT INTO books_backup SELECT id,title,sort,timestamp,series_index,author_sort,isbn,path FROM books;
|
||||||
|
DROP TABLE books;
|
||||||
|
CREATE TABLE books ( id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
title TEXT NOT NULL DEFAULT 'Unknown' COLLATE NOCASE,
|
||||||
|
sort TEXT COLLATE NOCASE,
|
||||||
|
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
pubdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
series_index REAL NOT NULL DEFAULT 1.0,
|
||||||
|
author_sort TEXT COLLATE NOCASE,
|
||||||
|
isbn TEXT DEFAULT "" COLLATE NOCASE,
|
||||||
|
lccn TEXT DEFAULT "" COLLATE NOCASE,
|
||||||
|
path TEXT NOT NULL DEFAULT "",
|
||||||
|
flags INTEGER NOT NULL DEFAULT 1
|
||||||
|
);
|
||||||
|
INSERT INTO
|
||||||
|
books (id,title,sort,timestamp,pubdate,series_index,author_sort,isbn,path)
|
||||||
|
SELECT id,title,sort,timestamp,timestamp,series_index,author_sort,isbn,path FROM books_backup;
|
||||||
|
DROP TABLE books_backup;
|
||||||
|
|
||||||
|
DROP VIEW meta;
|
||||||
|
CREATE VIEW meta AS
|
||||||
|
SELECT id, title,
|
||||||
|
(SELECT concat(name) FROM authors WHERE authors.id IN (SELECT author from books_authors_link WHERE book=books.id)) authors,
|
||||||
|
(SELECT name FROM publishers WHERE publishers.id IN (SELECT publisher from books_publishers_link WHERE book=books.id)) publisher,
|
||||||
|
(SELECT rating FROM ratings WHERE ratings.id IN (SELECT rating from books_ratings_link WHERE book=books.id)) rating,
|
||||||
|
timestamp,
|
||||||
|
(SELECT MAX(uncompressed_size) FROM data WHERE book=books.id) size,
|
||||||
|
(SELECT concat(name) FROM tags WHERE tags.id IN (SELECT tag from books_tags_link WHERE book=books.id)) tags,
|
||||||
|
(SELECT text FROM comments WHERE book=books.id) comments,
|
||||||
|
(SELECT name FROM series WHERE series.id IN (SELECT series FROM books_series_link WHERE book=books.id)) series,
|
||||||
|
series_index,
|
||||||
|
sort,
|
||||||
|
author_sort,
|
||||||
|
(SELECT concat(format) FROM data WHERE data.book=books.id) formats,
|
||||||
|
isbn,
|
||||||
|
path,
|
||||||
|
lccn,
|
||||||
|
pubdate,
|
||||||
|
flags
|
||||||
|
FROM books;
|
||||||
|
''')
|
||||||
|
|
||||||
def last_modified(self):
|
def last_modified(self):
|
||||||
''' Return last modified time as a UTC datetime object'''
|
''' Return last modified time as a UTC datetime object'''
|
||||||
@ -610,6 +658,16 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
return img
|
return img
|
||||||
return f if as_file else f.read()
|
return f if as_file else f.read()
|
||||||
|
|
||||||
|
def timestamp(self, index, index_is_id=False):
|
||||||
|
if index_is_id:
|
||||||
|
return self.conn.get('SELECT timestamp FROM meta WHERE id=?', (index,), all=False)
|
||||||
|
return self.data[index][FIELD_MAP['timestamp']]
|
||||||
|
|
||||||
|
def pubdate(self, index, index_is_id=False):
|
||||||
|
if index_is_id:
|
||||||
|
return self.conn.get('SELECT pubdate FROM meta WHERE id=?', (index,), all=False)
|
||||||
|
return self.data[index][FIELD_MAP['pubdate']]
|
||||||
|
|
||||||
def get_metadata(self, idx, index_is_id=False, get_cover=False):
|
def get_metadata(self, idx, index_is_id=False, get_cover=False):
|
||||||
'''
|
'''
|
||||||
Convenience method to return metadata as a L{MetaInformation} object.
|
Convenience method to return metadata as a L{MetaInformation} object.
|
||||||
@ -621,6 +679,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
mi.comments = self.comments(idx, index_is_id=index_is_id)
|
mi.comments = self.comments(idx, index_is_id=index_is_id)
|
||||||
mi.publisher = self.publisher(idx, index_is_id=index_is_id)
|
mi.publisher = self.publisher(idx, index_is_id=index_is_id)
|
||||||
mi.timestamp = self.timestamp(idx, index_is_id=index_is_id)
|
mi.timestamp = self.timestamp(idx, index_is_id=index_is_id)
|
||||||
|
mi.pubdate = self.pubdate(idx, index_is_id=index_is_id)
|
||||||
tags = self.tags(idx, index_is_id=index_is_id)
|
tags = self.tags(idx, index_is_id=index_is_id)
|
||||||
if tags:
|
if tags:
|
||||||
mi.tags = [i.strip() for i in tags.split(',')]
|
mi.tags = [i.strip() for i in tags.split(',')]
|
||||||
@ -917,7 +976,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
self.set_comment(id, mi.comments, notify=False)
|
self.set_comment(id, mi.comments, notify=False)
|
||||||
if mi.isbn and mi.isbn.strip():
|
if mi.isbn and mi.isbn.strip():
|
||||||
self.set_isbn(id, mi.isbn, notify=False)
|
self.set_isbn(id, mi.isbn, notify=False)
|
||||||
if mi.series_index and mi.series_index > 0:
|
if mi.series_index:
|
||||||
self.set_series_index(id, mi.series_index, notify=False)
|
self.set_series_index(id, mi.series_index, notify=False)
|
||||||
if getattr(mi, 'timestamp', None) is not None:
|
if getattr(mi, 'timestamp', None) is not None:
|
||||||
self.set_timestamp(id, mi.timestamp, notify=False)
|
self.set_timestamp(id, mi.timestamp, notify=False)
|
||||||
@ -983,6 +1042,15 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
if notify:
|
if notify:
|
||||||
self.notify('metadata', [id])
|
self.notify('metadata', [id])
|
||||||
|
|
||||||
|
def set_pubdate(self, id, dt, notify=True):
|
||||||
|
if dt:
|
||||||
|
self.conn.execute('UPDATE books SET pubdate=? WHERE id=?', (dt, id))
|
||||||
|
self.data.set(id, FIELD_MAP['pubdate'], dt, row_is_id=True)
|
||||||
|
self.conn.commit()
|
||||||
|
if notify:
|
||||||
|
self.notify('metadata', [id])
|
||||||
|
|
||||||
|
|
||||||
def set_publisher(self, id, publisher, notify=True):
|
def set_publisher(self, id, publisher, notify=True):
|
||||||
self.conn.execute('DELETE FROM books_publishers_link WHERE book=?',(id,))
|
self.conn.execute('DELETE FROM books_publishers_link WHERE book=?',(id,))
|
||||||
self.conn.execute('DELETE FROM publishers WHERE (SELECT COUNT(id) FROM books_publishers_link WHERE publisher=publishers.id) < 1')
|
self.conn.execute('DELETE FROM publishers WHERE (SELECT COUNT(id) FROM books_publishers_link WHERE publisher=publishers.id) < 1')
|
||||||
@ -1103,17 +1171,11 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
|
|
||||||
def set_series_index(self, id, idx, notify=True):
|
def set_series_index(self, id, idx, notify=True):
|
||||||
if idx is None:
|
if idx is None:
|
||||||
idx = 1
|
idx = 1.0
|
||||||
idx = int(idx)
|
idx = float(idx)
|
||||||
self.conn.execute('UPDATE books SET series_index=? WHERE id=?', (int(idx), id))
|
self.conn.execute('UPDATE books SET series_index=? WHERE id=?', (idx, id))
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
try:
|
self.data.set(id, FIELD_MAP['series_index'], idx, row_is_id=True)
|
||||||
row = self.row(id)
|
|
||||||
if row is not None:
|
|
||||||
self.data.set(row, 10, idx)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
self.data.set(id, FIELD_MAP['series_index'], int(idx), row_is_id=True)
|
|
||||||
if notify:
|
if notify:
|
||||||
self.notify('metadata', [id])
|
self.notify('metadata', [id])
|
||||||
|
|
||||||
@ -1156,7 +1218,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
mi = get_metadata(stream, format, use_libprs_metadata=False)
|
mi = get_metadata(stream, format, use_libprs_metadata=False)
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
mi.series_index = 1
|
mi.series_index = 1.0
|
||||||
mi.tags = [_('News'), recipe.title]
|
mi.tags = [_('News'), recipe.title]
|
||||||
obj = self.conn.execute('INSERT INTO books(title, author_sort) VALUES (?, ?)',
|
obj = self.conn.execute('INSERT INTO books(title, author_sort) VALUES (?, ?)',
|
||||||
(mi.title, mi.authors[0]))
|
(mi.title, mi.authors[0]))
|
||||||
@ -1188,7 +1250,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
def create_book_entry(self, mi, cover=None, add_duplicates=True):
|
def create_book_entry(self, mi, cover=None, add_duplicates=True):
|
||||||
if not add_duplicates and self.has_book(mi):
|
if not add_duplicates and self.has_book(mi):
|
||||||
return None
|
return None
|
||||||
series_index = 1 if mi.series_index is None else mi.series_index
|
series_index = 1.0 if mi.series_index is None else mi.series_index
|
||||||
aus = mi.author_sort if mi.author_sort else ', '.join(mi.authors)
|
aus = mi.author_sort if mi.author_sort else ', '.join(mi.authors)
|
||||||
title = mi.title
|
title = mi.title
|
||||||
if isinstance(aus, str):
|
if isinstance(aus, str):
|
||||||
@ -1207,33 +1269,29 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
return id
|
return id
|
||||||
|
|
||||||
|
|
||||||
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=True):
|
def add_books(self, paths, formats, metadata, add_duplicates=True):
|
||||||
'''
|
'''
|
||||||
Add a book to the database. The result cache is not updated.
|
Add a book to the database. The result cache is not updated.
|
||||||
:param:`paths` List of paths to book files or file-like objects
|
:param:`paths` List of paths to book files or file-like objects
|
||||||
'''
|
'''
|
||||||
formats, metadata, uris = iter(formats), iter(metadata), iter(uris)
|
formats, metadata = iter(formats), iter(metadata)
|
||||||
duplicates = []
|
duplicates = []
|
||||||
ids = []
|
ids = []
|
||||||
for path in paths:
|
for path in paths:
|
||||||
mi = metadata.next()
|
mi = metadata.next()
|
||||||
format = formats.next()
|
format = formats.next()
|
||||||
try:
|
|
||||||
uri = uris.next()
|
|
||||||
except StopIteration:
|
|
||||||
uri = None
|
|
||||||
if not add_duplicates and self.has_book(mi):
|
if not add_duplicates and self.has_book(mi):
|
||||||
duplicates.append((path, format, mi, uri))
|
duplicates.append((path, format, mi))
|
||||||
continue
|
continue
|
||||||
series_index = 1 if mi.series_index is None else mi.series_index
|
series_index = 1.0 if mi.series_index is None else mi.series_index
|
||||||
aus = mi.author_sort if mi.author_sort else ', '.join(mi.authors)
|
aus = mi.author_sort if mi.author_sort else ', '.join(mi.authors)
|
||||||
title = mi.title
|
title = mi.title
|
||||||
if isinstance(aus, str):
|
if isinstance(aus, str):
|
||||||
aus = aus.decode(preferred_encoding, 'replace')
|
aus = aus.decode(preferred_encoding, 'replace')
|
||||||
if isinstance(title, str):
|
if isinstance(title, str):
|
||||||
title = title.decode(preferred_encoding)
|
title = title.decode(preferred_encoding)
|
||||||
obj = self.conn.execute('INSERT INTO books(title, uri, series_index, author_sort) VALUES (?, ?, ?, ?)',
|
obj = self.conn.execute('INSERT INTO books(title, series_index, author_sort) VALUES (?, ?, ?)',
|
||||||
(title, uri, series_index, aus))
|
(title, series_index, aus))
|
||||||
id = obj.lastrowid
|
id = obj.lastrowid
|
||||||
self.data.books_added([id], self.conn)
|
self.data.books_added([id], self.conn)
|
||||||
ids.append(id)
|
ids.append(id)
|
||||||
@ -1251,12 +1309,11 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
paths = list(duplicate[0] for duplicate in duplicates)
|
paths = list(duplicate[0] for duplicate in duplicates)
|
||||||
formats = list(duplicate[1] for duplicate in duplicates)
|
formats = list(duplicate[1] for duplicate in duplicates)
|
||||||
metadata = list(duplicate[2] for duplicate in duplicates)
|
metadata = list(duplicate[2] for duplicate in duplicates)
|
||||||
uris = list(duplicate[3] for duplicate in duplicates)
|
return (paths, formats, metadata), len(ids)
|
||||||
return (paths, formats, metadata, uris), len(ids)
|
|
||||||
return None, len(ids)
|
return None, len(ids)
|
||||||
|
|
||||||
def import_book(self, mi, formats, notify=True):
|
def import_book(self, mi, formats, notify=True):
|
||||||
series_index = 1 if mi.series_index is None else mi.series_index
|
series_index = 1.0 if mi.series_index is None else mi.series_index
|
||||||
if not mi.title:
|
if not mi.title:
|
||||||
mi.title = _('Unknown')
|
mi.title = _('Unknown')
|
||||||
if not mi.authors:
|
if not mi.authors:
|
||||||
@ -1266,8 +1323,8 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
aus = aus.decode(preferred_encoding, 'replace')
|
aus = aus.decode(preferred_encoding, 'replace')
|
||||||
title = mi.title if isinstance(mi.title, unicode) else \
|
title = mi.title if isinstance(mi.title, unicode) else \
|
||||||
mi.title.decode(preferred_encoding, 'replace')
|
mi.title.decode(preferred_encoding, 'replace')
|
||||||
obj = self.conn.execute('INSERT INTO books(title, uri, series_index, author_sort) VALUES (?, ?, ?, ?)',
|
obj = self.conn.execute('INSERT INTO books(title, series_index, author_sort) VALUES (?, ?, ?)',
|
||||||
(title, None, series_index, aus))
|
(title, series_index, aus))
|
||||||
id = obj.lastrowid
|
id = obj.lastrowid
|
||||||
self.data.books_added([id], self.conn)
|
self.data.books_added([id], self.conn)
|
||||||
self.set_path(id, True)
|
self.set_path(id, True)
|
||||||
@ -1368,12 +1425,12 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
QCoreApplication.processEvents()
|
QCoreApplication.processEvents()
|
||||||
db.conn.row_factory = lambda cursor, row : tuple(row)
|
db.conn.row_factory = lambda cursor, row : tuple(row)
|
||||||
db.conn.text_factory = lambda x : unicode(x, 'utf-8', 'replace')
|
db.conn.text_factory = lambda x : unicode(x, 'utf-8', 'replace')
|
||||||
books = db.conn.get('SELECT id, title, sort, timestamp, uri, series_index, author_sort, isbn FROM books ORDER BY id ASC')
|
books = db.conn.get('SELECT id, title, sort, timestamp, series_index, author_sort, isbn FROM books ORDER BY id ASC')
|
||||||
progress.setAutoReset(False)
|
progress.setAutoReset(False)
|
||||||
progress.setRange(0, len(books))
|
progress.setRange(0, len(books))
|
||||||
|
|
||||||
for book in books:
|
for book in books:
|
||||||
self.conn.execute('INSERT INTO books(id, title, sort, timestamp, uri, series_index, author_sort, isbn) VALUES(?, ?, ?, ?, ?, ?, ?, ?);', book)
|
self.conn.execute('INSERT INTO books(id, title, sort, timestamp, series_index, author_sort, isbn) VALUES(?, ?, ?, ?, ?, ?, ?, ?);', book)
|
||||||
|
|
||||||
tables = '''
|
tables = '''
|
||||||
authors ratings tags series books_tags_link
|
authors ratings tags series books_tags_link
|
||||||
|
@ -25,6 +25,7 @@ from calibre.library.database2 import LibraryDatabase2, FIELD_MAP
|
|||||||
from calibre.utils.config import config_dir
|
from calibre.utils.config import config_dir
|
||||||
from calibre.utils.mdns import publish as publish_zeroconf, \
|
from calibre.utils.mdns import publish as publish_zeroconf, \
|
||||||
stop_server as stop_zeroconf
|
stop_server as stop_zeroconf
|
||||||
|
from calibre.ebooks.metadata import fmt_sidx
|
||||||
|
|
||||||
build_time = datetime.strptime(build_time, '%d %m %Y %H%M%S')
|
build_time = datetime.strptime(build_time, '%d %m %Y %H%M%S')
|
||||||
server_resources['jquery.js'] = jquery
|
server_resources['jquery.js'] = jquery
|
||||||
@ -271,7 +272,7 @@ class LibraryServer(object):
|
|||||||
|
|
||||||
@expose
|
@expose
|
||||||
def stanza(self):
|
def stanza(self):
|
||||||
' Feeds to read calibre books on a ipod with stanza.'
|
'Feeds to read calibre books on a ipod with stanza.'
|
||||||
books = []
|
books = []
|
||||||
for record in iter(self.db):
|
for record in iter(self.db):
|
||||||
r = record[FIELD_MAP['formats']]
|
r = record[FIELD_MAP['formats']]
|
||||||
@ -289,8 +290,8 @@ class LibraryServer(object):
|
|||||||
extra.append('TAGS: %s<br />'%', '.join(tags.split(',')))
|
extra.append('TAGS: %s<br />'%', '.join(tags.split(',')))
|
||||||
series = record[FIELD_MAP['series']]
|
series = record[FIELD_MAP['series']]
|
||||||
if series:
|
if series:
|
||||||
extra.append('SERIES: %s [%d]<br />'%(series,
|
extra.append('SERIES: %s [%s]<br />'%(series,
|
||||||
record[FIELD_MAP['series_index']]))
|
fmt_sidx(record[FIELD_MAP['series_index']])))
|
||||||
fmt = 'epub' if 'EPUB' in r else 'pdb'
|
fmt = 'epub' if 'EPUB' in r else 'pdb'
|
||||||
mimetype = guess_type('dummy.'+fmt)[0]
|
mimetype = guess_type('dummy.'+fmt)[0]
|
||||||
books.append(self.STANZA_ENTRY.generate(
|
books.append(self.STANZA_ENTRY.generate(
|
||||||
@ -339,6 +340,7 @@ class LibraryServer(object):
|
|||||||
for record in items[start:start+num]:
|
for record in items[start:start+num]:
|
||||||
aus = record[2] if record[2] else __builtins__._('Unknown')
|
aus = record[2] if record[2] else __builtins__._('Unknown')
|
||||||
authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
|
authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
|
||||||
|
r[10] = fmt_sidx(r[10])
|
||||||
books.append(book.generate(r=record, authors=authors).render('xml').decode('utf-8'))
|
books.append(book.generate(r=record, authors=authors).render('xml').decode('utf-8'))
|
||||||
updated = self.db.last_modified()
|
updated = self.db.last_modified()
|
||||||
|
|
||||||
|
1
todo
1
todo
@ -2,6 +2,7 @@
|
|||||||
* Refactor web.fetch.simple to use per connection timeouts via the timeout kwarg for mechanize.open
|
* Refactor web.fetch.simple to use per connection timeouts via the timeout kwarg for mechanize.open
|
||||||
|
|
||||||
* Rationalize books table. Add a pubdate column, remove the uri column (and associated support in add_books) and convert series_index to a float.
|
* Rationalize books table. Add a pubdate column, remove the uri column (and associated support in add_books) and convert series_index to a float.
|
||||||
|
- test adding/recusrsize adding and adding of duplicates
|
||||||
|
|
||||||
* Testing framework
|
* Testing framework
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user