mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
24c8b1679b
@ -4,6 +4,53 @@
|
|||||||
# for important features/bug fixes.
|
# for important features/bug fixes.
|
||||||
# Also, each release can have new and improved recipes.
|
# Also, each release can have new and improved recipes.
|
||||||
|
|
||||||
|
- version: 0.7.6
|
||||||
|
date: 2010-06-28
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Add support for the new firmware of the Azbooka"
|
||||||
|
tickets: [5994]
|
||||||
|
|
||||||
|
- title: "A few speedups for calibre startup, should add up to a few seconds of startup time on slower machines"
|
||||||
|
|
||||||
|
- title: "Support for the Sweem MM300"
|
||||||
|
|
||||||
|
- title: "Add keyboard shorcut for Download metadata and covers"
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Fix regression in 0.7.5 that broke conversion of malformed HTML files (like those Microsoft Word outputs)"
|
||||||
|
type: major
|
||||||
|
tickets: [5991]
|
||||||
|
|
||||||
|
- title: "Don't download tags from librarything, as the tagging there is not very good"
|
||||||
|
|
||||||
|
- title: "Add mimetype for FB2 so that it can be served by the content server"
|
||||||
|
tickets: [6011]
|
||||||
|
|
||||||
|
- title: "Ensure cover is not resized to less than the available space in the Edit Meta Information dialog"
|
||||||
|
tickets: [6001]
|
||||||
|
|
||||||
|
- title: "SONY driver: Only update collections when sending book to device for the first time"
|
||||||
|
|
||||||
|
- title: "calibre should now work on windows when the location for the library contains non-ascii characters"
|
||||||
|
tickets: [5983]
|
||||||
|
|
||||||
|
- title: "Cover browser once again distorts instead of cropping covers that have an incorrect aspect ratio"
|
||||||
|
|
||||||
|
- title: "ISBNDb metadata plugin: Fix bug causing only first page of results to be fetched"
|
||||||
|
|
||||||
|
- title: "Move iTunes driver to the bottom so that it doesn't interfere with device detection for people that have iphones and an ereader plugged in"
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Houston Chronicle
|
||||||
|
- Hindu
|
||||||
|
- Times of India
|
||||||
|
- New York Times
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: Winnipeg Sun
|
||||||
|
author: rty
|
||||||
|
|
||||||
- version: 0.7.5
|
- version: 0.7.5
|
||||||
date: 2010-06-25
|
date: 2010-06-25
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import with_statement
|
|||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
|
||||||
import re
|
import time
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class TheHindu(BasicNewsRecipe):
|
class TheHindu(BasicNewsRecipe):
|
||||||
@ -10,45 +10,41 @@ class TheHindu(BasicNewsRecipe):
|
|||||||
language = 'en_IN'
|
language = 'en_IN'
|
||||||
|
|
||||||
oldest_article = 7
|
oldest_article = 7
|
||||||
__author__ = 'Kovid Goyal and Sujata Raman'
|
__author__ = 'Kovid Goyal'
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
|
||||||
remove_tags_before = {'name':'font', 'class':'storyhead'}
|
keep_only_tags = [dict(id='content')]
|
||||||
preprocess_regexps = [
|
remove_tags = [dict(attrs={'class':['article-links', 'breadcr']}),
|
||||||
(re.compile(r'<!-- story ends -->.*', re.DOTALL),
|
dict(id=['email-section', 'right-column', 'printfooter'])]
|
||||||
lambda match: '</body></html>'),
|
|
||||||
]
|
extra_css = '.photo-caption { font-size: smaller }'
|
||||||
extra_css = '''
|
|
||||||
.storyhead{font-family:Arial,Helvetica,sans-serif; font-size:large; color:#000099;}
|
|
||||||
body{font-family:Verdana,Arial,Helvetica,sans-serif; font-size:x-small; text-align:left;}
|
|
||||||
'''
|
|
||||||
feeds = [
|
|
||||||
(u'Main - Front Page', u'http://www.hindu.com/rss/01hdline.xml'),
|
|
||||||
(u'Main - National', u'http://www.hindu.com/rss/02hdline.xml'),
|
|
||||||
(u'Main - International', u'http://www.hindu.com/rss/03hdline.xml'),
|
|
||||||
(u'Main - Opinion', u'http://www.hindu.com/rss/05hdline.xml'),
|
|
||||||
(u'Main - Business', u'http://www.hindu.com/rss/06hdline.xml'),
|
|
||||||
(u'Main - Sport', u'http://www.hindu.com/rss/07hdline.xml'),
|
|
||||||
(u'Main - Weather / Religion / Crossword / Cartoon',
|
|
||||||
u'http://www.hindu.com/rss/10hdline.xml'),
|
|
||||||
(u'Main - Engagements', u'http://www.hindu.com/rss/26hdline.xml'),
|
|
||||||
(u'Supplement - Literary Review',
|
|
||||||
u'http://www.hindu.com/rss/lrhdline.xml'),
|
|
||||||
(u'Supplement - Sunday Magazine',
|
|
||||||
u'http://www.hindu.com/rss/maghdline.xml'),
|
|
||||||
(u'Supplement - Open Page', u'http://www.hindu.com/rss/ophdline.xml'),
|
|
||||||
(u'Supplement - Business Review',
|
|
||||||
u'http://www.hindu.com/rss/bizhdline.xml'),
|
|
||||||
(u'Supplement - Book Review',
|
|
||||||
u'http://www.hindu.com/rss/brhdline.xml'),
|
|
||||||
(u'Supplement - Science & Technology',
|
|
||||||
u'http://www.hindu.com/rss/setahdline.xml')
|
|
||||||
]
|
|
||||||
|
|
||||||
def postprocess_html(self, soup, first_fetch):
|
def postprocess_html(self, soup, first_fetch):
|
||||||
for t in soup.findAll(['table', 'tr', 'td','center']):
|
for t in soup.findAll(['table', 'tr', 'td','center']):
|
||||||
t.name = 'div'
|
t.name = 'div'
|
||||||
|
|
||||||
|
|
||||||
return soup
|
return soup
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
today = time.strftime('%Y-%m-%d')
|
||||||
|
soup = self.index_to_soup(
|
||||||
|
'http://www.thehindu.com/todays-paper/tp-index/?date=' + today)
|
||||||
|
div = soup.find(id='left-column')
|
||||||
|
feeds = []
|
||||||
|
current_section = None
|
||||||
|
current_articles = []
|
||||||
|
for x in div.findAll(['h3', 'div']):
|
||||||
|
if current_section and x.get('class', '') == 'tpaper':
|
||||||
|
a = x.find('a', href=True)
|
||||||
|
if a is not None:
|
||||||
|
current_articles.append({'url':a['href']+'?css=print',
|
||||||
|
'title':self.tag_to_string(a), 'date': '',
|
||||||
|
'description':''})
|
||||||
|
if x.name == 'h3':
|
||||||
|
if current_section and current_articles:
|
||||||
|
feeds.append((current_section, current_articles))
|
||||||
|
current_section = self.tag_to_string(x)
|
||||||
|
current_articles = []
|
||||||
|
return feeds
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
import string, pprint
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class HoustonChronicle(BasicNewsRecipe):
|
class HoustonChronicle(BasicNewsRecipe):
|
||||||
|
|
||||||
title = u'The Houston Chronicle'
|
title = u'The Houston Chronicle'
|
||||||
description = 'News from Houston, Texas'
|
description = 'News from Houston, Texas'
|
||||||
__author__ = 'Kovid Goyal and Sujata Raman'
|
__author__ = 'Kovid Goyal'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
timefmt = ' [%a, %d %b, %Y]'
|
timefmt = ' [%a, %d %b, %Y]'
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
@ -38,54 +41,23 @@ class HoustonChronicle(BasicNewsRecipe):
|
|||||||
|
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
soup = self.index_to_soup('http://www.chron.com/news/')
|
categories = ['news', 'sports', 'business', 'entertainment', 'life',
|
||||||
container = soup.find('table', attrs={'class':'body-columns'})
|
'travel']
|
||||||
|
|
||||||
feeds = []
|
feeds = []
|
||||||
current_section = 'Top Stories'
|
for cat in categories:
|
||||||
current_articles = []
|
articles = []
|
||||||
|
soup = self.index_to_soup('http://www.chron.com/%s/'%cat)
|
||||||
self.log('\tFound section:', current_section)
|
for elem in soup.findAll(comptype='story', storyid=True):
|
||||||
|
a = elem.find('a', href=True)
|
||||||
for div in container.findAll('div'):
|
if a is None: continue
|
||||||
if div.get('class', None) == 'module-mast':
|
url = a['href']
|
||||||
t = self.tag_to_string(div).replace(u'\xbb', '').strip()
|
if not url.startswith('http://'):
|
||||||
if t and 'interactives' not in t:
|
url = 'http://www.chron.com'+url
|
||||||
if current_section and current_articles:
|
articles.append({'title':self.tag_to_string(a), 'url':url,
|
||||||
feeds.append((current_section, current_articles))
|
'description':'', 'date':''})
|
||||||
current_section = t
|
pprint.pprint(articles[-1])
|
||||||
current_articles = []
|
if articles:
|
||||||
self.log('\tFound section:', current_section)
|
feeds.append((string.capwords(cat), articles))
|
||||||
elif div.get('storyid', False):
|
|
||||||
a = div.find('a', href=True)
|
|
||||||
if a:
|
|
||||||
title = self.tag_to_string(a)
|
|
||||||
url = a.get('href')
|
|
||||||
if title and url:
|
|
||||||
if url.startswith('/'):
|
|
||||||
url = 'http://www.chron.com'+url
|
|
||||||
self.log('\t\tFound article:', title)
|
|
||||||
self.log('\t\t\t', url)
|
|
||||||
current_articles.append({'title':title, 'url':url,
|
|
||||||
'date':'', 'description':''})
|
|
||||||
elif div.get('class', None) == 'columnbox' and \
|
|
||||||
'special' in current_section.lower():
|
|
||||||
a = div.find('a')
|
|
||||||
if a:
|
|
||||||
title = self.tag_to_string(a)
|
|
||||||
url = a.get('href')
|
|
||||||
if title and url:
|
|
||||||
if not url.startswith('/'): continue
|
|
||||||
url = 'http://www.chron.com'+url
|
|
||||||
self.log('\t\tFound article:', title)
|
|
||||||
self.log('\t\t\t', url)
|
|
||||||
a.extract()
|
|
||||||
desc = self.tag_to_string(div)
|
|
||||||
current_articles.append({'title':title, 'url':url,
|
|
||||||
'date':'', 'description':desc})
|
|
||||||
|
|
||||||
if current_section and current_articles:
|
|
||||||
feeds.append((current_section, current_articles))
|
|
||||||
return feeds
|
return feeds
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,21 +1,16 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
|
||||||
|
|
||||||
class TimesOfIndia(BasicNewsRecipe):
|
class TimesOfIndia(BasicNewsRecipe):
|
||||||
title = u'Times of India'
|
title = u'Times of India'
|
||||||
language = 'en_IN'
|
language = 'en_IN'
|
||||||
__author__ = 'Krittika Goyal'
|
__author__ = 'Kovid Goyal'
|
||||||
oldest_article = 1 #days
|
oldest_article = 1 #days
|
||||||
max_articles_per_feed = 25
|
max_articles_per_feed = 25
|
||||||
|
|
||||||
remove_stylesheets = True
|
no_stylesheets = True
|
||||||
|
keep_only_tags = [dict(attrs={'class':'prttabl'})]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='iframe'),
|
dict(style=lambda x: x and 'float' in x)
|
||||||
dict(name='td', attrs={'class':'newptool1'}),
|
|
||||||
dict(name='div', attrs={'id':'newptool'}),
|
|
||||||
dict(name='ul', attrs={'class':'newtabcontent_tabs_new'}),
|
|
||||||
dict(name='b', text='Topics'),
|
|
||||||
dict(name='span', text=':'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
@ -42,13 +37,8 @@ class TimesOfIndia(BasicNewsRecipe):
|
|||||||
('Most Read',
|
('Most Read',
|
||||||
'http://timesofindia.indiatimes.com/rssfeedmostread.cms')
|
'http://timesofindia.indiatimes.com/rssfeedmostread.cms')
|
||||||
]
|
]
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?prtpage=1'
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
heading = soup.find(name='h1', attrs={'class':'heading'})
|
|
||||||
td = heading.findParent(name='td')
|
|
||||||
td.extract()
|
|
||||||
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
|
||||||
body = soup.find(name='body')
|
|
||||||
body.insert(0, td)
|
|
||||||
td.name = 'div'
|
|
||||||
return soup
|
return soup
|
||||||
|
@ -30,6 +30,7 @@ mimetypes.add_type('application/epub+zip', '.epub')
|
|||||||
mimetypes.add_type('text/x-sony-bbeb+xml', '.lrs')
|
mimetypes.add_type('text/x-sony-bbeb+xml', '.lrs')
|
||||||
mimetypes.add_type('application/xhtml+xml', '.xhtml')
|
mimetypes.add_type('application/xhtml+xml', '.xhtml')
|
||||||
mimetypes.add_type('image/svg+xml', '.svg')
|
mimetypes.add_type('image/svg+xml', '.svg')
|
||||||
|
mimetypes.add_type('text/fb2+xml', '.fb2')
|
||||||
mimetypes.add_type('application/x-sony-bbeb', '.lrf')
|
mimetypes.add_type('application/x-sony-bbeb', '.lrf')
|
||||||
mimetypes.add_type('application/x-sony-bbeb', '.lrx')
|
mimetypes.add_type('application/x-sony-bbeb', '.lrx')
|
||||||
mimetypes.add_type('application/x-dtbncx+xml', '.ncx')
|
mimetypes.add_type('application/x-dtbncx+xml', '.ncx')
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.7.5'
|
__version__ = '0.7.6'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -24,7 +24,7 @@ class N516(USBMS):
|
|||||||
|
|
||||||
VENDOR_ID = [0x0525]
|
VENDOR_ID = [0x0525]
|
||||||
PRODUCT_ID = [0xa4a5]
|
PRODUCT_ID = [0xa4a5]
|
||||||
BCD = [0x323, 0x326]
|
BCD = [0x323, 0x326, 0x327]
|
||||||
|
|
||||||
VENDOR_NAME = 'INGENIC'
|
VENDOR_NAME = 'INGENIC'
|
||||||
WINDOWS_MAIN_MEM = '_FILE-STOR_GADGE'
|
WINDOWS_MAIN_MEM = '_FILE-STOR_GADGE'
|
||||||
|
@ -298,8 +298,9 @@ class USBMS(CLI, Device):
|
|||||||
# Clear the _new_book indication, as we are supposed to be done with
|
# Clear the _new_book indication, as we are supposed to be done with
|
||||||
# adding books at this point
|
# adding books at this point
|
||||||
for blist in booklists:
|
for blist in booklists:
|
||||||
for book in blist:
|
if blist is not None:
|
||||||
book._new_book = False
|
for book in blist:
|
||||||
|
book._new_book = False
|
||||||
|
|
||||||
self.report_progress(1.0, _('Sending metadata to device...'))
|
self.report_progress(1.0, _('Sending metadata to device...'))
|
||||||
debug_print('USBMS: finished sync_booklists')
|
debug_print('USBMS: finished sync_booklists')
|
||||||
|
@ -7,6 +7,7 @@ Fetch cover from LibraryThing.com based on ISBN number.
|
|||||||
import sys, socket, os, re
|
import sys, socket, os, re
|
||||||
|
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
import mechanize
|
||||||
|
|
||||||
from calibre import browser, prints
|
from calibre import browser, prints
|
||||||
from calibre.utils.config import OptionParser
|
from calibre.utils.config import OptionParser
|
||||||
@ -14,11 +15,17 @@ from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
|||||||
|
|
||||||
OPENLIBRARY = 'http://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false'
|
OPENLIBRARY = 'http://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false'
|
||||||
|
|
||||||
|
class HeadRequest(mechanize.Request):
|
||||||
|
|
||||||
|
def get_method(self):
|
||||||
|
return 'HEAD'
|
||||||
|
|
||||||
def check_for_cover(isbn, timeout=5.):
|
def check_for_cover(isbn, timeout=5.):
|
||||||
br = browser()
|
br = browser()
|
||||||
br.set_handle_redirect(False)
|
br.set_handle_redirect(False)
|
||||||
try:
|
try:
|
||||||
br.open_novisit(OPENLIBRARY%isbn, timeout=timeout)
|
br.open_novisit(HeadRequest(OPENLIBRARY%isbn), timeout=timeout)
|
||||||
|
return True
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if callable(getattr(e, 'getcode', None)) and e.getcode() == 302:
|
if callable(getattr(e, 'getcode', None)) and e.getcode() == 302:
|
||||||
return True
|
return True
|
||||||
@ -126,10 +133,10 @@ def get_social_metadata(title, authors, publisher, isbn, username=None,
|
|||||||
if match is not None:
|
if match is not None:
|
||||||
si = float(match.group())
|
si = float(match.group())
|
||||||
mi.series_index = si
|
mi.series_index = si
|
||||||
tags = root.xpath('//div[@class="tags"]/span[@class="tag"]/a')
|
#tags = root.xpath('//div[@class="tags"]/span[@class="tag"]/a')
|
||||||
if tags:
|
#if tags:
|
||||||
mi.tags = [html.tostring(x, method='text', encoding=unicode) for x
|
# mi.tags = [html.tostring(x, method='text', encoding=unicode) for x
|
||||||
in tags]
|
# in tags]
|
||||||
span = root.xpath(
|
span = root.xpath(
|
||||||
'//table[@class="wsltable"]/tr[@class="wslcontent"]/td[4]//span')
|
'//table[@class="wsltable"]/tr[@class="wslcontent"]/td[4]//span')
|
||||||
if span:
|
if span:
|
||||||
|
@ -882,7 +882,7 @@ class Manifest(object):
|
|||||||
"<?xml version='1.0' encoding='utf-8'?><o:p></o:p>",
|
"<?xml version='1.0' encoding='utf-8'?><o:p></o:p>",
|
||||||
'')
|
'')
|
||||||
data = data.replace("<?xml version='1.0' encoding='utf-8'??>", '')
|
data = data.replace("<?xml version='1.0' encoding='utf-8'??>", '')
|
||||||
data = etree.fromstring(data)
|
data = etree.fromstring(data, parser=RECOVER_PARSER)
|
||||||
elif namespace(data.tag) != XHTML_NS:
|
elif namespace(data.tag) != XHTML_NS:
|
||||||
# OEB_DOC_NS, but possibly others
|
# OEB_DOC_NS, but possibly others
|
||||||
ns = namespace(data.tag)
|
ns = namespace(data.tag)
|
||||||
|
@ -173,6 +173,7 @@ class Label(QLabel):
|
|||||||
self.setTextFormat(Qt.RichText)
|
self.setTextFormat(Qt.RichText)
|
||||||
self.setText('')
|
self.setText('')
|
||||||
self.setWordWrap(True)
|
self.setWordWrap(True)
|
||||||
|
self.setAlignment(Qt.AlignTop)
|
||||||
self.linkActivated.connect(self.link_activated)
|
self.linkActivated.connect(self.link_activated)
|
||||||
self._link_clicked = False
|
self._link_clicked = False
|
||||||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
@ -205,15 +206,15 @@ class BookInfo(QScrollArea):
|
|||||||
rows = render_rows(data)
|
rows = render_rows(data)
|
||||||
rows = u'\n'.join([u'<tr><td valign="top"><b>%s:</b></td><td valign="top">%s</td></tr>'%(k,t) for
|
rows = u'\n'.join([u'<tr><td valign="top"><b>%s:</b></td><td valign="top">%s</td></tr>'%(k,t) for
|
||||||
k, t in rows])
|
k, t in rows])
|
||||||
|
comments = ''
|
||||||
|
if data.get(_('Comments'), '') not in ('', u'None'):
|
||||||
|
comments = data[_('Comments')]
|
||||||
|
comments = comments_to_html(comments)
|
||||||
if self.vertical:
|
if self.vertical:
|
||||||
if _('Comments') in data and data[_('Comments')]:
|
if comments:
|
||||||
comments = comments_to_html(data[_('Comments')])
|
|
||||||
rows += u'<tr><td colspan="2">%s</td></tr>'%comments
|
rows += u'<tr><td colspan="2">%s</td></tr>'%comments
|
||||||
self.label.setText(u'<table>%s</table>'%rows)
|
self.label.setText(u'<table>%s</table>'%rows)
|
||||||
else:
|
else:
|
||||||
comments = ''
|
|
||||||
if _('Comments') in data:
|
|
||||||
comments = comments_to_html(data[_('Comments')])
|
|
||||||
left_pane = u'<table>%s</table>'%rows
|
left_pane = u'<table>%s</table>'%rows
|
||||||
right_pane = u'<div>%s</div>'%comments
|
right_pane = u'<div>%s</div>'%comments
|
||||||
self.label.setText(u'<table><tr><td valign="top" '
|
self.label.setText(u'<table><tr><td valign="top" '
|
||||||
|
@ -87,6 +87,9 @@ class MetadataWidget(Widget, Ui_Form):
|
|||||||
if not pm.isNull():
|
if not pm.isNull():
|
||||||
self.cover.setPixmap(pm)
|
self.cover.setPixmap(pm)
|
||||||
self.cover_data = cover
|
self.cover_data = cover
|
||||||
|
else:
|
||||||
|
self.cover.setPixmap(QPixmap(I('default_cover.svg')))
|
||||||
|
|
||||||
|
|
||||||
def initialize_combos(self):
|
def initialize_combos(self):
|
||||||
self.initalize_authors()
|
self.initalize_authors()
|
||||||
|
@ -20,36 +20,6 @@
|
|||||||
<string>Book Cover</string>
|
<string>Book Cover</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="_2">
|
<layout class="QGridLayout" name="_2">
|
||||||
<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="../../../../resources/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">
|
<item row="1" column="0">
|
||||||
<layout class="QVBoxLayout" name="_4">
|
<layout class="QVBoxLayout" name="_4">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
@ -101,6 +71,30 @@
|
|||||||
</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" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
<zorder>opt_prefer_metadata_cover</zorder>
|
<zorder>opt_prefer_metadata_cover</zorder>
|
||||||
<zorder></zorder>
|
<zorder></zorder>
|
||||||
@ -308,11 +302,6 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
|
||||||
<class>ImageView</class>
|
|
||||||
<extends>QLabel</extends>
|
|
||||||
<header>widgets.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>EnLineEdit</class>
|
<class>EnLineEdit</class>
|
||||||
<extends>QLineEdit</extends>
|
<extends>QLineEdit</extends>
|
||||||
@ -328,6 +317,12 @@
|
|||||||
<extends>QLineEdit</extends>
|
<extends>QLineEdit</extends>
|
||||||
<header>widgets.h</header>
|
<header>widgets.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>ImageView</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>calibre/gui2/widgets.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>title</tabstop>
|
<tabstop>title</tabstop>
|
||||||
|
@ -11,7 +11,7 @@ import re
|
|||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from PyQt4.Qt import SIGNAL, QObject, QCoreApplication, Qt, QTimer, QThread, QDate, \
|
from PyQt4.Qt import SIGNAL, QObject, Qt, QTimer, QThread, QDate, \
|
||||||
QPixmap, QListWidgetItem, QDialog
|
QPixmap, QListWidgetItem, QDialog
|
||||||
|
|
||||||
from calibre.gui2 import error_dialog, file_icon_provider, dynamic, \
|
from calibre.gui2 import error_dialog, file_icon_provider, dynamic, \
|
||||||
@ -25,7 +25,6 @@ from calibre.ebooks import BOOK_EXTENSIONS
|
|||||||
from calibre.ebooks.metadata import string_to_authors, \
|
from calibre.ebooks.metadata import string_to_authors, \
|
||||||
authors_to_string, check_isbn
|
authors_to_string, check_isbn
|
||||||
from calibre.ebooks.metadata.library_thing import cover_from_isbn
|
from calibre.ebooks.metadata.library_thing import cover_from_isbn
|
||||||
from calibre import islinux, isfreebsd
|
|
||||||
from calibre.ebooks.metadata.meta import get_metadata
|
from calibre.ebooks.metadata.meta import get_metadata
|
||||||
from calibre.utils.config import prefs, tweaks
|
from calibre.utils.config import prefs, tweaks
|
||||||
from calibre.utils.date import qt_to_dt
|
from calibre.utils.date import qt_to_dt
|
||||||
@ -311,7 +310,6 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
self.formats.setAcceptDrops(True)
|
self.formats.setAcceptDrops(True)
|
||||||
self.cover_changed = False
|
self.cover_changed = False
|
||||||
self.cpixmap = None
|
self.cpixmap = None
|
||||||
self.cover.setAcceptDrops(True)
|
|
||||||
self.pubdate.setMinimumDate(QDate(100,1,1))
|
self.pubdate.setMinimumDate(QDate(100,1,1))
|
||||||
pubdate_format = tweaks['gui_pubdate_display_format']
|
pubdate_format = tweaks['gui_pubdate_display_format']
|
||||||
if pubdate_format is not None:
|
if pubdate_format is not None:
|
||||||
@ -399,11 +397,6 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
self.series.lineEdit().editingFinished.connect(self.increment_series_index)
|
self.series.lineEdit().editingFinished.connect(self.increment_series_index)
|
||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
height_of_rest = self.frameGeometry().height() - self.cover.height()
|
|
||||||
width_of_rest = self.frameGeometry().width() - self.cover.width()
|
|
||||||
ag = QCoreApplication.instance().desktop().availableGeometry(self)
|
|
||||||
self.cover.MAX_HEIGHT = ag.height()-(25 if (islinux or isfreebsd) else 0)-height_of_rest
|
|
||||||
self.cover.MAX_WIDTH = ag.width()-(25 if (islinux or isfreebsd) else 0)-width_of_rest
|
|
||||||
pm = QPixmap()
|
pm = QPixmap()
|
||||||
if cover:
|
if cover:
|
||||||
pm.loadFromData(cover)
|
pm.loadFromData(cover)
|
||||||
|
@ -576,22 +576,13 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
<widget class="ImageView" name="cover">
|
<widget class="ImageView" name="cover" native="true">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>100</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="pixmap">
|
|
||||||
<pixmap resource="../../../../resources/images.qrc">:/images/book.svg</pixmap>
|
|
||||||
</property>
|
|
||||||
<property name="scaledContents">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -707,11 +698,6 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
|
||||||
<class>ImageView</class>
|
|
||||||
<extends>QLabel</extends>
|
|
||||||
<header>widgets.h</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>EnLineEdit</class>
|
<class>EnLineEdit</class>
|
||||||
<extends>QLineEdit</extends>
|
<extends>QLineEdit</extends>
|
||||||
@ -732,6 +718,12 @@
|
|||||||
<extends>QListWidget</extends>
|
<extends>QListWidget</extends>
|
||||||
<header location="global">calibre/gui2/widgets.h</header>
|
<header location="global">calibre/gui2/widgets.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>ImageView</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>calibre/gui2/widgets.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>title</tabstop>
|
<tabstop>title</tabstop>
|
||||||
|
@ -13,7 +13,7 @@ from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
|
|||||||
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
|
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
|
||||||
QAbstractButton, QPainter, QLineEdit, QComboBox, \
|
QAbstractButton, QPainter, QLineEdit, QComboBox, \
|
||||||
QMenu, QStringListModel, QCompleter, QStringList, \
|
QMenu, QStringListModel, QCompleter, QStringList, \
|
||||||
QTimer
|
QTimer, QRect
|
||||||
|
|
||||||
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs
|
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs
|
||||||
|
|
||||||
@ -146,10 +146,15 @@ class FormatList(QListWidget):
|
|||||||
return QListWidget.keyPressEvent(self, event)
|
return QListWidget.keyPressEvent(self, event)
|
||||||
|
|
||||||
|
|
||||||
class ImageView(QLabel):
|
class ImageView(QWidget):
|
||||||
|
|
||||||
MAX_WIDTH = 400
|
def __init__(self, parent=None):
|
||||||
MAX_HEIGHT = 300
|
QWidget.__init__(self, parent)
|
||||||
|
self._pixmap = QPixmap(self)
|
||||||
|
self.setMinimumSize(QSize(150, 200))
|
||||||
|
self.setAcceptDrops(True)
|
||||||
|
|
||||||
|
# Drag 'n drop {{{
|
||||||
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS
|
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -186,13 +191,45 @@ class ImageView(QLabel):
|
|||||||
|
|
||||||
def dragMoveEvent(self, event):
|
def dragMoveEvent(self, event):
|
||||||
event.acceptProposedAction()
|
event.acceptProposedAction()
|
||||||
|
# }}}
|
||||||
|
|
||||||
def setPixmap(self, pixmap):
|
def setPixmap(self, pixmap):
|
||||||
QLabel.setPixmap(self, pixmap)
|
if not isinstance(pixmap, QPixmap):
|
||||||
width, height = fit_image(pixmap.width(), pixmap.height(), self.MAX_WIDTH, self.MAX_HEIGHT)[1:]
|
raise TypeError('Must use a QPixmap')
|
||||||
self.setMaximumWidth(width)
|
self._pixmap = pixmap
|
||||||
self.setMaximumHeight(height)
|
self.updateGeometry()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def pixmap(self):
|
||||||
|
return self._pixmap
|
||||||
|
|
||||||
|
def sizeHint(self):
|
||||||
|
if self._pixmap.isNull():
|
||||||
|
return self.minimumSize()
|
||||||
|
return self._pixmap.size()
|
||||||
|
|
||||||
|
def paintEvent(self, event):
|
||||||
|
QWidget.paintEvent(self, event)
|
||||||
|
pmap = self._pixmap
|
||||||
|
if pmap.isNull():
|
||||||
|
return
|
||||||
|
w, h = pmap.width(), pmap.height()
|
||||||
|
cw, ch = self.rect().width(), self.rect().height()
|
||||||
|
scaled, nw, nh = fit_image(w, h, cw, ch)
|
||||||
|
if scaled:
|
||||||
|
pmap = pmap.scaled(nw, nh, Qt.IgnoreAspectRatio,
|
||||||
|
Qt.SmoothTransformation)
|
||||||
|
w, h = pmap.width(), pmap.height()
|
||||||
|
x = int(abs(cw - w)/2.)
|
||||||
|
y = int(abs(ch - h)/2.)
|
||||||
|
target = QRect(x, y, w, h)
|
||||||
|
p = QPainter(self)
|
||||||
|
p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
|
||||||
|
p.drawPixmap(target, pmap)
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
|
||||||
|
# Clipboard copy/paste # {{{
|
||||||
def contextMenuEvent(self, ev):
|
def contextMenuEvent(self, ev):
|
||||||
cm = QMenu(self)
|
cm = QMenu(self)
|
||||||
copy = cm.addAction(_('Copy Image'))
|
copy = cm.addAction(_('Copy Image'))
|
||||||
@ -215,6 +252,7 @@ class ImageView(QLabel):
|
|||||||
self.setPixmap(pmap)
|
self.setPixmap(pmap)
|
||||||
self.emit(SIGNAL('cover_changed(PyQt_PyObject)'),
|
self.emit(SIGNAL('cover_changed(PyQt_PyObject)'),
|
||||||
pixmap_to_data(pmap))
|
pixmap_to_data(pmap))
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class LocationModel(QAbstractListModel):
|
class LocationModel(QAbstractListModel):
|
||||||
|
@ -39,6 +39,11 @@ def comments_to_html(comments):
|
|||||||
if not isinstance(comments, unicode):
|
if not isinstance(comments, unicode):
|
||||||
comments = comments.decode(preferred_encoding, 'replace')
|
comments = comments.decode(preferred_encoding, 'replace')
|
||||||
|
|
||||||
|
if '<' not in comments:
|
||||||
|
comments = prepare_string_for_xml(comments)
|
||||||
|
comments = comments.replace(u'\n', u'<br />')
|
||||||
|
return u'<p>%s</p>'%comments
|
||||||
|
|
||||||
# Hackish - ignoring sentences ending or beginning in numbers to avoid
|
# Hackish - ignoring sentences ending or beginning in numbers to avoid
|
||||||
# confusion with decimal points.
|
# confusion with decimal points.
|
||||||
|
|
||||||
|
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
File diff suppressed because it is too large
Load Diff
@ -15,19 +15,23 @@ if _dev_path is not None:
|
|||||||
if not os.path.exists(_dev_path):
|
if not os.path.exists(_dev_path):
|
||||||
_dev_path = None
|
_dev_path = None
|
||||||
|
|
||||||
|
_path_cache = {}
|
||||||
|
|
||||||
def get_path(path, data=False):
|
def get_path(path, data=False):
|
||||||
global _dev_path
|
global _dev_path
|
||||||
path = path.replace(os.sep, '/')
|
path = path.replace(os.sep, '/')
|
||||||
base = None
|
base = sys.resources_location
|
||||||
if _dev_path is not None:
|
if _dev_path is not None:
|
||||||
|
if path in _path_cache:
|
||||||
|
return _path_cache[path]
|
||||||
if os.path.exists(os.path.join(_dev_path, *path.split('/'))):
|
if os.path.exists(os.path.join(_dev_path, *path.split('/'))):
|
||||||
base = _dev_path
|
base = _dev_path
|
||||||
if base is None:
|
fpath = os.path.join(base, *path.split('/'))
|
||||||
base = sys.resources_location
|
if _dev_path is not None:
|
||||||
path = os.path.join(base, *path.split('/'))
|
_path_cache[path] = fpath
|
||||||
if data:
|
if data:
|
||||||
return open(path, 'rb').read()
|
return open(fpath, 'rb').read()
|
||||||
return path
|
return fpath
|
||||||
|
|
||||||
def get_image_path(path, data=False):
|
def get_image_path(path, data=False):
|
||||||
return get_path('images/'+path, data=data)
|
return get_path('images/'+path, data=data)
|
||||||
|
@ -96,19 +96,21 @@ class NewsItem(NewsTreeItem):
|
|||||||
builtin, custom, scheduler_config, parent):
|
builtin, custom, scheduler_config, parent):
|
||||||
NewsTreeItem.__init__(self, builtin, custom, scheduler_config, parent)
|
NewsTreeItem.__init__(self, builtin, custom, scheduler_config, parent)
|
||||||
self.urn, self.title = urn, title
|
self.urn, self.title = urn, title
|
||||||
|
self.icon = self.default_icon = None
|
||||||
|
self.default_icon = default_icon
|
||||||
if 'custom:' in self.urn:
|
if 'custom:' in self.urn:
|
||||||
self.icon = custom_icon
|
self.icon = custom_icon
|
||||||
else:
|
|
||||||
icon = I('news/%s.png'%self.urn[8:])
|
|
||||||
if os.path.exists(icon):
|
|
||||||
self.icon = QVariant(QIcon(icon))
|
|
||||||
else:
|
|
||||||
self.icon = default_icon
|
|
||||||
|
|
||||||
def data(self, role):
|
def data(self, role):
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
return QVariant(self.title)
|
return QVariant(self.title)
|
||||||
if role == Qt.DecorationRole:
|
if role == Qt.DecorationRole:
|
||||||
|
if self.icon is None:
|
||||||
|
icon = I('news/%s.png'%self.urn[8:])
|
||||||
|
if os.path.exists(icon):
|
||||||
|
self.icon = QVariant(QIcon(icon))
|
||||||
|
else:
|
||||||
|
self.icon = self.default_icon
|
||||||
return self.icon
|
return self.icon
|
||||||
return NONE
|
return NONE
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user