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
5330c07458
@ -4,6 +4,85 @@
|
||||
# for important features/bug fixes.
|
||||
# Also, each release can have new and improved recipes.
|
||||
|
||||
- version: 0.7.23
|
||||
date: 2010-10-08
|
||||
|
||||
new features:
|
||||
- title: "Drag and drop to Tag Browser. You can use this to conveniently add tags, set series/publisher etc for a group of books"
|
||||
|
||||
- title: "Allow switching of library even when a device is connected"
|
||||
|
||||
- title: "Support for the PD Novel running Kobo"
|
||||
|
||||
- title: "Move check library integrity from preferences to drop down menu accessed by clicking arrow next to calibre icon"
|
||||
|
||||
- title: "Nicer, non-blocking update available notification"
|
||||
|
||||
- title: "E-book viewer: If you choose to remeber last used window size, the state of the Table of Contents view is also remembered"
|
||||
tickets: [7082]
|
||||
|
||||
- title: "Allow moving as well as copying of books to another library"
|
||||
|
||||
- title: "Apple devices: Add support for plugboards"
|
||||
|
||||
- title: "Allow DJVU to be sent to the DR1000"
|
||||
|
||||
bug fixes:
|
||||
- title: "Searching: Fix search expression parser to allow use of escaped double quotes in the search expression"
|
||||
|
||||
- title: "When saving cover images don't re-encode the image data unless absolutely neccessary. This prevents information loss due to JPEG re-compression"
|
||||
|
||||
- title: "Fix regression that broke setting of metadata for some MOBI/AZW/PRC files"
|
||||
|
||||
- title: "Fix regression in last release that could cause download of metadata for multiple files to only download the metadata for a few of them"
|
||||
tickets: [7071]
|
||||
|
||||
- title: "MOBI Output: More tweaking of the margin handling to yield results closer to the input document."
|
||||
|
||||
- title: "Device drivers: Fix regression that could cause geenration of invalid metadata.calibre cache files"
|
||||
|
||||
- title: "Fix saving to disk with ISBN in filename"
|
||||
tickets: [7090]
|
||||
|
||||
- title: "Fix another regression in the ISBNdb.com metadata download plugin"
|
||||
|
||||
- title: "Fix dragging to not interfere with multi-selection. Also dont allow drag and drop from the library to itself"
|
||||
|
||||
- title: "CHM input: handle another class of broken CHM files"
|
||||
tickets: [7058]
|
||||
|
||||
|
||||
new recipes:
|
||||
- title: "Communications of the Association for Computing Machinery"
|
||||
author: jonmisurda
|
||||
|
||||
- title: "Anand Tech"
|
||||
author: "Oliver Niesner"
|
||||
|
||||
- title: "gsp.ro"
|
||||
author: "bucsie"
|
||||
|
||||
- title: "Il Fatto Quotidiano"
|
||||
author: "egilh"
|
||||
|
||||
- title: "Serbian Literature blog and Rusia Hoy"
|
||||
author: "Darko Miletic"
|
||||
|
||||
- title: "Medscape"
|
||||
author: "Tony Stegall"
|
||||
|
||||
|
||||
improved recipes:
|
||||
- The Age
|
||||
- Australian
|
||||
- Wiki news
|
||||
- Times Online
|
||||
- New Yorker
|
||||
- Guardian
|
||||
- Sueddeutsche
|
||||
- HNA
|
||||
- Revista Muy Interesante
|
||||
|
||||
- version: 0.7.22
|
||||
date: 2010-10-03
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
font-size: 1.25em;
|
||||
border: 1px solid black;
|
||||
text-color: black;
|
||||
text-decoration: none;
|
||||
margin-right: 0.5em;
|
||||
background-color: #ddd;
|
||||
border-top: 1px solid ThreeDLightShadow;
|
||||
border-right: 1px solid ButtonShadow;
|
||||
@ -70,6 +72,7 @@ div.navigation {
|
||||
padding-right: 0em;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#logo {
|
||||
|
@ -38,7 +38,7 @@ class Guardian(BasicNewsRecipe):
|
||||
dict(name='div', attrs={'id':["article-toolbox","subscribe-feeds",]}),
|
||||
dict(name='ul', attrs={'class':["pagination"]}),
|
||||
dict(name='ul', attrs={'id':["content-actions"]}),
|
||||
dict(name='img'),
|
||||
#dict(name='img'),
|
||||
]
|
||||
use_embedded_content = False
|
||||
|
||||
|
64
resources/recipes/medscape.recipe
Normal file
64
resources/recipes/medscape.recipe
Normal file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'Tony Stegall'
|
||||
__copyright__ = '2010, Tony Stegall or Tonythebookworm on mobileread.com'
|
||||
__version__ = '1'
|
||||
__date__ = '01, October 2010'
|
||||
__docformat__ = 'English'
|
||||
|
||||
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class MedScrape(BasicNewsRecipe):
|
||||
|
||||
title = 'MedScape'
|
||||
__author__ = 'Tony Stegall'
|
||||
description = 'Nursing News'
|
||||
language = 'en'
|
||||
timefmt = ' [%a, %d %b, %Y]'
|
||||
needs_subscription = True
|
||||
masthead_url = 'http://images.medscape.com/pi/global/header/sp/bg-sp-medscape.gif'
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
conversion_options = {'linearize_tables' : True}
|
||||
extra_css = '''
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||
|
||||
|
||||
p.authors{text-align:right; font-size:small;margin-top:0px;margin-bottom: 0px;}
|
||||
p.postingdate{text-align:right; font-size:small;margin-top:0px;margin-bottom: 0px;}
|
||||
h2{text-align:right; font-size:small;margin-top:0px;margin-bottom: 0px;}
|
||||
|
||||
|
||||
p{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||
'''
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':['closewindow2']}),
|
||||
dict(name='div', attrs={'id': ['basicheaderlinks']})
|
||||
]
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open('https://profreg.medscape.com/px/getlogin.do')
|
||||
br.select_form(name='LoginForm')
|
||||
br['userId'] = self.username
|
||||
br['password'] = self.password
|
||||
br.submit()
|
||||
return br
|
||||
|
||||
feeds = [
|
||||
('MedInfo', 'http://www.medscape.com/cx/rssfeeds/2685.xml'),
|
||||
]
|
||||
|
||||
def print_version(self,url):
|
||||
#the original url is: http://www.medscape.com/viewarticle/728955?src=rss
|
||||
#the print url is: http://www.medscape.com/viewarticle/728955_print
|
||||
print_url = url.partition('?')[0] +'_print'
|
||||
#print 'the printable version is: ',print_url
|
||||
return print_url
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(attrs={'style':True}):
|
||||
del item['style']
|
||||
return soup
|
@ -6,9 +6,9 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, re, cStringIO, base64, httplib, subprocess
|
||||
import os, re, cStringIO, base64, httplib, subprocess, hashlib, shutil
|
||||
from subprocess import check_call
|
||||
from tempfile import NamedTemporaryFile
|
||||
from tempfile import NamedTemporaryFile, mkdtemp
|
||||
|
||||
from setup import Command, __version__, installer_name, __appname__
|
||||
|
||||
@ -331,5 +331,19 @@ class UploadToServer(Command):
|
||||
%(__version__, DOWNLOADS), shell=True)
|
||||
check_call('ssh divok /etc/init.d/apache2 graceful',
|
||||
shell=True)
|
||||
tdir = mkdtemp()
|
||||
for installer in installers():
|
||||
if not os.path.exists(installer):
|
||||
continue
|
||||
with open(installer, 'rb') as f:
|
||||
raw = f.read()
|
||||
fingerprint = hashlib.sha512(raw).hexdigest()
|
||||
fname = os.path.basename(installer+'.sha512')
|
||||
with open(os.path.join(tdir, fname), 'wb') as f:
|
||||
f.write(fingerprint)
|
||||
check_call('scp %s/*.sha512 divok:%s/signatures/' % (tdir, DOWNLOADS),
|
||||
shell=True)
|
||||
shutil.rmtree(tdir)
|
||||
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.7.22'
|
||||
__version__ = '0.7.23'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
import re
|
||||
|
@ -259,6 +259,8 @@ class ITUNES(DriverBase):
|
||||
self.report_progress(1.0, _('Updating device metadata listing...'))
|
||||
|
||||
# Add new books to booklists[0]
|
||||
# Charles thinks this should be
|
||||
# for new_book in metadata[0]:
|
||||
for new_book in locations[0]:
|
||||
if DEBUG:
|
||||
self.log.info(" adding '%s' by '%s' to booklists[0]" %
|
||||
@ -1208,6 +1210,10 @@ class ITUNES(DriverBase):
|
||||
except:
|
||||
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0]))
|
||||
self.log.error(" error scaling '%s' for '%s'" % (metadata.cover,metadata.title))
|
||||
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
return thumb
|
||||
|
||||
if isosx:
|
||||
@ -2400,7 +2406,7 @@ class ITUNES(DriverBase):
|
||||
try:
|
||||
storage_path = os.path.split(cached_book['lib_book'].location().path)
|
||||
if cached_book['lib_book'].location().path.startswith(self.iTunes_media) and \
|
||||
not storage_path[0].startswith(self.calibre_library_path):
|
||||
not storage_path[0].startswith(prefs['library_path']):
|
||||
title_storage_path = storage_path[0]
|
||||
if DEBUG:
|
||||
self.log.info(" removing title_storage_path: %s" % title_storage_path)
|
||||
@ -2452,7 +2458,7 @@ class ITUNES(DriverBase):
|
||||
|
||||
if book:
|
||||
if self.iTunes_media and path.startswith(self.iTunes_media) and \
|
||||
not path.startswith(self.calibre_library_path):
|
||||
not path.startswith(prefs['library_path']):
|
||||
storage_path = os.path.split(path)
|
||||
if DEBUG:
|
||||
self.log.info(" removing '%s' at %s" %
|
||||
|
@ -417,14 +417,16 @@ class DevicePlugin(Plugin):
|
||||
select a specific plugboard. This method is called immediately before
|
||||
add_books and sync_booklists.
|
||||
|
||||
pb_func is a callable with the following signature:
|
||||
pb_func is a callable with the following signature::
|
||||
def pb_func(device_name, format, plugboards)
|
||||
|
||||
You give it the current device name (either the class name or
|
||||
DEVICE_PLUGBOARD_NAME), the format you are interested in (a 'real'
|
||||
format or 'device_db'), and the plugboards (you were given those by
|
||||
set_plugboards, the same place you got this method).
|
||||
|
||||
Return value: None or a single plugboard instance.
|
||||
:return: None or a single plugboard instance.
|
||||
|
||||
'''
|
||||
pass
|
||||
|
||||
|
@ -131,6 +131,7 @@ class InterfaceAction(QObject):
|
||||
Called whenever the current library is changed.
|
||||
|
||||
:param db: The LibraryDatabase corresponding to the current library.
|
||||
|
||||
'''
|
||||
pass
|
||||
|
||||
@ -148,6 +149,7 @@ class InterfaceAction(QObject):
|
||||
long periods of time.
|
||||
|
||||
:return: False to halt the shutdown. You are responsible for telling
|
||||
the user why the shutdown was halted.
|
||||
the user why the shutdown was halted.
|
||||
|
||||
'''
|
||||
return True
|
||||
|
@ -340,10 +340,10 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
m.start_metadata_backup()
|
||||
|
||||
def restore_database(self):
|
||||
info_dialog(self.gui, _('Recover database'),
|
||||
info_dialog(self.gui, _('Recover database'), '<p>'+
|
||||
_(
|
||||
'This command rebuilds your calibre database from the information '
|
||||
'stored by calibre in the OPF files.' + '<p>' +
|
||||
'stored by calibre in the OPF files.<p>'
|
||||
'This function is not currently available in the GUI. You can '
|
||||
'recover your database using the \'calibredb restore_database\' '
|
||||
'command line function.'
|
||||
|
@ -38,7 +38,10 @@ class CustomRecipeModel(QAbstractListModel):
|
||||
return False
|
||||
|
||||
def rowCount(self, *args):
|
||||
return len(self.recipe_model.custom_recipe_collection)
|
||||
try:
|
||||
return len(self.recipe_model.custom_recipe_collection)
|
||||
except:
|
||||
return 0
|
||||
|
||||
def data(self, index, role):
|
||||
if role == Qt.DisplayRole:
|
||||
@ -100,6 +103,8 @@ class UserProfiles(ResizableDialog, Ui_Dialog):
|
||||
|
||||
def break_cycles(self):
|
||||
self.recipe_model = self._model.recipe_model = None
|
||||
self.available_profiles = None
|
||||
self.model = self._model = None
|
||||
|
||||
def remove_selected_items(self):
|
||||
indices = self.available_profiles.selectionModel().selectedRows()
|
||||
|
@ -3,13 +3,14 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import traceback
|
||||
|
||||
from PyQt4.Qt import QThread, pyqtSignal, Qt, QUrl
|
||||
from PyQt4.Qt import QThread, pyqtSignal, Qt, QUrl, QDialog, QGridLayout, \
|
||||
QLabel, QCheckBox, QDialogButtonBox, QIcon, QPixmap
|
||||
import mechanize
|
||||
|
||||
from calibre.constants import __appname__, __version__, iswindows, isosx
|
||||
from calibre import browser
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.gui2 import config, dynamic, question_dialog, open_url
|
||||
from calibre.gui2 import config, dynamic, open_url
|
||||
|
||||
URL = 'http://status.calibre-ebook.com/latest'
|
||||
|
||||
@ -37,6 +38,53 @@ class CheckForUpdates(QThread):
|
||||
traceback.print_exc()
|
||||
self.sleep(self.INTERVAL)
|
||||
|
||||
class UpdateNotification(QDialog):
|
||||
|
||||
def __init__(self, version, parent=None):
|
||||
QDialog.__init__(self, parent)
|
||||
self.resize(400, 250)
|
||||
self.l = QGridLayout()
|
||||
self.setLayout(self.l)
|
||||
self.logo = QLabel()
|
||||
self.logo.setMaximumWidth(110)
|
||||
self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100,
|
||||
Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
|
||||
self.label = QLabel('<p>'+
|
||||
_('%s has been updated to version <b>%s</b>. '
|
||||
'See the <a href="http://calibre-ebook.com/whats-new'
|
||||
'">new features</a>. Visit the download pa'
|
||||
'ge?')%(__appname__, version))
|
||||
self.label.setOpenExternalLinks(True)
|
||||
self.label.setWordWrap(True)
|
||||
self.setWindowTitle(_('Update available!'))
|
||||
self.setWindowIcon(QIcon(I('lt.png')))
|
||||
self.l.addWidget(self.logo, 0, 0)
|
||||
self.l.addWidget(self.label, 0, 1)
|
||||
self.cb = QCheckBox(
|
||||
_('Show this notification for future updates'), self)
|
||||
self.l.addWidget(self.cb, 1, 0, 1, -1)
|
||||
self.cb.setChecked(config.get('new_version_notification'))
|
||||
self.cb.stateChanged.connect(self.show_future)
|
||||
self.bb = QDialogButtonBox(self)
|
||||
b = self.bb.addButton(_('&Get update'), self.bb.AcceptRole)
|
||||
b.setDefault(True)
|
||||
b.setIcon(QIcon(I('arrow-down.png')))
|
||||
self.bb.addButton(self.bb.Cancel)
|
||||
self.l.addWidget(self.bb, 2, 0, 1, -1)
|
||||
self.bb.accepted.connect(self.accept)
|
||||
self.bb.rejected.connect(self.reject)
|
||||
dynamic.set('update to version %s'%version, False)
|
||||
|
||||
def show_future(self, *args):
|
||||
config.set('new_version_notification', bool(self.cb.isChecked()))
|
||||
|
||||
def accept(self):
|
||||
url = 'http://calibre-ebook.com/download_'+\
|
||||
('windows' if iswindows else 'osx' if isosx else 'linux')
|
||||
open_url(QUrl(url))
|
||||
|
||||
QDialog.accept(self)
|
||||
|
||||
class UpdateMixin(object):
|
||||
|
||||
def __init__(self, opts):
|
||||
@ -53,15 +101,8 @@ class UpdateMixin(object):
|
||||
|
||||
if config.get('new_version_notification') and \
|
||||
dynamic.get('update to version %s'%version, True):
|
||||
if question_dialog(self, _('Update available'),
|
||||
_('%s has been updated to version %s. '
|
||||
'See the <a href="http://calibre-ebook.com/whats-new'
|
||||
'">new features</a>. Visit the download pa'
|
||||
'ge?')%(__appname__, version)):
|
||||
url = 'http://calibre-ebook.com/download_'+\
|
||||
('windows' if iswindows else 'osx' if isosx else 'linux')
|
||||
open_url(QUrl(url))
|
||||
dynamic.set('update to version %s'%version, False)
|
||||
|
||||
self._update_notification__ = UpdateNotification(version,
|
||||
parent=self)
|
||||
self._update_notification__.show()
|
||||
|
||||
|
||||
|
@ -958,17 +958,17 @@ def command_check_library(args, dbpath):
|
||||
|
||||
def restore_database_option_parser():
|
||||
parser = get_parser(_(
|
||||
'''
|
||||
%prog restore_database [options]
|
||||
'''\
|
||||
%prog restore_database [options]
|
||||
|
||||
Restore this database from the metadata stored in OPF files in each
|
||||
directory of the calibre library. This is useful if your metadata.db file
|
||||
has been corrupted.
|
||||
Restore this database from the metadata stored in OPF files in each
|
||||
directory of the calibre library. This is useful if your metadata.db file
|
||||
has been corrupted.
|
||||
|
||||
WARNING: This command completely regenerates your database. You will lose
|
||||
all saved searches, user categories, plugboards, stored per-book conversion
|
||||
settings, and custom recipes. Restored metadata will only be as accurate as
|
||||
what is found in the OPF files.
|
||||
WARNING: This command completely regenerates your database. You will lose
|
||||
all saved searches, user categories, plugboards, stored per-book conversion
|
||||
settings, and custom recipes. Restored metadata will only be as accurate as
|
||||
what is found in the OPF files.
|
||||
'''))
|
||||
|
||||
parser.add_option('-r', '--really-do-it', default=False, action='store_true',
|
||||
|
@ -121,7 +121,7 @@ def build_index(books, num, search, sort, order, start, total, url_base, CKEYS):
|
||||
book['id'], fmt)
|
||||
),
|
||||
CLASS('button'))
|
||||
s.tail = u'\u202f' #
|
||||
s.tail = u''
|
||||
last = s
|
||||
data.append(s)
|
||||
|
||||
|
@ -62,9 +62,9 @@ If you want the search to ignore upper/lowercase differences, uncheck the `Case
|
||||
|
||||
You can have |app| change the case of the result (information after the replace has happened) by choosing one of the functions from the `Apply function after replace` box. The operations available are:
|
||||
|
||||
*`Lower case` -- change all the characters in the field to lower case
|
||||
*`Upper case` -- change all the characters in the field to upper case
|
||||
*`Title case` -- capitalize each word in the result.
|
||||
* `Lower case` -- change all the characters in the field to lower case
|
||||
* `Upper case` -- change all the characters in the field to upper case
|
||||
* `Title case` -- capitalize each word in the result.
|
||||
|
||||
The `Your test` box is provided for you to enter text to check that search/replace is doing what you want. In the majority of cases the book test boxes will be sufficient, but it is possible that there is a case you want to check that isn't shown in these boxes. Enter that case into `Your test`.
|
||||
|
||||
|
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
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