mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add an option to remove all tags from selected books in the bulk metadata editor. Add a tweak to control how the dates in the Date column are formatted. SONY driver: Use the tz field (available in newer readers) to set timestamps correctly, when available.
This commit is contained in:
commit
d23d99017d
@ -44,7 +44,7 @@ bool_custom_columns_are_tristate = 'yes'
|
|||||||
# title within authors.
|
# title within authors.
|
||||||
sort_columns_at_startup = None
|
sort_columns_at_startup = None
|
||||||
|
|
||||||
# Format to be used for publication date
|
# Format to be used for publication date and the timestamp (date).
|
||||||
# A string controlling how the publication date is displayed in the GUI
|
# A string controlling how the publication date is displayed in the GUI
|
||||||
# d the day as number without a leading zero (1 to 31)
|
# d the day as number without a leading zero (1 to 31)
|
||||||
# dd the day as number with a leading zero (01 to 31)
|
# dd the day as number with a leading zero (01 to 31)
|
||||||
@ -59,8 +59,10 @@ sort_columns_at_startup = None
|
|||||||
# For example, given the date of 9 Jan 2010, the following formats show
|
# For example, given the date of 9 Jan 2010, the following formats show
|
||||||
# MMM yyyy ==> Jan 2010 yyyy ==> 2010 dd MMM yyyy ==> 09 Jan 2010
|
# MMM yyyy ==> Jan 2010 yyyy ==> 2010 dd MMM yyyy ==> 09 Jan 2010
|
||||||
# MM/yyyy ==> 01/2010 d/M/yy ==> 9/1/10 yy ==> 10
|
# MM/yyyy ==> 01/2010 d/M/yy ==> 9/1/10 yy ==> 10
|
||||||
# default if not set: MMM yyyy
|
# publication default if not set: MMM yyyy
|
||||||
|
# timestamp default if not set: dd MMM yyyy
|
||||||
gui_pubdate_display_format = 'MMM yyyy'
|
gui_pubdate_display_format = 'MMM yyyy'
|
||||||
|
gui_timestamp_display_format = 'dd MMM yyyy'
|
||||||
|
|
||||||
# Control title and series sorting in the library view.
|
# Control title and series sorting in the library view.
|
||||||
# If set to 'library_order', Leading articles such as The and A will be ignored.
|
# If set to 'library_order', Leading articles such as The and A will be ignored.
|
||||||
|
@ -354,19 +354,22 @@ class XMLCache(object):
|
|||||||
root = self.record_roots[i]
|
root = self.record_roots[i]
|
||||||
lpath_map = self.build_lpath_map(root)
|
lpath_map = self.build_lpath_map(root)
|
||||||
gtz_count = ltz_count = 0
|
gtz_count = ltz_count = 0
|
||||||
|
use_tz_var = False
|
||||||
for book in booklist:
|
for book in booklist:
|
||||||
path = os.path.join(self.prefixes[i], *(book.lpath.split('/')))
|
path = os.path.join(self.prefixes[i], *(book.lpath.split('/')))
|
||||||
record = lpath_map.get(book.lpath, None)
|
record = lpath_map.get(book.lpath, None)
|
||||||
if record is None:
|
if record is None:
|
||||||
record = self.create_text_record(root, i, book.lpath)
|
record = self.create_text_record(root, i, book.lpath)
|
||||||
(gtz_count, ltz_count) = self.update_text_record(record, book,
|
(gtz_count, ltz_count, use_tz_var) = \
|
||||||
path, i, gtz_count, ltz_count)
|
self.update_text_record(record, book, path, i,
|
||||||
|
gtz_count, ltz_count, use_tz_var)
|
||||||
# Ensure the collections in the XML database are recorded for
|
# Ensure the collections in the XML database are recorded for
|
||||||
# this book
|
# this book
|
||||||
if book.device_collections is None:
|
if book.device_collections is None:
|
||||||
book.device_collections = []
|
book.device_collections = []
|
||||||
book.device_collections = playlist_map.get(book.lpath, [])
|
book.device_collections = playlist_map.get(book.lpath, [])
|
||||||
debug_print('Timezone votes: %d GMT, %d LTZ'%(gtz_count, ltz_count))
|
debug_print('Timezone votes: %d GMT, %d LTZ, use_tz_var='%
|
||||||
|
(gtz_count, ltz_count, use_tz_var))
|
||||||
self.update_playlists(i, root, booklist, collections_attributes)
|
self.update_playlists(i, root, booklist, collections_attributes)
|
||||||
# Update the device collections because update playlist could have added
|
# Update the device collections because update playlist could have added
|
||||||
# some new ones.
|
# some new ones.
|
||||||
@ -464,21 +467,27 @@ class XMLCache(object):
|
|||||||
root.append(ans)
|
root.append(ans)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def update_text_record(self, record, book, path, bl_index, gtz_count, ltz_count):
|
def update_text_record(self, record, book, path, bl_index,
|
||||||
|
gtz_count, ltz_count, use_tz_var):
|
||||||
'''
|
'''
|
||||||
Update the Sony database from the book. This is done if the timestamp in
|
Update the Sony database from the book. This is done if the timestamp in
|
||||||
the db differs from the timestamp on the file.
|
the db differs from the timestamp on the file.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# It seems that a Sony device can sometimes know what timezone it is in,
|
# It seems that a Sony device can sometimes know what timezone it is in,
|
||||||
# and apparently converts the dates to GMT when it writes them to the
|
# and apparently converts the dates to GMT when it writes them to its
|
||||||
# db. Unfortunately, we can't tell when it does this, so we use a
|
# DB. We can detect that a device is timezone-aware because there is a
|
||||||
# horrible heuristic. First, set dates only for new books, trying to
|
# 'tz' variable in the Sony DB, which we can set to "0" to tell the
|
||||||
# avoid upsetting the sony. Use the timezone determined through the
|
# device to ignore its own timezone when comparing mtime to the date in
|
||||||
# voting described next. Second, voting: if a book is not new, compare
|
# the DB.
|
||||||
# its Sony DB date against localtime and gmtime. Count the matches. When
|
|
||||||
# we must set a date, use the one with the most matches. Use localtime
|
# Unfortunately, if there is no tz variable in the DB, then we can't
|
||||||
# if the case of a tie, and hope it is right.
|
# tell when the device applies a timezone conversion. We use a horrible
|
||||||
|
# heuristic to work around this problem. First, set dates only for new
|
||||||
|
# books, trying to avoid upsetting the sony. Second, voting: if a book
|
||||||
|
# is not new, compare its Sony DB date against localtime and gmtime.
|
||||||
|
# Count the matches. When we must set a date, use the one with the most
|
||||||
|
# matches. Use localtime if the case of a tie, and hope it is right.
|
||||||
timestamp = os.path.getmtime(path)
|
timestamp = os.path.getmtime(path)
|
||||||
rec_date = record.get('date', None)
|
rec_date = record.get('date', None)
|
||||||
|
|
||||||
@ -489,20 +498,25 @@ class XMLCache(object):
|
|||||||
return x
|
return x
|
||||||
|
|
||||||
if not getattr(book, '_new_book', False): # book is not new
|
if not getattr(book, '_new_book', False): # book is not new
|
||||||
if strftime(timestamp, zone=time.gmtime) == rec_date:
|
if record.get('tz', None) is not None:
|
||||||
gtz_count += 1
|
use_tz_var = True
|
||||||
elif strftime(timestamp, zone=time.localtime) == rec_date:
|
if strftime(timestamp, zone=time.gmtime) == rec_date:
|
||||||
ltz_count += 1
|
gtz_count += 1
|
||||||
|
elif strftime(timestamp, zone=time.localtime) == rec_date:
|
||||||
|
ltz_count += 1
|
||||||
else: # book is new. Set the time using the current votes
|
else: # book is new. Set the time using the current votes
|
||||||
if ltz_count >= gtz_count:
|
if use_tz_var:
|
||||||
tz = time.localtime
|
tz = time.localtime
|
||||||
debug_print("Using localtime TZ for new book", book.lpath)
|
record.set('tz', '0')
|
||||||
|
debug_print("Use localtime TZ and tz='0' for new book", book.lpath)
|
||||||
|
elif ltz_count >= gtz_count:
|
||||||
|
tz = time.localtime
|
||||||
|
debug_print("Use localtime TZ for new book", book.lpath)
|
||||||
else:
|
else:
|
||||||
tz = time.gmtime
|
tz = time.gmtime
|
||||||
debug_print("Using GMT TZ for new book", book.lpath)
|
debug_print("Use GMT TZ for new book", book.lpath)
|
||||||
date = strftime(timestamp, zone=tz)
|
date = strftime(timestamp, zone=tz)
|
||||||
record.set('date', clean(date))
|
record.set('date', clean(date))
|
||||||
|
|
||||||
record.set('size', clean(str(os.stat(path).st_size)))
|
record.set('size', clean(str(os.stat(path).st_size)))
|
||||||
title = book.title if book.title else _('Unknown')
|
title = book.title if book.title else _('Unknown')
|
||||||
record.set('title', clean(title))
|
record.set('title', clean(title))
|
||||||
@ -532,7 +546,7 @@ class XMLCache(object):
|
|||||||
if 'id' not in record.attrib:
|
if 'id' not in record.attrib:
|
||||||
num = self.max_id(record.getroottree().getroot())
|
num = self.max_id(record.getroottree().getroot())
|
||||||
record.set('id', str(num+1))
|
record.set('id', str(num+1))
|
||||||
return (gtz_count, ltz_count)
|
return (gtz_count, ltz_count, use_tz_var)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Writing the XML files {{{
|
# Writing the XML files {{{
|
||||||
|
@ -550,9 +550,7 @@ class BulkText(BulkBase):
|
|||||||
remove_all, adding, rtext = self.gui_val
|
remove_all, adding, rtext = self.gui_val
|
||||||
remove = set()
|
remove = set()
|
||||||
if remove_all:
|
if remove_all:
|
||||||
for book_id in book_ids:
|
remove = set(self.db.all_custom(num=self.col_id))
|
||||||
remove |= set(self.db.get_custom(book_id, num=self.col_id,
|
|
||||||
index_is_id=True))
|
|
||||||
else:
|
else:
|
||||||
txt = rtext
|
txt = rtext
|
||||||
if txt:
|
if txt:
|
||||||
|
@ -199,7 +199,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
for w in getattr(self, 'custom_column_widgets', []):
|
for w in getattr(self, 'custom_column_widgets', []):
|
||||||
w.gui_val
|
w.gui_val
|
||||||
|
|
||||||
remove = unicode(self.remove_tags.text()).strip().split(',')
|
if self.remove_all_tags.isChecked():
|
||||||
|
remove = self.db.all_tags()
|
||||||
|
else:
|
||||||
|
remove = unicode(self.remove_tags.text()).strip().split(',')
|
||||||
add = unicode(self.tags.text()).strip().split(',')
|
add = unicode(self.tags.text()).strip().split(',')
|
||||||
au = unicode(self.authors.text())
|
au = unicode(self.authors.text())
|
||||||
aus = unicode(self.author_sort.text())
|
aus = unicode(self.author_sort.text())
|
||||||
|
@ -140,7 +140,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="1" colspan="2">
|
||||||
<widget class="EnComboBox" name="publisher">
|
<widget class="EnComboBox" name="publisher">
|
||||||
<property name="editable">
|
<property name="editable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -191,14 +191,23 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1" colspan="2">
|
<item row="6" column="1">
|
||||||
<widget class="TagsLineEdit" name="remove_tags">
|
<widget class="TagsLineEdit" name="remove_tags">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Comma separated list of tags to remove from the books. </string>
|
<string>Comma separated list of tags to remove from the books. </string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0">
|
<item row="6" column="2">
|
||||||
|
<widget class="QCheckBox" name="remove_all_tags">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove all</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Check this box to remove all tags from the books.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item><item row="7" 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>
|
||||||
|
@ -98,14 +98,14 @@ class DateDelegate(QStyledItemDelegate): # {{{
|
|||||||
d = val.toDate()
|
d = val.toDate()
|
||||||
if d <= UNDEFINED_QDATE:
|
if d <= UNDEFINED_QDATE:
|
||||||
return ''
|
return ''
|
||||||
return format_date(d.toPyDate(), 'dd MMM yyyy')
|
format = tweaks['gui_timestamp_display_format']
|
||||||
|
if format is None:
|
||||||
|
format = 'dd MMM yyyy'
|
||||||
|
return format_date(d.toPyDate(), format)
|
||||||
|
|
||||||
def createEditor(self, parent, option, index):
|
def createEditor(self, parent, option, index):
|
||||||
qde = QStyledItemDelegate.createEditor(self, parent, option, index)
|
qde = QStyledItemDelegate.createEditor(self, parent, option, index)
|
||||||
stdformat = unicode(qde.displayFormat())
|
qde.setDisplayFormat('dd MMM yyyy')
|
||||||
if 'yyyy' not in stdformat:
|
|
||||||
stdformat = stdformat.replace('yy', 'yyyy')
|
|
||||||
qde.setDisplayFormat(stdformat)
|
|
||||||
qde.setMinimumDate(UNDEFINED_QDATE)
|
qde.setMinimumDate(UNDEFINED_QDATE)
|
||||||
qde.setSpecialValueText(_('Undefined'))
|
qde.setSpecialValueText(_('Undefined'))
|
||||||
qde.setCalendarPopup(True)
|
qde.setCalendarPopup(True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user