mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Pull from Trunk
This commit is contained in:
commit
83c1b281c7
111
Changelog.yaml
111
Changelog.yaml
@ -4,6 +4,117 @@
|
||||
# for important features/bug fixes.
|
||||
# Also, each release can have new and improved recipes.
|
||||
|
||||
- version: 0.7.3
|
||||
date: 2010-06-18
|
||||
|
||||
new features:
|
||||
- title: "The Tag Browser now display an average rating for each item"
|
||||
type: major
|
||||
description: >
|
||||
"
|
||||
The icons of each individual item in the Tag Browser are now partially colored to indicate the average rating of
|
||||
all books belonging to that category. For example, the icon next to each author is partially colored based on the
|
||||
averagerating of all books by that author in your calibre library. You can also hover your mouse over the item to
|
||||
see the average rating in a tooltip. Can be turned off via Preferences->Interface
|
||||
"
|
||||
|
||||
- title: "Editable author sort for each author"
|
||||
type: major
|
||||
description: >
|
||||
"calibre has always allowed you to specify the author sort for each bookin your collection. Now you
|
||||
can also specify the way the name of each individual author should be sorted. This is used to display the list
|
||||
of authors in the Tag Browser and OPDS feeds in the Content Server"
|
||||
|
||||
- title: "When downloading metadata, also get series information from librarything.com"
|
||||
type: major
|
||||
tickets: [5148]
|
||||
|
||||
- title: "Redesign of the Book Details pane"
|
||||
type: major
|
||||
description: >
|
||||
"The Book details pane now display covers with animation. Also instead of showing the full path to the book, you now have
|
||||
clickable links to open the containing folder or individual formats. The path information is still accessible via a tooltip"
|
||||
|
||||
- title: "New User Interface layouts"
|
||||
type: major
|
||||
description: >
|
||||
"calibre now has two user interface layouts selectable from Preferences->Interface. The 'wide' layout has the book details pane on the side
|
||||
and the 'narrow' layout has it on the bottom. The default layout is now wide."
|
||||
|
||||
- title: "You can now add books directly from the device to the calibre library by right clicking on the books in the device views"
|
||||
|
||||
- title: "iPad driver: Create category from series preferentially, also handle series sorting"
|
||||
|
||||
- title: "SONY driver: Add an option to use author_sort instead of author when sending to device"
|
||||
|
||||
- title: "Hitting Enter in the search box now causes the search to be re-run"
|
||||
tickets: [5856]
|
||||
|
||||
- title: "Boox driver: Make destination directory for books customizable"
|
||||
|
||||
- title: "Add plugin to download metadata from douban.com. Disabled by default."
|
||||
|
||||
- title: "OS X/linux driver for PocketBook 301"
|
||||
|
||||
- title: "Support for the Samsung Galaxy and Sigmatek EBK52"
|
||||
|
||||
- title: "On startup do not focus the search bar. Instead you can acces the search bar easily by pressing the / key or the standard search keyboard shortcut for your operating system"
|
||||
|
||||
bug fixes:
|
||||
- title: "iPad driver: Various bug fixes"
|
||||
|
||||
- title: "Kobo Output profile: Adjust the screen dimensions when converting comics"
|
||||
|
||||
- title: "Fix using Preferences when a device is connected causes items in device menu to be disabled"
|
||||
|
||||
- title: "CHM Input: Skip files whoose names are too long for windows"
|
||||
|
||||
- title: "Brighten up calibre icon on dark backgrounds"
|
||||
|
||||
- title: "Ignore 'Unknown' in title/autors when downloading metadata"
|
||||
tickets: [5633]
|
||||
|
||||
- title: "Fix regression that broke various entries in the menus - Preferences, Open containing folder and Edit metadata individually"
|
||||
|
||||
- title: "EPUB metadata: Handle comma separated entries in <dc:subject> tags correctly"
|
||||
tickets: [5855]
|
||||
|
||||
- title: "MOBI Output: Fix underlines not being rendered"
|
||||
tickets: [5830]
|
||||
|
||||
- title: "EPUB Output: Remove workaround for old versions of Adobe Digital Editions' faulty rendering of links in html. calibre no longer forces links to be blue and underlined"
|
||||
|
||||
- title: "Fix a bug that could cause the show pane buttons to not show hidden panes"
|
||||
|
||||
- title: "Fix Tag Editor does not reflect recently changed data in Tag Catagory Text Box"
|
||||
tickets: [5809]
|
||||
|
||||
- title: "Content server: Fix sorting of books by authors instead of author_sort in the main and mobile views"
|
||||
|
||||
- title: "Cover cache: Resize covers larger than 600x800 in the cover cache to reduce memory consumption in the GUI"
|
||||
|
||||
- title: "EPUB Output: Default cover is generated is now generated as a JPEG instead of PNG32, reducing size by an order of magnitude."
|
||||
tickets: [5810]
|
||||
|
||||
- title: "Cover Browser: Scale text size with height of cover browser. Only show a reflection of half the cover. Also restore rendering quality after regression in 0.7.1"
|
||||
tickets: [5808]
|
||||
|
||||
- title: "Book list: Do not let the default layout have any column wider than 350 pixels"
|
||||
|
||||
new recipes:
|
||||
- title: Akter
|
||||
author: Darko Miletic
|
||||
|
||||
- title: Thai Rath and The Nation (Thailand)
|
||||
author: Anat Ruangrassamee
|
||||
|
||||
improved recipes:
|
||||
- Wall Street Journal
|
||||
- New York Times
|
||||
- Slashdot
|
||||
- Publico
|
||||
- Danas
|
||||
|
||||
- version: 0.7.2
|
||||
date: 2010-06-11
|
||||
|
||||
|
@ -17,6 +17,7 @@ class NYTimes(BasicNewsRecipe):
|
||||
title = 'New York Times Top Stories'
|
||||
__author__ = 'GRiker'
|
||||
language = 'en'
|
||||
requires_version = (0, 7, 3)
|
||||
description = 'Top Stories from the New York Times'
|
||||
|
||||
# List of sections typically included in Top Stories. Use a keyword from the
|
||||
@ -64,6 +65,7 @@ class NYTimes(BasicNewsRecipe):
|
||||
timefmt = ''
|
||||
needs_subscription = True
|
||||
masthead_url = 'http://graphics8.nytimes.com/images/misc/nytlogo379x64.gif'
|
||||
cover_margins = (18,18,'grey99')
|
||||
|
||||
remove_tags_before = dict(id='article')
|
||||
remove_tags_after = dict(id='article')
|
||||
@ -183,6 +185,16 @@ class NYTimes(BasicNewsRecipe):
|
||||
self.log("\nFailed to login")
|
||||
return br
|
||||
|
||||
def skip_ad_pages(self, soup):
|
||||
# Skip ad pages served before actual article
|
||||
skip_tag = soup.find(True, {'name':'skip'})
|
||||
if skip_tag is not None:
|
||||
self.log.warn("Found forwarding link: %s" % skip_tag.parent['href'])
|
||||
url = 'http://www.nytimes.com' + re.sub(r'\?.*', '', skip_tag.parent['href'])
|
||||
url += '?pagewanted=all'
|
||||
self.log.warn("Skipping ad to article at '%s'" % url)
|
||||
return self.index_to_soup(url, raw=True)
|
||||
|
||||
def get_cover_url(self):
|
||||
cover = None
|
||||
st = time.localtime()
|
||||
@ -391,14 +403,6 @@ class NYTimes(BasicNewsRecipe):
|
||||
return ans
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
# Skip ad pages served before actual article
|
||||
skip_tag = soup.find(True, {'name':'skip'})
|
||||
if skip_tag is not None:
|
||||
self.log.error("Found forwarding link: %s" % skip_tag.parent['href'])
|
||||
url = 'http://www.nytimes.com' + re.sub(r'\?.*', '', skip_tag.parent['href'])
|
||||
url += '?pagewanted=all'
|
||||
self.log.error("Skipping ad to article at '%s'" % url)
|
||||
soup = self.index_to_soup(url)
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
def postprocess_html(self,soup, True):
|
||||
|
@ -20,6 +20,7 @@ class NYTimes(BasicNewsRecipe):
|
||||
title = 'The New York Times'
|
||||
__author__ = 'GRiker'
|
||||
language = 'en'
|
||||
requires_version = (0, 7, 3)
|
||||
|
||||
description = 'Daily news from the New York Times (subscription version)'
|
||||
allSectionKeywords = ['The Front Page', 'International','National','Obituaries','Editorials',
|
||||
@ -103,6 +104,7 @@ class NYTimes(BasicNewsRecipe):
|
||||
]),
|
||||
dict(name=['script', 'noscript', 'style'])]
|
||||
masthead_url = 'http://graphics8.nytimes.com/images/misc/nytlogo379x64.gif'
|
||||
cover_margins = (18,18,'grey99')
|
||||
no_stylesheets = True
|
||||
extra_css = '.headline {text-align: left;}\n \
|
||||
.byline {font-family: monospace; \
|
||||
@ -158,7 +160,7 @@ class NYTimes(BasicNewsRecipe):
|
||||
return cover
|
||||
|
||||
def get_masthead_title(self):
|
||||
return 'NYTimes GR Version'
|
||||
return self.title
|
||||
|
||||
def dump_ans(self, ans):
|
||||
total_article_count = 0
|
||||
@ -279,15 +281,17 @@ class NYTimes(BasicNewsRecipe):
|
||||
self.dump_ans(ans)
|
||||
return ans
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
def skip_ad_pages(self, soup):
|
||||
# Skip ad pages served before actual article
|
||||
skip_tag = soup.find(True, {'name':'skip'})
|
||||
if skip_tag is not None:
|
||||
self.log.error("Found forwarding link: %s" % skip_tag.parent['href'])
|
||||
self.log.warn("Found forwarding link: %s" % skip_tag.parent['href'])
|
||||
url = 'http://www.nytimes.com' + re.sub(r'\?.*', '', skip_tag.parent['href'])
|
||||
url += '?pagewanted=all'
|
||||
self.log.error("Skipping ad to article at '%s'" % url)
|
||||
soup = self.index_to_soup(url)
|
||||
self.log.warn("Skipping ad to article at '%s'" % url)
|
||||
return self.index_to_soup(url, raw=True)
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
def postprocess_html(self,soup, True):
|
||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.7.2'
|
||||
__version__ = '0.7.3'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
import re
|
||||
|
@ -279,6 +279,7 @@ class KoboReaderOutput(OutputProfile):
|
||||
description = _('This profile is intended for the Kobo Reader.')
|
||||
|
||||
screen_size = (590, 775)
|
||||
comic_screen_size = (540, 718)
|
||||
dpi = 168.451
|
||||
fbase = 12
|
||||
fsizes = [7.5, 9, 10, 12, 15.5, 20, 22, 24]
|
||||
|
@ -160,6 +160,7 @@ class ITUNES(DevicePlugin):
|
||||
sources = None
|
||||
update_msg = None
|
||||
update_needed = False
|
||||
use_series_data = True
|
||||
|
||||
# Public methods
|
||||
def add_books_to_metadata(self, locations, metadata, booklists):
|
||||
@ -398,7 +399,7 @@ class ITUNES(DevicePlugin):
|
||||
attempts -= 1
|
||||
time.sleep(0.5)
|
||||
if DEBUG:
|
||||
self.log.warning(" waiting for identified iPad, attempt #%d" % (10 - attempts))
|
||||
self.log.warning(" waiting for connected iPad, attempt #%d" % (10 - attempts))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(' found connected iPad')
|
||||
@ -474,7 +475,7 @@ class ITUNES(DevicePlugin):
|
||||
attempts -= 1
|
||||
time.sleep(0.5)
|
||||
if DEBUG:
|
||||
self.log.warning(" waiting for identified iPad, attempt #%d" % (10 - attempts))
|
||||
self.log.warning(" waiting for connected iPad, attempt #%d" % (10 - attempts))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(' found connected iPad in iTunes')
|
||||
@ -693,6 +694,8 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
# Purge the booklist, self.cached_books
|
||||
for i,bl_book in enumerate(booklists[0]):
|
||||
if False:
|
||||
self.log.info(" evaluating '%s'" % bl_book.uuid)
|
||||
if bl_book.uuid == self.cached_books[path]['uuid']:
|
||||
# Remove from booklists[0]
|
||||
booklists[0].pop(i)
|
||||
@ -703,6 +706,10 @@ class ITUNES(DevicePlugin):
|
||||
break
|
||||
break
|
||||
|
||||
if False:
|
||||
self._dump_booklist(booklists[0], indent = 2)
|
||||
self._dump_cached_books(indent=2)
|
||||
|
||||
def reset(self, key='-1', log_packets=False, report_progress=None,
|
||||
detected_device=None) :
|
||||
"""
|
||||
@ -1061,7 +1068,7 @@ class ITUNES(DevicePlugin):
|
||||
except:
|
||||
if DEBUG:
|
||||
self.log.warning(" iTunes automation interface reported an error"
|
||||
" when adding artwork to '%s'" % metadata.title)
|
||||
" when adding artwork to '%s' on the iDevice" % metadata.title)
|
||||
#import traceback
|
||||
#traceback.print_exc()
|
||||
#from calibre import ipython
|
||||
@ -1264,18 +1271,18 @@ class ITUNES(DevicePlugin):
|
||||
def _dump_cached_book(self, cached_book, header=None,indent=0):
|
||||
'''
|
||||
'''
|
||||
if header:
|
||||
msg = '%s%s' % (' '*indent,header)
|
||||
self.log.info(msg)
|
||||
self.log.info( "%s%s" % (' '*indent, '-' * len(msg)))
|
||||
if isosx:
|
||||
self.log.info("%s%-40.40s %-30.30s %-10.10s %-10.10s %s" %
|
||||
(' '*indent,
|
||||
'title',
|
||||
'author',
|
||||
'lib_book',
|
||||
'dev_book',
|
||||
'uuid'))
|
||||
if header:
|
||||
msg = '%s%s' % (' '*indent,header)
|
||||
self.log.info(msg)
|
||||
self.log.info( "%s%s" % (' '*indent, '-' * len(msg)))
|
||||
self.log.info("%s%-40.40s %-30.30s %-10.10s %-10.10s %s" %
|
||||
(' '*indent,
|
||||
'title',
|
||||
'author',
|
||||
'lib_book',
|
||||
'dev_book',
|
||||
'uuid'))
|
||||
self.log.info("%s%-40.40s %-30.30s %-10.10s %-10.10s %s" %
|
||||
(' '*indent,
|
||||
cached_book['title'],
|
||||
@ -1284,14 +1291,17 @@ class ITUNES(DevicePlugin):
|
||||
str(cached_book['dev_book'])[-9:],
|
||||
cached_book['uuid']))
|
||||
elif iswindows:
|
||||
if header:
|
||||
msg = '%s%s' % (' '*indent,header)
|
||||
self.log.info(msg)
|
||||
self.log.info( "%s%s" % (' '*indent, '-' * len(msg)))
|
||||
|
||||
self.log.info("%s%-40.40s %-30.30s %s" %
|
||||
(' '*indent,
|
||||
cached_book['title'],
|
||||
cached_book['author'],
|
||||
cached_book['uuid']))
|
||||
|
||||
self.log.info()
|
||||
|
||||
def _dump_cached_books(self, header=None, indent=0):
|
||||
'''
|
||||
'''
|
||||
@ -1415,19 +1425,21 @@ class ITUNES(DevicePlugin):
|
||||
(search['uuid'], search['title'], search['author']))
|
||||
attempts = 9
|
||||
while attempts:
|
||||
# Try by uuid
|
||||
# Try by uuid - only one hit
|
||||
hits = dev_books.Search(search['uuid'],self.SearchField.index('Albums'))
|
||||
if hits:
|
||||
hit = hits[0]
|
||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album))
|
||||
return hit
|
||||
|
||||
# Try by author
|
||||
# Try by author - there could be multiple hits
|
||||
hits = dev_books.Search(search['author'],self.SearchField.index('Artists'))
|
||||
if hits:
|
||||
hit = hits[0]
|
||||
self.log.info(" found '%s' by %s" % (hit.Name, hit.Artist))
|
||||
return hit
|
||||
for hit in hits:
|
||||
if hit.Name == search['title']:
|
||||
if DEBUG:
|
||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album))
|
||||
return hit
|
||||
|
||||
attempts -= 1
|
||||
time.sleep(0.5)
|
||||
@ -1438,19 +1450,19 @@ class ITUNES(DevicePlugin):
|
||||
self.log.error(" no hits")
|
||||
return None
|
||||
|
||||
def _find_library_book(self, cached_book):
|
||||
def _find_library_book(self, search):
|
||||
'''
|
||||
Windows-only method to get a handle to a library book in the current pythoncom session
|
||||
'''
|
||||
if iswindows:
|
||||
if DEBUG:
|
||||
self.log.info(" ITUNES._find_library_book()")
|
||||
if 'uuid' in cached_book:
|
||||
if 'uuid' in search:
|
||||
self.log.info(" looking for '%s' by %s (%s)" %
|
||||
(cached_book['title'], cached_book['author'], cached_book['uuid']))
|
||||
(search['title'], search['author'], search['uuid']))
|
||||
else:
|
||||
self.log.info(" looking for '%s' by %s" %
|
||||
(cached_book['title'], cached_book['author']))
|
||||
(search['title'], search['author']))
|
||||
|
||||
for source in self.iTunes.sources:
|
||||
if source.Kind == self.Sources.index('Library'):
|
||||
@ -1477,22 +1489,26 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
attempts = 9
|
||||
while attempts:
|
||||
# Find book whose Album field = cached_book['uuid']
|
||||
if 'uuid' in cached_book:
|
||||
hits = lib_books.Search(cached_book['uuid'],self.SearchField.index('Albums'))
|
||||
# Find book whose Album field = search['uuid']
|
||||
if 'uuid' in search:
|
||||
if DEBUG:
|
||||
self.log.info(" searching by uuid '%s' ..." % search['uuid'])
|
||||
hits = lib_books.Search(search['uuid'],self.SearchField.index('Albums'))
|
||||
if hits:
|
||||
hit = hits[0]
|
||||
if DEBUG:
|
||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album))
|
||||
return hit
|
||||
|
||||
hits = lib_books.Search(cached_book['author'],self.SearchField.index('Artists'))
|
||||
if DEBUG:
|
||||
self.log.info(" searching by author '%s' ..." % search['author'])
|
||||
hits = lib_books.Search(search['author'],self.SearchField.index('Artists'))
|
||||
if hits:
|
||||
hit = hits[0]
|
||||
if hit.Name == cached_book['title']:
|
||||
if DEBUG:
|
||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album))
|
||||
return hit
|
||||
for hit in hits:
|
||||
if hit.Name == search['title']:
|
||||
if DEBUG:
|
||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album))
|
||||
return hit
|
||||
|
||||
attempts -= 1
|
||||
time.sleep(0.5)
|
||||
@ -1500,7 +1516,7 @@ class ITUNES(DevicePlugin):
|
||||
self.log.warning(" attempt #%d" % (10 - attempts))
|
||||
|
||||
if DEBUG:
|
||||
self.log.error(" search for '%s' yielded no hits" % cached_book['title'])
|
||||
self.log.error(" search for '%s' yielded no hits" % search['title'])
|
||||
return None
|
||||
|
||||
def _generate_thumbnail(self, book_path, book):
|
||||
@ -1617,7 +1633,7 @@ class ITUNES(DevicePlugin):
|
||||
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(" %-30.30s %-30.30s %s [%s]" %
|
||||
self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" %
|
||||
(book.name(), book.artist(), book.album(), book.kind()))
|
||||
device_books.append(book)
|
||||
if DEBUG:
|
||||
@ -1649,7 +1665,7 @@ class ITUNES(DevicePlugin):
|
||||
self.log.info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(" %-30.30s %-30.30s %s [%s]" % (book.Name, book.Artist, book.Album, book.KindAsString))
|
||||
self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" % (book.Name, book.Artist, book.Album, book.KindAsString))
|
||||
device_books.append(book)
|
||||
if DEBUG:
|
||||
self.log.info()
|
||||
@ -1663,8 +1679,6 @@ class ITUNES(DevicePlugin):
|
||||
'''
|
||||
assumes pythoncom wrapper
|
||||
'''
|
||||
# if DEBUG:
|
||||
# self.log.info(" ITUNES._get_device_books_playlist()")
|
||||
if iswindows:
|
||||
if 'iPod' in self.sources:
|
||||
pl = None
|
||||
@ -1707,11 +1721,6 @@ class ITUNES(DevicePlugin):
|
||||
if update_md:
|
||||
self._update_epub_metadata(fpath, metadata)
|
||||
|
||||
# if DEBUG:
|
||||
# self.log.info(" metadata before rewrite: '{0[0]}' '{0[1]}' '{0[2]}'".format(self._dump_epub_metadata(fpath)))
|
||||
# self._update_epub_metadata(fpath, metadata)
|
||||
# if DEBUG:
|
||||
# self.log.info(" metadata after rewrite: '{0[0]}' '{0[1]}' '{0[2]}'".format(self._dump_epub_metadata(fpath)))
|
||||
return fpath
|
||||
|
||||
def _get_library_books(self):
|
||||
@ -1766,7 +1775,7 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
library_books[path] = book
|
||||
if DEBUG:
|
||||
self.log.info(" %-30.30s %-30.30s %s [%s]" % (book.name(), book.artist(), book.album(), book.kind()))
|
||||
self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" % (book.name(), book.artist(), book.album(), book.kind()))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(' no Library playlists')
|
||||
@ -1819,7 +1828,7 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
library_books[path] = book
|
||||
if DEBUG:
|
||||
self.log.info(" %-30.30s %-30.30s %s [%s]" % (book.Name, book.Artist, book.Album, book.KindAsString))
|
||||
self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" % (book.Name, book.Artist, book.Album, book.KindAsString))
|
||||
except:
|
||||
if DEBUG:
|
||||
self.log.info(" no books in library")
|
||||
@ -1852,8 +1861,12 @@ class ITUNES(DevicePlugin):
|
||||
Check for >1 iPod device connected to iTunes
|
||||
'''
|
||||
if isosx:
|
||||
names = [s.name() for s in self.iTunes.sources()]
|
||||
kinds = [str(s.kind()).rpartition('.')[2] for s in self.iTunes.sources()]
|
||||
try:
|
||||
names = [s.name() for s in self.iTunes.sources()]
|
||||
kinds = [str(s.kind()).rpartition('.')[2] for s in self.iTunes.sources()]
|
||||
except:
|
||||
# User probably quit iTunes
|
||||
return {}
|
||||
elif iswindows:
|
||||
# Assumes a pythoncom wrapper
|
||||
it_sources = ['Unknown','Library','iPod','AudioCD','MP3CD','Device','RadioTuner','SharedLibrary']
|
||||
@ -2130,21 +2143,6 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
# Refresh epub metadata
|
||||
with open(fpath,'r+b') as zfo:
|
||||
'''
|
||||
# Touch the timestamp to force a recache
|
||||
if metadata.timestamp:
|
||||
if DEBUG:
|
||||
self.log.info(" old timestamp: %s" % metadata.timestamp)
|
||||
old_ts = metadata.timestamp
|
||||
metadata.timestamp = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour,
|
||||
old_ts.minute, old_ts.second, old_ts.microsecond+1, old_ts.tzinfo)
|
||||
if DEBUG:
|
||||
self.log.info(" new timestamp: %s" % metadata.timestamp)
|
||||
else:
|
||||
metadata.timestamp = isoformat(now())
|
||||
if DEBUG:
|
||||
self.log.info(" add timestamp: %s" % metadata.timestamp)
|
||||
'''
|
||||
# Touch the OPF timestamp
|
||||
zf_opf = ZipFile(fpath,'r')
|
||||
fnames = zf_opf.namelist()
|
||||
@ -2273,16 +2271,20 @@ class ITUNES(DevicePlugin):
|
||||
pass
|
||||
|
||||
# Set genre from series if available, else first alpha tag
|
||||
# Otherwise iTunes grabs the first dc:subject from the opf metadata,
|
||||
if metadata.series:
|
||||
# Otherwise iTunes grabs the first dc:subject from the opf metadata
|
||||
if self.use_series_data and metadata.series:
|
||||
if lb_added:
|
||||
lb_added.sort_name.set("%s %03d" % (metadata.series, metadata.series_index))
|
||||
lb_added.genre.set(metadata.series)
|
||||
lb_added.episode_ID.set(metadata.series)
|
||||
lb_added.episode_number.set(metadata.series_index)
|
||||
|
||||
if db_added:
|
||||
db_added.sort_name.set("%s %03d" % (metadata.series, metadata.series_index))
|
||||
db_added.genre.set(metadata.series)
|
||||
db_added.episode_ID.set(metadata.series)
|
||||
db_added.episode_number.set(metadata.series_index)
|
||||
|
||||
elif metadata.tags:
|
||||
for tag in metadata.tags:
|
||||
if self._is_alpha(tag[0]):
|
||||
@ -2323,36 +2325,38 @@ class ITUNES(DevicePlugin):
|
||||
except:
|
||||
if DEBUG:
|
||||
self.log.warning(" iTunes automation interface reported an error"
|
||||
" setting AlbumRating")
|
||||
" setting AlbumRating on iDevice")
|
||||
|
||||
# Set Category from first alpha tag, overwrite with series if available
|
||||
# Set Genre from first alpha tag, overwrite with series if available
|
||||
# Otherwise iBooks uses first <dc:subject> from opf
|
||||
# iTunes balks on setting EpisodeNumber, but it sticks (9.1.1.12)
|
||||
|
||||
if metadata.series:
|
||||
if self.use_series_data and metadata.series:
|
||||
if lb_added:
|
||||
lb_added.Category = metadata.series
|
||||
lb_added.SortName = "%s %03d" % (metadata.series, metadata.series_index)
|
||||
lb_added.Genre = metadata.series
|
||||
lb_added.EpisodeID = metadata.series
|
||||
try:
|
||||
lb_added.EpisodeNumber = metadata.series_index
|
||||
except:
|
||||
pass
|
||||
if db_added:
|
||||
db_added.Category = metadata.series
|
||||
db_added.SortName = "%s %03d" % (metadata.series, metadata.series_index)
|
||||
db_added.Genre = metadata.series
|
||||
db_added.EpisodeID = metadata.series
|
||||
try:
|
||||
db_added.EpisodeNumber = metadata.series_index
|
||||
except:
|
||||
if DEBUG:
|
||||
self.log.warning(" iTunes automation interface reported an error"
|
||||
" setting EpisodeNumber")
|
||||
" setting EpisodeNumber on iDevice")
|
||||
elif metadata.tags:
|
||||
for tag in metadata.tags:
|
||||
if self._is_alpha(tag[0]):
|
||||
if lb_added:
|
||||
lb_added.Category = tag
|
||||
lb_added.Genre = tag
|
||||
if db_added:
|
||||
db_added.Category = tag
|
||||
db_added.Genre = tag
|
||||
break
|
||||
|
||||
|
||||
|
@ -411,6 +411,34 @@ class AddAction(object): # {{{
|
||||
if hasattr(self._adder, 'cleanup'):
|
||||
self._adder.cleanup()
|
||||
self._adder = None
|
||||
|
||||
def _add_from_device_adder(self, paths=[], names=[], infos=[],
|
||||
on_card=None, model=None):
|
||||
self._files_added(paths, names, infos, on_card=on_card)
|
||||
# set the in-library flags, and as a consequence send the library's
|
||||
# metadata for this book to the device. This sets the uuid to the
|
||||
# correct value.
|
||||
self.set_books_in_library(booklists=[model.db], reset=True)
|
||||
model.reset()
|
||||
|
||||
def add_books_from_device(self, view):
|
||||
rows = view.selectionModel().selectedRows()
|
||||
if not rows or len(rows) == 0:
|
||||
d = error_dialog(self, _('Add to library'), _('No book selected'))
|
||||
d.exec_()
|
||||
return
|
||||
paths = [p for p in view._model.paths(rows) if p is not None]
|
||||
if not paths or len(paths) == 0:
|
||||
d = error_dialog(self, _('Add to library'), _('No book files found'))
|
||||
d.exec_()
|
||||
return
|
||||
from calibre.gui2.add import Adder
|
||||
self.__adder_func = partial(self._add_from_device_adder, on_card=None,
|
||||
model=view._model)
|
||||
self._adder = Adder(self, self.library_view.model().db,
|
||||
Dispatcher(self.__adder_func), spare_server=self.spare_server)
|
||||
self._adder.add(paths)
|
||||
|
||||
# }}}
|
||||
|
||||
class DeleteAction(object): # {{{
|
||||
|
@ -602,7 +602,6 @@ class Emailer(Thread): # {{{
|
||||
class DeviceMixin(object): # {{{
|
||||
|
||||
def __init__(self):
|
||||
self.db_book_uuid_cache = set()
|
||||
self.device_error_dialog = error_dialog(self, _('Error'),
|
||||
_('Error communicating with device'), ' ')
|
||||
self.device_error_dialog.setModal(Qt.NonModal)
|
||||
@ -1350,11 +1349,18 @@ class DeviceMixin(object): # {{{
|
||||
return loc
|
||||
|
||||
def set_books_in_library(self, booklists, reset=False):
|
||||
if reset:
|
||||
# First build a cache of the library, so the search isn't On**2
|
||||
# Force a reset if the caches are not initialized
|
||||
if reset or not hasattr(self, 'db_book_title_cache'):
|
||||
# It might be possible to get here without having initialized the
|
||||
# library view. In this case, simply give up
|
||||
if not hasattr(self, 'library_view') or self.library_view is None:
|
||||
return
|
||||
db = getattr(self.library_view.model(), 'db', None)
|
||||
if db is None:
|
||||
return
|
||||
# Build a cache (map) of the library, so the search isn't On**2
|
||||
self.db_book_title_cache = {}
|
||||
self.db_book_uuid_cache = {}
|
||||
db = self.library_view.model().db
|
||||
for id in db.data.iterallids():
|
||||
mi = db.get_metadata(id, index_is_id=True)
|
||||
title = re.sub('(?u)\W|[_]', '', mi.title.lower())
|
||||
|
@ -218,20 +218,25 @@ class LibraryViewMixin(object): # {{{
|
||||
partial(self.show_similar_books, 'tag'))
|
||||
self.action_books_by_this_publisher.triggered.connect(
|
||||
partial(self.show_similar_books, 'publisher'))
|
||||
|
||||
self.library_view.set_context_menu(self.action_edit, self.action_sync,
|
||||
self.action_convert, self.action_view,
|
||||
self.action_save,
|
||||
self.action_open_containing_folder,
|
||||
self.action_show_book_details,
|
||||
self.action_del,
|
||||
add_to_library = None,
|
||||
similar_menu=similar_menu)
|
||||
|
||||
add_to_library = (_('Add books to library'), self.add_books_from_device)
|
||||
self.memory_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None, self.action_del)
|
||||
self.action_view, self.action_save, None, None, self.action_del,
|
||||
add_to_library=add_to_library)
|
||||
self.card_a_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None, self.action_del)
|
||||
self.action_view, self.action_save, None, None, self.action_del,
|
||||
add_to_library=add_to_library)
|
||||
self.card_b_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None, self.action_del)
|
||||
self.action_view, self.action_save, None, None, self.action_del,
|
||||
add_to_library=add_to_library)
|
||||
|
||||
self.library_view.files_dropped.connect(self.files_dropped, type=Qt.QueuedConnection)
|
||||
for func, args in [
|
||||
|
@ -370,7 +370,8 @@ class BooksView(QTableView): # {{{
|
||||
|
||||
# Context Menu {{{
|
||||
def set_context_menu(self, edit_metadata, send_to_device, convert, view,
|
||||
save, open_folder, book_details, delete, similar_menu=None):
|
||||
save, open_folder, book_details, delete,
|
||||
similar_menu=None, add_to_library=None):
|
||||
self.setContextMenuPolicy(Qt.DefaultContextMenu)
|
||||
self.context_menu = QMenu(self)
|
||||
if edit_metadata is not None:
|
||||
@ -389,6 +390,9 @@ class BooksView(QTableView): # {{{
|
||||
self.context_menu.addAction(book_details)
|
||||
if similar_menu is not None:
|
||||
self.context_menu.addMenu(similar_menu)
|
||||
if add_to_library is not None:
|
||||
func = partial(add_to_library[1], view=self)
|
||||
self.context_menu.addAction(add_to_library[0], func)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
self.context_menu.popup(event.globalPos())
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
9544
src/calibre/translations/fa.po
Normal file
9544
src/calibre/translations/fa.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user