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
08b3a29292
63
resources/recipes/auto.recipe
Normal file
63
resources/recipes/auto.recipe
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'GabrieleMarini, based on Darko Miletic'
|
||||||
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>, Gabriele Marini'
|
||||||
|
__version__ = 'v1.02 Marini Gabriele '
|
||||||
|
__date__ = '14062010'
|
||||||
|
__description__ = 'Italian daily newspaper'
|
||||||
|
|
||||||
|
'''
|
||||||
|
http://www.corrieredellosport.it/
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Auto(BasicNewsRecipe):
|
||||||
|
__author__ = 'Gabriele Marini'
|
||||||
|
description = 'Auto and Formula 1'
|
||||||
|
|
||||||
|
cover_url = 'http://www.auto.it/res/imgs/logo_Auto.png'
|
||||||
|
|
||||||
|
|
||||||
|
title = u'Auto'
|
||||||
|
publisher = 'CONTE Editore'
|
||||||
|
category = 'Sport'
|
||||||
|
|
||||||
|
language = 'it'
|
||||||
|
timefmt = '[%a, %d %b, %Y]'
|
||||||
|
|
||||||
|
oldest_article = 60
|
||||||
|
max_articles_per_feed = 30
|
||||||
|
use_embedded_content = False
|
||||||
|
recursion = 10
|
||||||
|
|
||||||
|
remove_javascript = True
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
|
html2lrf_options = [
|
||||||
|
'--comment', description
|
||||||
|
, '--category', category
|
||||||
|
, '--publisher', publisher
|
||||||
|
, '--ignore-tables'
|
||||||
|
]
|
||||||
|
|
||||||
|
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='h2', attrs={'class':['tit_Article y_Txt']}),
|
||||||
|
dict(name='h2', attrs={'class':['tit_Article']}),
|
||||||
|
dict(name='div', attrs={'class':['box_Img newsdet_new ']}),
|
||||||
|
dict(name='div', attrs={'class':['box_Img newsdet_as ']}),
|
||||||
|
dict(name='table', attrs={'class':['table_A']}),
|
||||||
|
dict(name='div', attrs={'class':['txt_Article txtBox_cms']}),
|
||||||
|
dict(name='testoscheda')]
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Tutte le News' , u'http://www.auto.it/rss/articoli.xml' ),
|
||||||
|
(u'Prove su Strada' , u'http://www.auto.it/rss/prove+6.xml'),
|
||||||
|
(u'Novit\xe0' , u'http://www.auto.it/rss/novita+3.xml')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
60
resources/recipes/corriere_dello_sport.recipe
Normal file
60
resources/recipes/corriere_dello_sport.recipe
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'GabrieleMarini, based on Darko Miletic'
|
||||||
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>, Gabriele Marini'
|
||||||
|
__version__ = ' '
|
||||||
|
__date__ = '14-06-2010'
|
||||||
|
__description__ = 'Italian daily newspaper'
|
||||||
|
|
||||||
|
'''
|
||||||
|
http://www.corrieredellosport.it/
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class ilCorrieredelloSport(BasicNewsRecipe):
|
||||||
|
__author__ = 'Gabriele Marini'
|
||||||
|
description = 'Italian daily newspaper'
|
||||||
|
|
||||||
|
cover_url = 'http://edicola.corrieredellosport.it/newsmem/corsport/prima/nazionale_prima.jpg'
|
||||||
|
|
||||||
|
|
||||||
|
title = u'Il Corriere dello Sport'
|
||||||
|
publisher = 'CORRIERE DELLO SPORT s.r.l. '
|
||||||
|
category = 'Sport'
|
||||||
|
|
||||||
|
language = 'it'
|
||||||
|
timefmt = '[%a, %d %b, %Y]'
|
||||||
|
|
||||||
|
oldest_article = 10
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
use_embedded_content = False
|
||||||
|
recursion = 10
|
||||||
|
|
||||||
|
remove_javascript = True
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
|
html2lrf_options = [
|
||||||
|
'--comment', description
|
||||||
|
, '--category', category
|
||||||
|
, '--publisher', publisher
|
||||||
|
, '--ignore-tables'
|
||||||
|
]
|
||||||
|
|
||||||
|
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='h1', attrs={'class':['tit_Article']}),
|
||||||
|
dict(name='h1', attrs={'class':['tit_Article_mondiali']}),
|
||||||
|
dict(name='div', attrs={'class':['box_Img']}),
|
||||||
|
dict(name='p', attrs={'class':['summary','text']})]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Primo Piano' , u'http://www.corrieredellosport.it/rss/primo_piano.xml' ),
|
||||||
|
(u'Calcio' , u'http://www.corrieredellosport.it/rss/Calcio-3.xml'),
|
||||||
|
(u'Formula 1' , u'http://www.corrieredellosport.it/rss/Formula-1-7.xml'),
|
||||||
|
(u'Moto' , u'http://www.corrieredellosport.it/rss/Moto-8.xml'),
|
||||||
|
(u'Piu visti' , u'http://www.corrieredellosport.it/rss/piu_visti.xml')
|
||||||
|
]
|
@ -7,12 +7,33 @@ __docformat__ = 'restructuredtext en'
|
|||||||
'''
|
'''
|
||||||
Device driver for Amazon's Kindle
|
Device driver for Amazon's Kindle
|
||||||
'''
|
'''
|
||||||
import datetime, os, re, sys
|
import datetime, os, re, sys, json, hashlib
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from struct import unpack
|
from struct import unpack
|
||||||
|
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
|
'''
|
||||||
|
Notes on collections:
|
||||||
|
|
||||||
|
A collections cache is stored at system/collections.json
|
||||||
|
The cache is read only, changes made to it are overwritten (it is regenerated)
|
||||||
|
on device disconnect
|
||||||
|
|
||||||
|
A log of collection creation/manipulation is available at
|
||||||
|
system/userannotationlog
|
||||||
|
|
||||||
|
collections.json refers to books via a SHA1 hash of the absolute path to the
|
||||||
|
book (prefix is /mnt/us on my Kindle). The SHA1 hash may or may not be prefixed
|
||||||
|
by some characters, use the last 40 characters.
|
||||||
|
|
||||||
|
Changing the metadata and resending the file doesn't seem to affect collections
|
||||||
|
|
||||||
|
Adding a book to a collection on the Kindle does not change the book file at all
|
||||||
|
(i.e. it is binary identical). Therefore collection information is not stored in
|
||||||
|
file metadata.
|
||||||
|
'''
|
||||||
|
|
||||||
class KINDLE(USBMS):
|
class KINDLE(USBMS):
|
||||||
|
|
||||||
name = 'Kindle Device Interface'
|
name = 'Kindle Device Interface'
|
||||||
@ -60,6 +81,7 @@ class KINDLE(USBMS):
|
|||||||
'replace')
|
'replace')
|
||||||
return mi
|
return mi
|
||||||
|
|
||||||
|
|
||||||
def get_annotations(self, path_map):
|
def get_annotations(self, path_map):
|
||||||
MBP_FORMATS = [u'azw', u'mobi', u'prc', u'txt']
|
MBP_FORMATS = [u'azw', u'mobi', u'prc', u'txt']
|
||||||
mbp_formats = set(MBP_FORMATS)
|
mbp_formats = set(MBP_FORMATS)
|
||||||
@ -150,6 +172,37 @@ class KINDLE2(KINDLE):
|
|||||||
PRODUCT_ID = [0x0002]
|
PRODUCT_ID = [0x0002]
|
||||||
BCD = [0x0100]
|
BCD = [0x0100]
|
||||||
|
|
||||||
|
def books(self, oncard=None, end_session=True):
|
||||||
|
bl = USBMS.books(self, oncard=oncard, end_session=end_session)
|
||||||
|
# Read collections information
|
||||||
|
collections = os.path.join(self._main_prefix, 'system', 'collections.json')
|
||||||
|
if os.access(collections, os.R_OK):
|
||||||
|
try:
|
||||||
|
self.kindle_update_booklist(bl, collections)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return bl
|
||||||
|
|
||||||
|
def kindle_update_booklist(self, bl, collections):
|
||||||
|
with open(collections, 'rb') as f:
|
||||||
|
collections = f.read()
|
||||||
|
collections = json.loads(collections)
|
||||||
|
path_map = {}
|
||||||
|
for name, val in collections.items():
|
||||||
|
col = name.split('@')[0]
|
||||||
|
items = val.get('items', [])
|
||||||
|
for x in items:
|
||||||
|
x = x[-40:]
|
||||||
|
if x not in path_map:
|
||||||
|
path_map[x] = set([])
|
||||||
|
path_map[x].add(col)
|
||||||
|
if path_map:
|
||||||
|
for book in bl:
|
||||||
|
path = '/mnt/us/'+book.lpath
|
||||||
|
h = hashlib.sha1(path).hexdigest()
|
||||||
|
if h in path_map:
|
||||||
|
book.device_collections = list(sorted(path_map[h]))
|
||||||
|
|
||||||
class KINDLE_DX(KINDLE2):
|
class KINDLE_DX(KINDLE2):
|
||||||
|
|
||||||
|
@ -10,12 +10,31 @@ import os
|
|||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
|
||||||
from calibre.customize import FileTypePlugin
|
from calibre.customize import FileTypePlugin
|
||||||
|
from calibre.utils.zipfile import ZipFile, stringFileHeader
|
||||||
|
|
||||||
def is_comic(list_of_names):
|
def is_comic(list_of_names):
|
||||||
extensions = set([x.rpartition('.')[-1].lower() for x in list_of_names])
|
extensions = set([x.rpartition('.')[-1].lower() for x in list_of_names])
|
||||||
comic_extensions = set(['jpg', 'jpeg', 'png'])
|
comic_extensions = set(['jpg', 'jpeg', 'png'])
|
||||||
return len(extensions - comic_extensions) == 0
|
return len(extensions - comic_extensions) == 0
|
||||||
|
|
||||||
|
def archive_type(stream):
|
||||||
|
try:
|
||||||
|
pos = stream.tell()
|
||||||
|
except:
|
||||||
|
pos = 0
|
||||||
|
id_ = stream.read(4)
|
||||||
|
ans = None
|
||||||
|
if id_ == stringFileHeader:
|
||||||
|
ans = 'zip'
|
||||||
|
elif id_.startswith('Rar'):
|
||||||
|
ans = 'rar'
|
||||||
|
try:
|
||||||
|
stream.seek(pos)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
class ArchiveExtract(FileTypePlugin):
|
class ArchiveExtract(FileTypePlugin):
|
||||||
name = 'Archive Extract'
|
name = 'Archive Extract'
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
@ -31,7 +50,6 @@ class ArchiveExtract(FileTypePlugin):
|
|||||||
if is_rar:
|
if is_rar:
|
||||||
from calibre.libunrar import extract_member, names
|
from calibre.libunrar import extract_member, names
|
||||||
else:
|
else:
|
||||||
from calibre.utils.zipfile import ZipFile
|
|
||||||
zf = ZipFile(archive, 'r')
|
zf = ZipFile(archive, 'r')
|
||||||
|
|
||||||
if is_rar:
|
if is_rar:
|
||||||
|
@ -9,7 +9,7 @@ import os, collections
|
|||||||
|
|
||||||
from PyQt4.Qt import QLabel, QPixmap, QSize, QWidget, Qt, pyqtSignal, \
|
from PyQt4.Qt import QLabel, QPixmap, QSize, QWidget, Qt, pyqtSignal, \
|
||||||
QVBoxLayout, QScrollArea, QPropertyAnimation, QEasingCurve, \
|
QVBoxLayout, QScrollArea, QPropertyAnimation, QEasingCurve, \
|
||||||
QSizePolicy, QPainter, QRect, pyqtProperty
|
QSizePolicy, QPainter, QRect, pyqtProperty, QDesktopServices, QUrl
|
||||||
|
|
||||||
from calibre import fit_image, prepare_string_for_xml
|
from calibre import fit_image, prepare_string_for_xml
|
||||||
from calibre.gui2.widgets import IMAGE_EXTENSIONS
|
from calibre.gui2.widgets import IMAGE_EXTENSIONS
|
||||||
@ -42,14 +42,18 @@ def render_rows(data):
|
|||||||
txt = prepare_string_for_xml(txt)
|
txt = prepare_string_for_xml(txt)
|
||||||
if 'id' in data:
|
if 'id' in data:
|
||||||
if key == _('Path'):
|
if key == _('Path'):
|
||||||
txt = '...'+os.sep+os.sep.join(txt.split(os.sep)[-2:])
|
txt = u'<a href="path:%s" title="%s">%s</a>'%(data['id'],
|
||||||
txt = u'<a href="path:%s">%s</a>'%(data['id'],
|
txt, _('Click to open'))
|
||||||
_('Click to open'))
|
|
||||||
if key == _('Formats') and txt and txt != _('None'):
|
if key == _('Formats') and txt and txt != _('None'):
|
||||||
fmts = [x.strip() for x in txt.split(',')]
|
fmts = [x.strip() for x in txt.split(',')]
|
||||||
fmts = [u'<a href="format:%s:%s">%s</a>' % (data['id'], x, x) for x
|
fmts = [u'<a href="format:%s:%s">%s</a>' % (data['id'], x, x) for x
|
||||||
in fmts]
|
in fmts]
|
||||||
txt = ', '.join(fmts)
|
txt = ', '.join(fmts)
|
||||||
|
else:
|
||||||
|
if key == _('Path'):
|
||||||
|
txt = u'<a href="devpath:%s">%s</a>'%(txt,
|
||||||
|
_('Click to open'))
|
||||||
|
|
||||||
rows.append((key, txt))
|
rows.append((key, txt))
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
@ -150,6 +154,7 @@ class Label(QLabel):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QLabel.__init__(self)
|
QLabel.__init__(self)
|
||||||
|
self.setTextFormat(Qt.RichText)
|
||||||
self.setText('')
|
self.setText('')
|
||||||
self.setWordWrap(True)
|
self.setWordWrap(True)
|
||||||
self.linkActivated.connect(self.link_activated)
|
self.linkActivated.connect(self.link_activated)
|
||||||
@ -249,9 +254,13 @@ class BookDetails(QWidget):
|
|||||||
typ, _, val = link.partition(':')
|
typ, _, val = link.partition(':')
|
||||||
if typ == 'path':
|
if typ == 'path':
|
||||||
self.open_containing_folder.emit(int(val))
|
self.open_containing_folder.emit(int(val))
|
||||||
if typ == 'format':
|
elif typ == 'format':
|
||||||
id_, fmt = val.split(':')
|
id_, fmt = val.split(':')
|
||||||
self.view_specific_format.emit(int(id_), fmt)
|
self.view_specific_format.emit(int(id_), fmt)
|
||||||
|
elif typ == 'devpath':
|
||||||
|
path = os.path.dirname(val)
|
||||||
|
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
||||||
|
|
||||||
|
|
||||||
def mouseReleaseEvent(self, ev):
|
def mouseReleaseEvent(self, ev):
|
||||||
ev.accept()
|
ev.accept()
|
||||||
@ -263,6 +272,10 @@ class BookDetails(QWidget):
|
|||||||
def show_data(self, data):
|
def show_data(self, data):
|
||||||
self.cover_view.show_data(data)
|
self.cover_view.show_data(data)
|
||||||
self.book_info.show_data(data)
|
self.book_info.show_data(data)
|
||||||
|
self.setToolTip('<p>'+_('Click to open Book Details window') +
|
||||||
|
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), ''))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def reset_info(self):
|
def reset_info(self):
|
||||||
self.show_data({})
|
self.show_data({})
|
||||||
|
@ -5,7 +5,7 @@ import os
|
|||||||
|
|
||||||
from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
|
from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \
|
||||||
QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \
|
QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \
|
||||||
QPropertyAnimation, QEasingCurve
|
QPropertyAnimation, QEasingCurve, QDesktopServices, QUrl
|
||||||
|
|
||||||
|
|
||||||
from calibre import fit_image, preferred_encoding, isosx
|
from calibre import fit_image, preferred_encoding, isosx
|
||||||
@ -163,6 +163,10 @@ class BookInfoDisplay(QWidget):
|
|||||||
self.book_data.updateGeometry()
|
self.book_data.updateGeometry()
|
||||||
self.updateGeometry()
|
self.updateGeometry()
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
|
self.setToolTip('<p>'+_('Click to open Book Details window') +
|
||||||
|
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), ''))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class StatusBarInterface(object):
|
class StatusBarInterface(object):
|
||||||
|
|
||||||
@ -231,9 +235,13 @@ class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
|
|||||||
typ, _, val = link.partition(':')
|
typ, _, val = link.partition(':')
|
||||||
if typ == 'path':
|
if typ == 'path':
|
||||||
self.open_containing_folder.emit(int(val))
|
self.open_containing_folder.emit(int(val))
|
||||||
if typ == 'format':
|
elif typ == 'format':
|
||||||
id_, fmt = val.split(':')
|
id_, fmt = val.split(':')
|
||||||
self.view_specific_format.emit(int(id_), fmt)
|
self.view_specific_format.emit(int(id_), fmt)
|
||||||
|
elif typ == 'devpath':
|
||||||
|
path = os.path.dirname(val)
|
||||||
|
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
||||||
|
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
self.resized.emit(self.size())
|
self.resized.emit(self.size())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user