mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix #2204 ("Delete news when sent" does not work)
This commit is contained in:
parent
5254aec93e
commit
c4f554dc0e
@ -4,14 +4,13 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Provides abstraction for metadata reading.writing from a variety of ebook formats.
|
Provides abstraction for metadata reading.writing from a variety of ebook formats.
|
||||||
"""
|
"""
|
||||||
import os, mimetypes, sys
|
import os, mimetypes, sys
|
||||||
from urllib import unquote, quote
|
from urllib import unquote, quote
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
|
||||||
from calibre.constants import __version__ as VERSION
|
|
||||||
from calibre import relpath
|
from calibre import relpath
|
||||||
from calibre.utils.config import OptionParser
|
from calibre.utils.config import OptionParser
|
||||||
|
|
||||||
@ -51,17 +50,17 @@ def get_parser(extension):
|
|||||||
|
|
||||||
class Resource(object):
|
class Resource(object):
|
||||||
'''
|
'''
|
||||||
Represents a resource (usually a file on the filesystem or a URL pointing
|
Represents a resource (usually a file on the filesystem or a URL pointing
|
||||||
to the web. Such resources are commonly referred to in OPF files.
|
to the web. Such resources are commonly referred to in OPF files.
|
||||||
|
|
||||||
They have the interface:
|
They have the interface:
|
||||||
|
|
||||||
:member:`path`
|
:member:`path`
|
||||||
:member:`mime_type`
|
:member:`mime_type`
|
||||||
:method:`href`
|
:method:`href`
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, href_or_path, basedir=os.getcwd(), is_path=True):
|
def __init__(self, href_or_path, basedir=os.getcwd(), is_path=True):
|
||||||
self._href = None
|
self._href = None
|
||||||
self._basedir = basedir
|
self._basedir = basedir
|
||||||
@ -91,13 +90,13 @@ class Resource(object):
|
|||||||
pc = unquote(pc).decode('utf-8')
|
pc = unquote(pc).decode('utf-8')
|
||||||
self.path = os.path.abspath(os.path.join(basedir, pc.replace('/', os.sep)))
|
self.path = os.path.abspath(os.path.join(basedir, pc.replace('/', os.sep)))
|
||||||
self.fragment = unquote(url[-1])
|
self.fragment = unquote(url[-1])
|
||||||
|
|
||||||
|
|
||||||
def href(self, basedir=None):
|
def href(self, basedir=None):
|
||||||
'''
|
'''
|
||||||
Return a URL pointing to this resource. If it is a file on the filesystem
|
Return a URL pointing to this resource. If it is a file on the filesystem
|
||||||
the URL is relative to `basedir`.
|
the URL is relative to `basedir`.
|
||||||
|
|
||||||
`basedir`: If None, the basedir of this resource is used (see :method:`set_basedir`).
|
`basedir`: If None, the basedir of this resource is used (see :method:`set_basedir`).
|
||||||
If this resource has no basedir, then the current working directory is used as the basedir.
|
If this resource has no basedir, then the current working directory is used as the basedir.
|
||||||
'''
|
'''
|
||||||
@ -119,54 +118,54 @@ class Resource(object):
|
|||||||
if isinstance(rpath, unicode):
|
if isinstance(rpath, unicode):
|
||||||
rpath = rpath.encode('utf-8')
|
rpath = rpath.encode('utf-8')
|
||||||
return quote(rpath.replace(os.sep, '/'))+frag
|
return quote(rpath.replace(os.sep, '/'))+frag
|
||||||
|
|
||||||
def set_basedir(self, path):
|
def set_basedir(self, path):
|
||||||
self._basedir = path
|
self._basedir = path
|
||||||
|
|
||||||
def basedir(self):
|
def basedir(self):
|
||||||
return self._basedir
|
return self._basedir
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Resource(%s, %s)'%(repr(self.path), repr(self.href()))
|
return 'Resource(%s, %s)'%(repr(self.path), repr(self.href()))
|
||||||
|
|
||||||
|
|
||||||
class ResourceCollection(object):
|
class ResourceCollection(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._resources = []
|
self._resources = []
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for r in self._resources:
|
for r in self._resources:
|
||||||
yield r
|
yield r
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._resources)
|
return len(self._resources)
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
return self._resources[index]
|
return self._resources[index]
|
||||||
|
|
||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
return len(self._resources) > 0
|
return len(self._resources) > 0
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
resources = map(repr, self)
|
resources = map(repr, self)
|
||||||
return '[%s]'%', '.join(resources)
|
return '[%s]'%', '.join(resources)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
def append(self, resource):
|
def append(self, resource):
|
||||||
if not isinstance(resource, Resource):
|
if not isinstance(resource, Resource):
|
||||||
raise ValueError('Can only append objects of type Resource')
|
raise ValueError('Can only append objects of type Resource')
|
||||||
self._resources.append(resource)
|
self._resources.append(resource)
|
||||||
|
|
||||||
def remove(self, resource):
|
def remove(self, resource):
|
||||||
self._resources.remove(resource)
|
self._resources.remove(resource)
|
||||||
|
|
||||||
def replace(self, start, end, items):
|
def replace(self, start, end, items):
|
||||||
'Same as list[start:end] = items'
|
'Same as list[start:end] = items'
|
||||||
self._resources[start:end] = items
|
self._resources[start:end] = items
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_directory_contents(top, topdown=True):
|
def from_directory_contents(top, topdown=True):
|
||||||
collection = ResourceCollection()
|
collection = ResourceCollection()
|
||||||
@ -176,28 +175,28 @@ class ResourceCollection(object):
|
|||||||
res.set_basedir(top)
|
res.set_basedir(top)
|
||||||
collection.append(res)
|
collection.append(res)
|
||||||
return collection
|
return collection
|
||||||
|
|
||||||
def set_basedir(self, path):
|
def set_basedir(self, path):
|
||||||
for res in self:
|
for res in self:
|
||||||
res.set_basedir(path)
|
res.set_basedir(path)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MetaInformation(object):
|
class MetaInformation(object):
|
||||||
'''Convenient encapsulation of book metadata'''
|
'''Convenient encapsulation of book metadata'''
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def copy(mi):
|
def copy(mi):
|
||||||
ans = MetaInformation(mi.title, mi.authors)
|
ans = MetaInformation(mi.title, mi.authors)
|
||||||
for attr in ('author_sort', 'title_sort', 'comments', 'category',
|
for attr in ('author_sort', 'title_sort', 'comments', 'category',
|
||||||
'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'):
|
'book_producer', 'timestamp'):
|
||||||
if hasattr(mi, attr):
|
if hasattr(mi, attr):
|
||||||
setattr(ans, attr, getattr(mi, attr))
|
setattr(ans, attr, getattr(mi, attr))
|
||||||
|
|
||||||
def __init__(self, title, authors=[_('Unknown')]):
|
def __init__(self, title, authors=(_('Unknown'),)):
|
||||||
'''
|
'''
|
||||||
@param title: title or "Unknown" or a MetaInformation object
|
@param title: title or "Unknown" or a MetaInformation object
|
||||||
@param authors: List of strings or []
|
@param authors: List of strings or []
|
||||||
@ -208,20 +207,20 @@ class MetaInformation(object):
|
|||||||
title = mi.title
|
title = mi.title
|
||||||
authors = mi.authors
|
authors = mi.authors
|
||||||
self.title = title
|
self.title = title
|
||||||
self.author = authors # Needed for backward compatibility
|
self.author = list(authors) # Needed for backward compatibility
|
||||||
#: List of strings or []
|
#: List of strings or []
|
||||||
self.authors = authors
|
self.authors = list(authors)
|
||||||
self.tags = getattr(mi, 'tags', [])
|
self.tags = getattr(mi, 'tags', [])
|
||||||
#: mi.cover_data = (ext, data)
|
#: mi.cover_data = (ext, data)
|
||||||
self.cover_data = getattr(mi, 'cover_data', (None, None))
|
self.cover_data = getattr(mi, 'cover_data', (None, None))
|
||||||
|
|
||||||
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'
|
'book_producer', 'timestamp'
|
||||||
):
|
):
|
||||||
setattr(self, x, getattr(mi, x, None))
|
setattr(self, x, getattr(mi, x, None))
|
||||||
|
|
||||||
def smart_update(self, mi):
|
def smart_update(self, mi):
|
||||||
'''
|
'''
|
||||||
Merge the information in C{mi} into self. In case of conflicts, the information
|
Merge the information in C{mi} into self. In case of conflicts, the information
|
||||||
@ -229,26 +228,26 @@ class MetaInformation(object):
|
|||||||
'''
|
'''
|
||||||
if mi.title and mi.title != _('Unknown'):
|
if mi.title and mi.title != _('Unknown'):
|
||||||
self.title = mi.title
|
self.title = mi.title
|
||||||
|
|
||||||
if mi.authors and mi.authors[0] != _('Unknown'):
|
if mi.authors and mi.authors[0] != _('Unknown'):
|
||||||
self.authors = mi.authors
|
self.authors = mi.authors
|
||||||
|
|
||||||
for attr in ('author_sort', 'title_sort', 'category',
|
for attr in ('author_sort', 'title_sort', 'category',
|
||||||
'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'):
|
'timestamp'):
|
||||||
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:
|
||||||
setattr(self, attr, val)
|
setattr(self, attr, val)
|
||||||
|
|
||||||
self.tags += mi.tags
|
self.tags += mi.tags
|
||||||
self.tags = list(set(self.tags))
|
self.tags = list(set(self.tags))
|
||||||
|
|
||||||
if getattr(mi, 'cover_data', None) and mi.cover_data[0] is not None:
|
if getattr(mi, 'cover_data', None) and mi.cover_data[0] is not None:
|
||||||
self.cover_data = mi.cover_data
|
self.cover_data = mi.cover_data
|
||||||
|
|
||||||
my_comments = getattr(self, 'comments', '')
|
my_comments = getattr(self, 'comments', '')
|
||||||
other_comments = getattr(mi, 'comments', '')
|
other_comments = getattr(mi, 'comments', '')
|
||||||
if not my_comments:
|
if not my_comments:
|
||||||
@ -257,14 +256,14 @@ class MetaInformation(object):
|
|||||||
other_comments = ''
|
other_comments = ''
|
||||||
if len(other_comments.strip()) > len(my_comments.strip()):
|
if len(other_comments.strip()) > len(my_comments.strip()):
|
||||||
self.comments = other_comments
|
self.comments = other_comments
|
||||||
|
|
||||||
def format_series_index(self):
|
def format_series_index(self):
|
||||||
try:
|
try:
|
||||||
x = float(self.series_index)
|
x = float(self.series_index)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
x = 1.0
|
x = 1.0
|
||||||
return '%d'%x if int(x) == x else '%.2f'%x
|
return '%d'%x if int(x) == x else '%.2f'%x
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
ans = u''
|
ans = u''
|
||||||
ans += u'Title : ' + unicode(self.title) + u'\n'
|
ans += u'Title : ' + unicode(self.title) + u'\n'
|
||||||
@ -275,7 +274,7 @@ class MetaInformation(object):
|
|||||||
ans += u'Publisher: '+ unicode(self.publisher) + u'\n'
|
ans += u'Publisher: '+ unicode(self.publisher) + u'\n'
|
||||||
if getattr(self, 'book_producer', False):
|
if getattr(self, 'book_producer', False):
|
||||||
ans += u'Producer : '+ unicode(self.book_producer) + u'\n'
|
ans += u'Producer : '+ unicode(self.book_producer) + u'\n'
|
||||||
if self.category:
|
if self.category:
|
||||||
ans += u'Category : ' + unicode(self.category) + u'\n'
|
ans += u'Category : ' + unicode(self.category) + u'\n'
|
||||||
if self.comments:
|
if self.comments:
|
||||||
ans += u'Comments : ' + unicode(self.comments) + u'\n'
|
ans += u'Comments : ' + unicode(self.comments) + u'\n'
|
||||||
@ -284,13 +283,13 @@ class MetaInformation(object):
|
|||||||
if self.tags:
|
if self.tags:
|
||||||
ans += u'Tags : ' + u', '.join([unicode(t) for t in self.tags]) + '\n'
|
ans += u'Tags : ' + u', '.join([unicode(t) for t in self.tags]) + '\n'
|
||||||
if self.series:
|
if self.series:
|
||||||
ans += u'Series : '+unicode(self.series) + ' #%s\n'%self.format_series_index()
|
ans += u'Series : '+unicode(self.series) + ' #%s\n'%self.format_series_index()
|
||||||
if self.language:
|
if self.language:
|
||||||
ans += u'Language : ' + unicode(self.language) + u'\n'
|
ans += u'Language : ' + unicode(self.language) + u'\n'
|
||||||
if self.timestamp is not None:
|
if self.timestamp is not None:
|
||||||
ans += u'Timestamp : ' + self.timestamp.isoformat(' ')
|
ans += u'Timestamp : ' + self.timestamp.isoformat(' ')
|
||||||
return ans.strip()
|
return ans.strip()
|
||||||
|
|
||||||
def to_html(self):
|
def to_html(self):
|
||||||
ans = [(_('Title'), unicode(self.title))]
|
ans = [(_('Title'), unicode(self.title))]
|
||||||
ans += [(_('Author(s)'), (authors_to_string(self.authors) if self.authors else _('Unknown')))]
|
ans += [(_('Author(s)'), (authors_to_string(self.authors) if self.authors else _('Unknown')))]
|
||||||
@ -307,9 +306,9 @@ class MetaInformation(object):
|
|||||||
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)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.__unicode__().encode('utf-8')
|
return self.__unicode__().encode('utf-8')
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
return bool(self.title or self.author or self.comments or self.category)
|
return bool(self.title or self.author or self.comments or self.category)
|
||||||
|
@ -397,6 +397,9 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
self.separate_cover_flow.setChecked(config['separate_cover_flow'])
|
self.separate_cover_flow.setChecked(config['separate_cover_flow'])
|
||||||
self.setup_email_page()
|
self.setup_email_page()
|
||||||
self.category_view.setCurrentIndex(self.category_view.model().index(0))
|
self.category_view.setCurrentIndex(self.category_view.model().index(0))
|
||||||
|
self.delete_news.setEnabled(bool(self.sync_news.isChecked()))
|
||||||
|
self.connect(self.sync_news, SIGNAL('toggled(bool)'),
|
||||||
|
self.delete_news.setEnabled)
|
||||||
|
|
||||||
def setup_email_page(self):
|
def setup_email_page(self):
|
||||||
opts = smtp_prefs().parse()
|
opts = smtp_prefs().parse()
|
||||||
|
@ -371,7 +371,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="delete_news">
|
<widget class="QCheckBox" name="delete_news">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Delete news from library when it is sent to reader</string>
|
<string>&Delete news from library when it is automatically sent to reader</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user