mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-05-29 10:12:34 -04:00
KG updates 0.7.6
This commit is contained in:
@@ -4,6 +4,53 @@
|
||||
# for important features/bug fixes.
|
||||
# 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
|
||||
date: 2010-06-25
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
|
||||
import string, pprint
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class HoustonChronicle(BasicNewsRecipe):
|
||||
|
||||
title = u'The Houston Chronicle'
|
||||
description = 'News from Houston, Texas'
|
||||
__author__ = 'Kovid Goyal and Sujata Raman'
|
||||
__author__ = 'Kovid Goyal'
|
||||
language = 'en'
|
||||
timefmt = ' [%a, %d %b, %Y]'
|
||||
no_stylesheets = True
|
||||
@@ -38,54 +41,23 @@ class HoustonChronicle(BasicNewsRecipe):
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
soup = self.index_to_soup('http://www.chron.com/news/')
|
||||
container = soup.find('table', attrs={'class':'body-columns'})
|
||||
|
||||
categories = ['news', 'sports', 'business', 'entertainment', 'life',
|
||||
'travel']
|
||||
feeds = []
|
||||
current_section = 'Top Stories'
|
||||
current_articles = []
|
||||
|
||||
self.log('\tFound section:', current_section)
|
||||
|
||||
for div in container.findAll('div'):
|
||||
if div.get('class', None) == 'module-mast':
|
||||
t = self.tag_to_string(div).replace(u'\xbb', '').strip()
|
||||
if t and 'interactives' not in t:
|
||||
if current_section and current_articles:
|
||||
feeds.append((current_section, current_articles))
|
||||
current_section = t
|
||||
current_articles = []
|
||||
self.log('\tFound section:', current_section)
|
||||
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))
|
||||
for cat in categories:
|
||||
articles = []
|
||||
soup = self.index_to_soup('http://www.chron.com/%s/'%cat)
|
||||
for elem in soup.findAll(comptype='story', storyid=True):
|
||||
a = elem.find('a', href=True)
|
||||
if a is None: continue
|
||||
url = a['href']
|
||||
if not url.startswith('http://'):
|
||||
url = 'http://www.chron.com'+url
|
||||
articles.append({'title':self.tag_to_string(a), 'url':url,
|
||||
'description':'', 'date':''})
|
||||
pprint.pprint(articles[-1])
|
||||
if articles:
|
||||
feeds.append((string.capwords(cat), articles))
|
||||
return feeds
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ mimetypes.add_type('application/epub+zip', '.epub')
|
||||
mimetypes.add_type('text/x-sony-bbeb+xml', '.lrs')
|
||||
mimetypes.add_type('application/xhtml+xml', '.xhtml')
|
||||
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', '.lrx')
|
||||
mimetypes.add_type('application/x-dtbncx+xml', '.ncx')
|
||||
@@ -43,7 +44,7 @@ mimetypes.add_type('application/x-mobipocket-ebook', '.prc')
|
||||
mimetypes.add_type('application/x-mobipocket-ebook', '.azw')
|
||||
mimetypes.add_type('application/x-cbz', '.cbz')
|
||||
mimetypes.add_type('application/x-cbr', '.cbr')
|
||||
mimetypes.add_type('application/x-koboreader-ebook', '.kobo')
|
||||
mimetypes.add_type('application/x-koboreader-ebook', '.kobo')
|
||||
mimetypes.add_type('image/wmf', '.wmf')
|
||||
guess_type = mimetypes.guess_type
|
||||
import cssutils
|
||||
|
||||
@@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.7.5'
|
||||
__version__ = '0.7.6'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
import re
|
||||
|
||||
@@ -24,7 +24,7 @@ class N516(USBMS):
|
||||
|
||||
VENDOR_ID = [0x0525]
|
||||
PRODUCT_ID = [0xa4a5]
|
||||
BCD = [0x323, 0x326]
|
||||
BCD = [0x323, 0x326, 0x327]
|
||||
|
||||
VENDOR_NAME = 'INGENIC'
|
||||
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
|
||||
# adding books at this point
|
||||
for blist in booklists:
|
||||
for book in blist:
|
||||
book._new_book = False
|
||||
if blist is not None:
|
||||
for book in blist:
|
||||
book._new_book = False
|
||||
|
||||
self.report_progress(1.0, _('Sending metadata to device...'))
|
||||
debug_print('USBMS: finished sync_booklists')
|
||||
|
||||
@@ -133,10 +133,10 @@ def get_social_metadata(title, authors, publisher, isbn, username=None,
|
||||
if match is not None:
|
||||
si = float(match.group())
|
||||
mi.series_index = si
|
||||
tags = root.xpath('//div[@class="tags"]/span[@class="tag"]/a')
|
||||
if tags:
|
||||
mi.tags = [html.tostring(x, method='text', encoding=unicode) for x
|
||||
in tags]
|
||||
#tags = root.xpath('//div[@class="tags"]/span[@class="tag"]/a')
|
||||
#if tags:
|
||||
# mi.tags = [html.tostring(x, method='text', encoding=unicode) for x
|
||||
# in tags]
|
||||
span = root.xpath(
|
||||
'//table[@class="wsltable"]/tr[@class="wslcontent"]/td[4]//span')
|
||||
if span:
|
||||
|
||||
@@ -882,7 +882,7 @@ class Manifest(object):
|
||||
"<?xml version='1.0' encoding='utf-8'?><o:p></o:p>",
|
||||
'')
|
||||
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:
|
||||
# OEB_DOC_NS, but possibly others
|
||||
ns = namespace(data.tag)
|
||||
|
||||
@@ -87,6 +87,9 @@ class MetadataWidget(Widget, Ui_Form):
|
||||
if not pm.isNull():
|
||||
self.cover.setPixmap(pm)
|
||||
self.cover_data = cover
|
||||
else:
|
||||
self.cover.setPixmap(QPixmap(I('default_cover.svg')))
|
||||
|
||||
|
||||
def initialize_combos(self):
|
||||
self.initalize_authors()
|
||||
|
||||
@@ -20,36 +20,6 @@
|
||||
<string>Book Cover</string>
|
||||
</property>
|
||||
<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">
|
||||
<layout class="QVBoxLayout" name="_4">
|
||||
<property name="spacing">
|
||||
@@ -101,6 +71,30 @@
|
||||
</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="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>
|
||||
<zorder>opt_prefer_metadata_cover</zorder>
|
||||
<zorder></zorder>
|
||||
@@ -308,11 +302,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ImageView</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>widgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>EnLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
@@ -328,6 +317,12 @@
|
||||
<extends>QLineEdit</extends>
|
||||
<header>widgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ImageView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>calibre/gui2/widgets.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>title</tabstop>
|
||||
|
||||
@@ -11,7 +11,7 @@ import re
|
||||
import time
|
||||
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
|
||||
|
||||
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, \
|
||||
authors_to_string, check_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.utils.config import prefs, tweaks
|
||||
from calibre.utils.date import qt_to_dt
|
||||
@@ -311,7 +310,6 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
self.formats.setAcceptDrops(True)
|
||||
self.cover_changed = False
|
||||
self.cpixmap = None
|
||||
self.cover.setAcceptDrops(True)
|
||||
self.pubdate.setMinimumDate(QDate(100,1,1))
|
||||
pubdate_format = tweaks['gui_pubdate_display_format']
|
||||
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.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()
|
||||
if cover:
|
||||
pm.loadFromData(cover)
|
||||
|
||||
@@ -576,22 +576,13 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="ImageView" name="cover">
|
||||
<widget class="ImageView" name="cover" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
<verstretch>100</verstretch>
|
||||
</sizepolicy>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
@@ -707,11 +698,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ImageView</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>widgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>EnLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
@@ -732,6 +718,12 @@
|
||||
<extends>QListWidget</extends>
|
||||
<header location="global">calibre/gui2/widgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ImageView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>calibre/gui2/widgets.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>title</tabstop>
|
||||
|
||||
@@ -13,7 +13,7 @@ from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
|
||||
QRegExp, QSettings, QSize, QModelIndex, QSplitter, \
|
||||
QAbstractButton, QPainter, QLineEdit, QComboBox, \
|
||||
QMenu, QStringListModel, QCompleter, QStringList, \
|
||||
QTimer
|
||||
QTimer, QRect
|
||||
|
||||
from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs
|
||||
|
||||
@@ -146,10 +146,15 @@ class FormatList(QListWidget):
|
||||
return QListWidget.keyPressEvent(self, event)
|
||||
|
||||
|
||||
class ImageView(QLabel):
|
||||
class ImageView(QWidget):
|
||||
|
||||
MAX_WIDTH = 400
|
||||
MAX_HEIGHT = 300
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self._pixmap = QPixmap(self)
|
||||
self.setMinimumSize(QSize(150, 200))
|
||||
self.setAcceptDrops(True)
|
||||
|
||||
# Drag 'n drop {{{
|
||||
DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS
|
||||
|
||||
@classmethod
|
||||
@@ -186,13 +191,45 @@ class ImageView(QLabel):
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
event.acceptProposedAction()
|
||||
# }}}
|
||||
|
||||
def setPixmap(self, pixmap):
|
||||
QLabel.setPixmap(self, pixmap)
|
||||
width, height = fit_image(pixmap.width(), pixmap.height(), self.MAX_WIDTH, self.MAX_HEIGHT)[1:]
|
||||
self.setMaximumWidth(width)
|
||||
self.setMaximumHeight(height)
|
||||
if not isinstance(pixmap, QPixmap):
|
||||
raise TypeError('Must use a QPixmap')
|
||||
self._pixmap = pixmap
|
||||
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):
|
||||
cm = QMenu(self)
|
||||
copy = cm.addAction(_('Copy Image'))
|
||||
@@ -215,6 +252,7 @@ class ImageView(QLabel):
|
||||
self.setPixmap(pmap)
|
||||
self.emit(SIGNAL('cover_changed(PyQt_PyObject)'),
|
||||
pixmap_to_data(pmap))
|
||||
# }}}
|
||||
|
||||
|
||||
class LocationModel(QAbstractListModel):
|
||||
|
||||
+824
-751
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+828
-748
File diff suppressed because it is too large
Load Diff
+856
-757
File diff suppressed because it is too large
Load Diff
+874
-768
File diff suppressed because it is too large
Load Diff
+860
-758
File diff suppressed because it is too large
Load Diff
+828
-748
File diff suppressed because it is too large
Load Diff
+824
-751
File diff suppressed because it is too large
Load Diff
+861
-759
File diff suppressed because it is too large
Load Diff
+824
-751
File diff suppressed because it is too large
Load Diff
+3001
-1745
File diff suppressed because it is too large
Load Diff
+820
-744
File diff suppressed because it is too large
Load Diff
+1073
-874
File diff suppressed because it is too large
Load Diff
+881
-788
File diff suppressed because it is too large
Load Diff
+824
-751
File diff suppressed because it is too large
Load Diff
+873
-752
File diff suppressed because it is too large
Load Diff
+824
-751
File diff suppressed because it is too large
Load Diff
+825
-752
File diff suppressed because it is too large
Load Diff
+827
-747
File diff suppressed because it is too large
Load Diff
+827
-747
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user