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
fa4e1b808f
18
recipes/daily_writing_tips.recipe
Normal file
18
recipes/daily_writing_tips.recipe
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class DailyWritingTips(BasicNewsRecipe):
|
||||||
|
title = u'Daily Writing Tips'
|
||||||
|
language = 'en_GB'
|
||||||
|
__author__ = 'NotTaken'
|
||||||
|
oldest_article = 7 #days
|
||||||
|
max_articles_per_feed = 40
|
||||||
|
use_embedded_content = True
|
||||||
|
no_stylesheets = True
|
||||||
|
auto_cleanup = False
|
||||||
|
encoding = 'utf-8'
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('Latest tips',
|
||||||
|
'http://feeds2.feedburner.com/DailyWritingTips'),
|
||||||
|
]
|
43
recipes/gs24_pl.recipe
Normal file
43
recipes/gs24_pl.recipe
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1322322819(BasicNewsRecipe):
|
||||||
|
title = u'GS24.pl (Głos Szczeciński)'
|
||||||
|
description = u'Internetowy serwis Głosu Szczecińskiego'
|
||||||
|
__author__ = u'Michał Szkutnik'
|
||||||
|
__license__ = u'GPL v3'
|
||||||
|
language = 'pl'
|
||||||
|
publisher = 'Media Regionalne sp. z o.o.'
|
||||||
|
category = 'news, szczecin'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
auto_cleanup = True
|
||||||
|
cover_url = "http://www.gs24.pl/images/top_logo.png"
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
# (u'Wszystko', u'http://www.gs24.pl/rss.xml'),
|
||||||
|
(u'Szczecin', u'http://www.gs24.pl/szczecin.xml'),
|
||||||
|
(u'Stargard', u'http://www.gs24.pl/stargard.xml'),
|
||||||
|
(u'Świnoujście', u'http://www.gs24.pl/swinoujscie.xml'),
|
||||||
|
(u'Goleniów', u'http://www.gs24.pl/goleniow.xml'),
|
||||||
|
(u'Gryfice', u'http://www.gs24.pl/gryfice.xml'),
|
||||||
|
(u'Kamień Pomorski', u'http://www.gs24.pl/kamienpomorski.xml'),
|
||||||
|
(u'Police', u'http://www.gs24.pl/police.xml'),
|
||||||
|
(u'Region', u'http://www.gs24.pl/region.xml'),
|
||||||
|
(u'Sport', u'http://www.gs24.pl/sport.xml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_article_url(self, article):
|
||||||
|
s = re.search("""/0L0S(gs24.*)/story01.htm""", article.link)
|
||||||
|
s = s.group(1)
|
||||||
|
replacements = { "0B" : ".", "0C" : "/", "0H" : ",", "0I" : "_", "0D" : "?", "0F" : "="}
|
||||||
|
for (a, b) in replacements.iteritems():
|
||||||
|
s = string.replace(s, a, b)
|
||||||
|
s = string.replace(s, "0A", "0")
|
||||||
|
return "http://"+s
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + "&Template=printpicart"
|
BIN
recipes/icons/skylife.png
Normal file
BIN
recipes/icons/skylife.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
@ -29,22 +29,7 @@ class RollingStones(BasicNewsRecipe):
|
|||||||
max_articles_per_feed = 25
|
max_articles_per_feed = 25
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
auto_cleanup = True
|
||||||
remove_javascript = True
|
|
||||||
#####################################################################################
|
|
||||||
# cleanup section #
|
|
||||||
#####################################################################################
|
|
||||||
keep_only_tags = [
|
|
||||||
dict(name='div', attrs={'class':['c65l']}),
|
|
||||||
dict(name='div', attrs={'id':['col1']}),
|
|
||||||
|
|
||||||
|
|
||||||
]
|
|
||||||
remove_tags = [
|
|
||||||
dict(name='div', attrs={'class': ['storyActions upper','storyActions lowerArticleNav']}),
|
|
||||||
dict(name='div', attrs={'id': ['comments','related']}),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'News', u'http://www.rollingstone.com/siteServices/rss/allNews'),
|
(u'News', u'http://www.rollingstone.com/siteServices/rss/allNews'),
|
||||||
@ -58,25 +43,7 @@ class RollingStones(BasicNewsRecipe):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_article_url(self, article):
|
def print_version(self, url):
|
||||||
return article.get('guid', None)
|
return url +'?print=true'
|
||||||
|
|
||||||
|
|
||||||
def append_page(self, soup, appendtag, position):
|
|
||||||
'''
|
|
||||||
Some are the articles are multipage so the below function
|
|
||||||
will get the articles that have <next>
|
|
||||||
'''
|
|
||||||
pager = soup.find('li',attrs={'class':'next'})
|
|
||||||
if pager:
|
|
||||||
nexturl = pager.a['href']
|
|
||||||
soup2 = self.index_to_soup(nexturl)
|
|
||||||
texttag = soup2.find('div', attrs={'id':'storyTextContainer'})
|
|
||||||
for it in texttag.findAll(style=True):
|
|
||||||
del it['style']
|
|
||||||
newpos = len(texttag.contents)
|
|
||||||
self.append_page(soup2,texttag,newpos)
|
|
||||||
texttag.extract()
|
|
||||||
appendtag.insert(position,texttag)
|
|
||||||
|
|
||||||
|
|
||||||
|
32
recipes/skylife.recipe
Normal file
32
recipes/skylife.recipe
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class THY (BasicNewsRecipe):
|
||||||
|
|
||||||
|
title = u'Skylife'
|
||||||
|
__author__ = u'thomass'
|
||||||
|
description = ' Türk Hava Yollarının yayınladığı aylık kültür dergisi (Fotoğrafları da içermesini isterseniz keep_only_tag''da belirttiğim kodu da ekleyin) '
|
||||||
|
oldest_article =32
|
||||||
|
max_articles_per_feed =100
|
||||||
|
no_stylesheets = True
|
||||||
|
#delay = 1
|
||||||
|
#use_embedded_content = False
|
||||||
|
encoding = 'utf-8'
|
||||||
|
publisher = 'thomass'
|
||||||
|
category = 'genel kültür, gezi,Türkçe'
|
||||||
|
language = 'tr'
|
||||||
|
publication_type = 'magazine'
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
keep_only_tags = [dict(name='h3', attrs={'id':['hpbaslik']}),dict(name='p', attrs={'id':['pyayin','hspot','picerik']})] #Fotoğrafları da eklemek için: dict(name='div', attrs={'id':['divResimler']})
|
||||||
|
masthead_url = 'http://www.turkishairlines.com/static/img/skylife/logo.png'
|
||||||
|
remove_empty_feeds= True
|
||||||
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
|
feeds = [( u'SKYLIFE', u'http://feed43.com/7783278414103376.xml')]
|
20
recipes/techdirt.recipe
Normal file
20
recipes/techdirt.recipe
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class TechDirt(BasicNewsRecipe):
|
||||||
|
title = u'Tech Dirt'
|
||||||
|
language = 'en'
|
||||||
|
__author__ = 'Krittika Goyal'
|
||||||
|
oldest_article = 7 #days
|
||||||
|
max_articles_per_feed = 25
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
auto_cleanup = True
|
||||||
|
encoding = 'latin1'
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('News',
|
||||||
|
'http://feeds.feedburner.com/techdirt/feed'),
|
||||||
|
]
|
||||||
|
|
98
recipes/vanityfair.recipe
Normal file
98
recipes/vanityfair.recipe
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
from datetime import date
|
||||||
|
import re
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class VanityFair(BasicNewsRecipe):
|
||||||
|
title = u"Vanity Fair"
|
||||||
|
description = 'Vanity Fair Magazine (U.S.)'
|
||||||
|
language = 'en'
|
||||||
|
__author__ = 'Barty'
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = False
|
||||||
|
auto_cleanup = False
|
||||||
|
timefmt = ' [%B %Y]'
|
||||||
|
oldest_article = 365
|
||||||
|
|
||||||
|
masthead_url = 'http://www.vanityfair.com/etc/designs/vanityfair/images/shell/print-logo.png'
|
||||||
|
|
||||||
|
INDEX = 'http://www.vanityfair.com'
|
||||||
|
CATEGORIES = [
|
||||||
|
# comment out categories you don't want
|
||||||
|
# (user friendly name, url suffix, max number of articles to load)
|
||||||
|
('Hollywood','hollywood',10),
|
||||||
|
('Culture','culture',10),
|
||||||
|
('Business','business',10),
|
||||||
|
('Politics','politics',10),
|
||||||
|
('Society','society',10),
|
||||||
|
('Style','style',10),
|
||||||
|
('VF Daily','online/daily',10),
|
||||||
|
("James Wolcott's Blog",'online/wolcott',10),
|
||||||
|
("The Oscars",'online/oscars',10),
|
||||||
|
]
|
||||||
|
# set this to False if you don't want to put the first article
|
||||||
|
# that appears in each section to a "Featured" section
|
||||||
|
FEATURED_CAT = True
|
||||||
|
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
{'name':['nav']},
|
||||||
|
{'class':re.compile(r'_(header|rubric|share|subnav|leaderboard)|comments-count|ecom_placement')}
|
||||||
|
]
|
||||||
|
remove_tags_after = [{'class':'cn_blogpost'},{'id':'wrapper'}]
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
self.cover_url = 'http://www.vanityfair.com/magazine/toc/contents-%s/_jcr_content/par/cn_contentwell/par-main/cn_pagination_contai/cn_image.size.cover_vanityfair_300.jpg' % (date.today().strftime('%Y%m'))
|
||||||
|
feeds = []
|
||||||
|
seen_urls = set([])
|
||||||
|
features = []
|
||||||
|
|
||||||
|
for category in self.CATEGORIES:
|
||||||
|
|
||||||
|
(cat_name, tag, max_articles) = category
|
||||||
|
self.log('Reading category:', cat_name)
|
||||||
|
articles = []
|
||||||
|
|
||||||
|
page = "%s/%s" % (self.INDEX, tag)
|
||||||
|
soup = self.index_to_soup(page)
|
||||||
|
headers = soup.findAll(attrs={'class':'headline '})
|
||||||
|
add_featured = self.FEATURED_CAT
|
||||||
|
|
||||||
|
for header in headers:
|
||||||
|
self.log(self.tag_to_string(header))
|
||||||
|
atags = header.findAll('a')
|
||||||
|
# if there's more than one a tag, it's some kind of list, skip
|
||||||
|
if not atags or len(atags)>1:
|
||||||
|
continue
|
||||||
|
atag = atags[0]
|
||||||
|
url = atag['href']
|
||||||
|
if url.startswith('/'):
|
||||||
|
url = self.INDEX + url
|
||||||
|
if url in seen_urls:
|
||||||
|
continue
|
||||||
|
seen_urls.add(url)
|
||||||
|
title = self.tag_to_string(atag)
|
||||||
|
self.log('\tFound article:', title)
|
||||||
|
self.log('\t', url)
|
||||||
|
par = header.findParent('article') if tag.startswith('online/') else header.findParent('section')
|
||||||
|
if par is not None:
|
||||||
|
desc = par.find(attrs={'class':'body '})
|
||||||
|
desc = self.tag_to_string(desc) if desc else ''
|
||||||
|
#self.log('\t', desc)
|
||||||
|
if add_featured:
|
||||||
|
features.append({'title':title,'url':url,'description':desc})
|
||||||
|
add_featured = False
|
||||||
|
else:
|
||||||
|
articles.append({'title':title,'url':url,'description':desc})
|
||||||
|
if len(articles) >= max_articles:
|
||||||
|
break
|
||||||
|
|
||||||
|
if articles:
|
||||||
|
feeds.append((cat_name, articles))
|
||||||
|
|
||||||
|
if features:
|
||||||
|
feeds.insert(0,('Featured', features))
|
||||||
|
|
||||||
|
return feeds
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url.replace('.html', '.print')
|
@ -545,7 +545,7 @@ from calibre.customize.profiles import input_profiles, output_profiles
|
|||||||
|
|
||||||
from calibre.devices.apple.driver import ITUNES
|
from calibre.devices.apple.driver import ITUNES
|
||||||
from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX, SPECTRA
|
from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX, SPECTRA
|
||||||
from calibre.devices.blackberry.driver import BLACKBERRY
|
from calibre.devices.blackberry.driver import BLACKBERRY, PLAYBOOK
|
||||||
from calibre.devices.cybook.driver import CYBOOK, ORIZON
|
from calibre.devices.cybook.driver import CYBOOK, ORIZON
|
||||||
from calibre.devices.eb600.driver import (EB600, COOL_ER, SHINEBOOK,
|
from calibre.devices.eb600.driver import (EB600, COOL_ER, SHINEBOOK,
|
||||||
POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK,
|
POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK,
|
||||||
@ -646,7 +646,7 @@ plugins += [
|
|||||||
plugins += [
|
plugins += [
|
||||||
HANLINV3,
|
HANLINV3,
|
||||||
HANLINV5,
|
HANLINV5,
|
||||||
BLACKBERRY,
|
BLACKBERRY, PLAYBOOK,
|
||||||
CYBOOK,
|
CYBOOK,
|
||||||
ORIZON,
|
ORIZON,
|
||||||
ILIAD,
|
ILIAD,
|
||||||
@ -1531,7 +1531,7 @@ class StoreWoblinkStore(StoreBase):
|
|||||||
actual_plugin = 'calibre.gui2.store.stores.woblink_plugin:WoblinkStore'
|
actual_plugin = 'calibre.gui2.store.stores.woblink_plugin:WoblinkStore'
|
||||||
|
|
||||||
headquarters = 'PL'
|
headquarters = 'PL'
|
||||||
formats = ['EPUB']
|
formats = ['EPUB', 'PDF', 'WOBLINK']
|
||||||
|
|
||||||
class XinXiiStore(StoreBase):
|
class XinXiiStore(StoreBase):
|
||||||
name = 'XinXii'
|
name = 'XinXii'
|
||||||
|
@ -39,15 +39,15 @@ def get_connected_device():
|
|||||||
if ok:
|
if ok:
|
||||||
dev = d
|
dev = d
|
||||||
dev.reset(log_packets=False, detected_device=det)
|
dev.reset(log_packets=False, detected_device=det)
|
||||||
connected_devices.append(dev)
|
connected_devices.append((det, dev))
|
||||||
|
|
||||||
if dev is None:
|
if dev is None:
|
||||||
print >>sys.stderr, 'Unable to find a connected ebook reader.'
|
print >>sys.stderr, 'Unable to find a connected ebook reader.'
|
||||||
return
|
return
|
||||||
|
|
||||||
for d in connected_devices:
|
for det, d in connected_devices:
|
||||||
try:
|
try:
|
||||||
d.open(None)
|
d.open(det, None)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
@ -121,7 +121,7 @@ def debug(ioreg_to_tmp=False, buf=None):
|
|||||||
out('Trying to open', dev.name, '...', end=' ')
|
out('Trying to open', dev.name, '...', end=' ')
|
||||||
try:
|
try:
|
||||||
dev.reset(detected_device=det)
|
dev.reset(detected_device=det)
|
||||||
dev.open(None)
|
dev.open(det, None)
|
||||||
out('OK')
|
out('OK')
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -167,12 +167,12 @@ class ANDROID(USBMS):
|
|||||||
'MB525', 'ANDROID2.3', 'SGH-I997', 'GT-I5800_CARD', 'MB612',
|
'MB525', 'ANDROID2.3', 'SGH-I997', 'GT-I5800_CARD', 'MB612',
|
||||||
'GT-S5830_CARD', 'GT-S5570_CARD', 'MB870', 'MID7015A',
|
'GT-S5830_CARD', 'GT-S5570_CARD', 'MB870', 'MID7015A',
|
||||||
'ALPANDIGITAL', 'ANDROID_MID', 'VTAB1008', 'EMX51_BBG_ANDROI',
|
'ALPANDIGITAL', 'ANDROID_MID', 'VTAB1008', 'EMX51_BBG_ANDROI',
|
||||||
'UMS', '.K080', 'P990', 'LTE']
|
'UMS', '.K080', 'P990', 'LTE', 'MB853', 'GT-S5660_CARD']
|
||||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
||||||
'__UMS_COMPOSITE', 'SGH-I997_CARD', 'MB870', 'ALPANDIGITAL',
|
'__UMS_COMPOSITE', 'SGH-I997_CARD', 'MB870', 'ALPANDIGITAL',
|
||||||
'ANDROID_MID', 'P990_SD_CARD', '.K080', 'LTE_CARD']
|
'ANDROID_MID', 'P990_SD_CARD', '.K080', 'LTE_CARD', 'MB853']
|
||||||
|
|
||||||
OSX_MAIN_MEM = 'Android Device Main Memory'
|
OSX_MAIN_MEM = 'Android Device Main Memory'
|
||||||
|
|
||||||
@ -199,6 +199,18 @@ class ANDROID(USBMS):
|
|||||||
dirs = list(map(aldiko_tweak, dirs))
|
dirs = list(map(aldiko_tweak, dirs))
|
||||||
return dirs
|
return dirs
|
||||||
|
|
||||||
|
def windows_sort_drives(self, drives):
|
||||||
|
try:
|
||||||
|
vid, pid, bcd = self.device_being_opened[:3]
|
||||||
|
except:
|
||||||
|
vid, pid, bcd = -1, -1, -1
|
||||||
|
if (vid, pid, bcd) == (0x0e79, 0x1408, 0x0222):
|
||||||
|
letter_a = drives.get('carda', None)
|
||||||
|
if letter_a is not None:
|
||||||
|
drives['carda'] = drives['main']
|
||||||
|
drives['main'] = letter_a
|
||||||
|
return drives
|
||||||
|
|
||||||
class S60(USBMS):
|
class S60(USBMS):
|
||||||
|
|
||||||
name = 'S60 driver'
|
name = 'S60 driver'
|
||||||
|
@ -808,7 +808,7 @@ class ITUNES(DriverBase):
|
|||||||
self.log.info("ITUNES.get_file(): exporting '%s'" % path)
|
self.log.info("ITUNES.get_file(): exporting '%s'" % path)
|
||||||
outfile.write(open(self.cached_books[path]['lib_book'].location().path).read())
|
outfile.write(open(self.cached_books[path]['lib_book'].location().path).read())
|
||||||
|
|
||||||
def open(self, library_uuid):
|
def open(self, connected_device, library_uuid):
|
||||||
'''
|
'''
|
||||||
Perform any device specific initialization. Called after the device is
|
Perform any device specific initialization. Called after the device is
|
||||||
detected but before any other functions that communicate with the device.
|
detected but before any other functions that communicate with the device.
|
||||||
@ -824,7 +824,7 @@ class ITUNES(DriverBase):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info("ITUNES.open()")
|
self.log.info("ITUNES.open(connected_device: %s)" % repr(connected_device))
|
||||||
|
|
||||||
# Display a dialog recommending using 'Connect to iTunes' if user hasn't
|
# Display a dialog recommending using 'Connect to iTunes' if user hasn't
|
||||||
# previously disabled the dialog
|
# previously disabled the dialog
|
||||||
@ -1306,6 +1306,7 @@ class ITUNES(DriverBase):
|
|||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" ITUNES._add_new_copy()")
|
self.log.info(" ITUNES._add_new_copy()")
|
||||||
|
|
||||||
|
if fpath.rpartition('.')[2].lower() == 'epub':
|
||||||
self._update_epub_metadata(fpath, metadata)
|
self._update_epub_metadata(fpath, metadata)
|
||||||
|
|
||||||
db_added = None
|
db_added = None
|
||||||
@ -3223,7 +3224,7 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
only_presence=False):
|
only_presence=False):
|
||||||
return self.connected, self
|
return self.connected, self
|
||||||
|
|
||||||
def open(self, library_uuid):
|
def open(self, connected_device, library_uuid):
|
||||||
'''
|
'''
|
||||||
Perform any device specific initialization. Called after the device is
|
Perform any device specific initialization. Called after the device is
|
||||||
detected but before any other functions that communicate with the device.
|
detected but before any other functions that communicate with the device.
|
||||||
@ -3238,7 +3239,7 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
we need to talk to iTunes to discover if there's a connected iPod
|
we need to talk to iTunes to discover if there's a connected iPod
|
||||||
'''
|
'''
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info("ITUNES_ASYNC.open()")
|
self.log.info("ITUNES_ASYNC.open(connected_device: %s)" % repr(connected_device))
|
||||||
|
|
||||||
# Confirm/create thumbs archive
|
# Confirm/create thumbs archive
|
||||||
if not os.path.exists(self.cache_dir):
|
if not os.path.exists(self.cache_dir):
|
||||||
|
@ -59,9 +59,9 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
def reset(self, key='-1', log_packets=False, report_progress=None,
|
def reset(self, key='-1', log_packets=False, report_progress=None,
|
||||||
detected_device=None) :
|
detected_device=None) :
|
||||||
self.open(None)
|
self.open(None, None)
|
||||||
|
|
||||||
def open(self, library_uuid):
|
def open(self, connected_device, library_uuid):
|
||||||
# Make sure the Bambook library is ready
|
# Make sure the Bambook library is ready
|
||||||
if not is_bambook_lib_ready():
|
if not is_bambook_lib_ready():
|
||||||
raise OpenFeedback(_("Unable to connect to Bambook, you need to install Bambook library first."))
|
raise OpenFeedback(_("Unable to connect to Bambook, you need to install Bambook library first."))
|
||||||
|
@ -28,3 +28,26 @@ class BLACKBERRY(USBMS):
|
|||||||
|
|
||||||
EBOOK_DIR_MAIN = 'eBooks'
|
EBOOK_DIR_MAIN = 'eBooks'
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
class PLAYBOOK(USBMS):
|
||||||
|
|
||||||
|
name = 'Blackberry Playbook Interface'
|
||||||
|
gui_name = 'Playbook'
|
||||||
|
description = _('Communicate with the Blackberry playbook.')
|
||||||
|
author = _('Kovid Goyal')
|
||||||
|
supported_platforms = ['windows', 'linux', 'osx']
|
||||||
|
|
||||||
|
# Ordered list of supported formats
|
||||||
|
FORMATS = ['epub']
|
||||||
|
|
||||||
|
VENDOR_ID = [0x0fca]
|
||||||
|
PRODUCT_ID = [0x8010]
|
||||||
|
BCD = [0x1]
|
||||||
|
|
||||||
|
VENDOR_NAME = 'GENERIC-'
|
||||||
|
WINDOWS_MAIN_MEM = 'MULTI-CARD'
|
||||||
|
|
||||||
|
MAIN_MEMORY_VOLUME_LABEL = 'Blackberry'
|
||||||
|
|
||||||
|
EBOOK_DIR_MAIN = 'media/books'
|
||||||
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
@ -79,7 +79,7 @@ class FOLDER_DEVICE(USBMS):
|
|||||||
only_presence=False):
|
only_presence=False):
|
||||||
return self.is_connected, self
|
return self.is_connected, self
|
||||||
|
|
||||||
def open(self, library_uuid):
|
def open(self, connected_device, library_uuid):
|
||||||
self.current_library_uuid = library_uuid
|
self.current_library_uuid = library_uuid
|
||||||
if not self._main_prefix:
|
if not self._main_prefix:
|
||||||
return False
|
return False
|
||||||
|
@ -175,7 +175,7 @@ class ODYSSEY(N516):
|
|||||||
|
|
||||||
FORMATS = ['epub', 'fb2', 'html', 'pdf', 'txt']
|
FORMATS = ['epub', 'fb2', 'html', 'pdf', 'txt']
|
||||||
|
|
||||||
EBOOK_DIR_MAIN = 'calibre'
|
EBOOK_DIR_MAIN = 'Digital Editions'
|
||||||
|
|
||||||
def get_main_ebook_dir(self, for_upload=False):
|
def get_main_ebook_dir(self, for_upload=False):
|
||||||
if for_upload:
|
if for_upload:
|
||||||
|
@ -133,8 +133,14 @@ class DevicePlugin(Plugin):
|
|||||||
if debug:
|
if debug:
|
||||||
self.print_usb_device_info(device_id)
|
self.print_usb_device_info(device_id)
|
||||||
if only_presence or self.can_handle_windows(device_id, debug=debug):
|
if only_presence or self.can_handle_windows(device_id, debug=debug):
|
||||||
return True
|
try:
|
||||||
return False
|
bcd = int(device_id.rpartition(
|
||||||
|
'rev_')[-1].replace(':', 'a'), 16)
|
||||||
|
except:
|
||||||
|
bcd = None
|
||||||
|
return True, (vendor_id, product_id, bcd, None,
|
||||||
|
None, None)
|
||||||
|
return False, None
|
||||||
|
|
||||||
def test_bcd(self, bcdDevice, bcd):
|
def test_bcd(self, bcdDevice, bcd):
|
||||||
if bcd is None or len(bcd) == 0:
|
if bcd is None or len(bcd) == 0:
|
||||||
@ -154,7 +160,7 @@ class DevicePlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
if iswindows:
|
if iswindows:
|
||||||
return self.is_usb_connected_windows(devices_on_system,
|
return self.is_usb_connected_windows(devices_on_system,
|
||||||
debug=debug, only_presence=only_presence), None
|
debug=debug, only_presence=only_presence)
|
||||||
|
|
||||||
vendors_on_system = set([x[0] for x in devices_on_system])
|
vendors_on_system = set([x[0] for x in devices_on_system])
|
||||||
vendors = self.VENDOR_ID if hasattr(self.VENDOR_ID, '__len__') else [self.VENDOR_ID]
|
vendors = self.VENDOR_ID if hasattr(self.VENDOR_ID, '__len__') else [self.VENDOR_ID]
|
||||||
@ -224,7 +230,7 @@ class DevicePlugin(Plugin):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def open(self, library_uuid):
|
def open(self, connected_device, library_uuid):
|
||||||
'''
|
'''
|
||||||
Perform any device specific initialization. Called after the device is
|
Perform any device specific initialization. Called after the device is
|
||||||
detected but before any other functions that communicate with the device.
|
detected but before any other functions that communicate with the device.
|
||||||
@ -238,6 +244,16 @@ class DevicePlugin(Plugin):
|
|||||||
|
|
||||||
This method can raise an OpenFeedback exception to display a message to
|
This method can raise an OpenFeedback exception to display a message to
|
||||||
the user.
|
the user.
|
||||||
|
|
||||||
|
:param connected_device: The device that we are trying to open. It is
|
||||||
|
a tuple of (vendor id, product id, bcd, manufacturer name, product
|
||||||
|
name, device serial number). However, some device have no serial number
|
||||||
|
and on windows only the first three fields are present, the rest are
|
||||||
|
None.
|
||||||
|
|
||||||
|
:param library_uuid: The UUID of the current calibre library. Can be
|
||||||
|
None if there is no library (for example when used from the command
|
||||||
|
line.
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -205,15 +205,15 @@ def main():
|
|||||||
if ok:
|
if ok:
|
||||||
dev = d
|
dev = d
|
||||||
dev.reset(log_packets=options.log_packets, detected_device=det)
|
dev.reset(log_packets=options.log_packets, detected_device=det)
|
||||||
connected_devices.append(dev)
|
connected_devices.append((det, dev))
|
||||||
|
|
||||||
if dev is None:
|
if dev is None:
|
||||||
print >>sys.stderr, 'Unable to find a connected ebook reader.'
|
print >>sys.stderr, 'Unable to find a connected ebook reader.'
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
for d in connected_devices:
|
for det, d in connected_devices:
|
||||||
try:
|
try:
|
||||||
d.open(None)
|
d.open(det, None)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
@ -240,7 +240,7 @@ class PRS500(DeviceConfig, DevicePlugin):
|
|||||||
def set_progress_reporter(self, report_progress):
|
def set_progress_reporter(self, report_progress):
|
||||||
self.report_progress = report_progress
|
self.report_progress = report_progress
|
||||||
|
|
||||||
def open(self, library_uuid) :
|
def open(self, connected_device, library_uuid) :
|
||||||
"""
|
"""
|
||||||
Claim an interface on the device for communication.
|
Claim an interface on the device for communication.
|
||||||
Requires write privileges to the device file.
|
Requires write privileges to the device file.
|
||||||
|
@ -847,9 +847,11 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
self._card_b_prefix = None
|
self._card_b_prefix = None
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
def open(self, library_uuid):
|
def open(self, connected_device, library_uuid):
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
|
self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
|
||||||
|
self.device_being_opened = connected_device
|
||||||
|
try:
|
||||||
if islinux:
|
if islinux:
|
||||||
try:
|
try:
|
||||||
self.open_linux()
|
self.open_linux()
|
||||||
@ -879,6 +881,8 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
self.current_library_uuid = library_uuid
|
self.current_library_uuid = library_uuid
|
||||||
self.post_open_callback()
|
self.post_open_callback()
|
||||||
|
finally:
|
||||||
|
self.device_being_opened = None
|
||||||
|
|
||||||
def post_open_callback(self):
|
def post_open_callback(self):
|
||||||
pass
|
pass
|
||||||
|
@ -132,9 +132,11 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
|
|
||||||
def upshift_markup(self): # {{{
|
def upshift_markup(self): # {{{
|
||||||
'Upgrade markup to comply with XHTML 1.1 where possible'
|
'Upgrade markup to comply with XHTML 1.1 where possible'
|
||||||
from calibre.ebooks.oeb.base import XPath
|
from calibre.ebooks.oeb.base import XPath, XML
|
||||||
for x in self.oeb.spine:
|
for x in self.oeb.spine:
|
||||||
root = x.data
|
root = x.data
|
||||||
|
if (not root.get(XML('lang'))) and (root.get('lang')):
|
||||||
|
root.set(XML('lang'), root.get('lang'))
|
||||||
body = XPath('//h:body')(root)
|
body = XPath('//h:body')(root)
|
||||||
if body:
|
if body:
|
||||||
body = body[0]
|
body = body[0]
|
||||||
|
@ -741,6 +741,14 @@ if __name__ == '__main__': # tests {{{
|
|||||||
isbn_test, title_test, authors_test)
|
isbn_test, title_test, authors_test)
|
||||||
com_tests = [ # {{{
|
com_tests = [ # {{{
|
||||||
|
|
||||||
|
( # # in title
|
||||||
|
{'title':'Expert C# 2008 Business Objects',
|
||||||
|
'authors':['Lhotka']},
|
||||||
|
[title_test('Expert C# 2008 Business Objects', exact=True),
|
||||||
|
authors_test(['Rockford Lhotka'])
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
( # Description has links
|
( # Description has links
|
||||||
{'identifiers':{'isbn': '9780671578275'}},
|
{'identifiers':{'isbn': '9780671578275'}},
|
||||||
[title_test('A Civil Campaign: A Comedy of Biology and Manners',
|
[title_test('A Civil Campaign: A Comedy of Biology and Manners',
|
||||||
|
@ -121,7 +121,18 @@ def cap_author_token(token):
|
|||||||
# Normalize tokens of the form J.K. to J. K.
|
# Normalize tokens of the form J.K. to J. K.
|
||||||
parts = token.split('.')
|
parts = token.split('.')
|
||||||
return '. '.join(map(capitalize, parts)).strip()
|
return '. '.join(map(capitalize, parts)).strip()
|
||||||
|
scots_name = None
|
||||||
|
for x in ('mc', 'mac'):
|
||||||
|
if (token.lower().startswith(x) and len(token) > len(x) and
|
||||||
|
(
|
||||||
|
token[len(x)] == upper(token[len(x)]) or
|
||||||
|
lt == token
|
||||||
|
)):
|
||||||
|
scots_name = len(x)
|
||||||
|
break
|
||||||
ans = capitalize(token)
|
ans = capitalize(token)
|
||||||
|
if scots_name is not None:
|
||||||
|
ans = ans[:scots_name] + upper(ans[scots_name]) + ans[scots_name+1:]
|
||||||
for x in ('-', "'"):
|
for x in ('-', "'"):
|
||||||
idx = ans.find(x)
|
idx = ans.find(x)
|
||||||
if idx > -1 and len(ans) > idx+2:
|
if idx > -1 and len(ans) > idx+2:
|
||||||
@ -333,7 +344,7 @@ class Source(Plugin):
|
|||||||
# Remove single quotes not followed by 's'
|
# Remove single quotes not followed by 's'
|
||||||
(r"'(?!s)", ''),
|
(r"'(?!s)", ''),
|
||||||
# Replace other special chars with a space
|
# Replace other special chars with a space
|
||||||
(r'''[:,;+!@#$%^&*(){}.`~"\s\[\]/]''', ' ')
|
(r'''[:,;+!@$%^&*(){}.`~"\s\[\]/]''', ' '),
|
||||||
]]
|
]]
|
||||||
|
|
||||||
for pat, repl in title_patterns:
|
for pat, repl in title_patterns:
|
||||||
|
@ -539,7 +539,8 @@ class Style(object):
|
|||||||
'Return value in pts'
|
'Return value in pts'
|
||||||
if base is None:
|
if base is None:
|
||||||
base = self.width
|
base = self.width
|
||||||
font = font or self.fontSize
|
if not font and font != 0:
|
||||||
|
font = self.fontSize
|
||||||
return unit_convert(value, base, font, self._profile.dpi)
|
return unit_convert(value, base, font, self._profile.dpi)
|
||||||
|
|
||||||
def pt_to_px(self, value):
|
def pt_to_px(self, value):
|
||||||
|
@ -283,7 +283,10 @@ class CSSFlattener(object):
|
|||||||
psize = fsize
|
psize = fsize
|
||||||
elif 'font-size' in cssdict or tag == 'body':
|
elif 'font-size' in cssdict or tag == 'body':
|
||||||
fsize = self.fmap[font_size]
|
fsize = self.fmap[font_size]
|
||||||
|
try:
|
||||||
cssdict['font-size'] = "%0.5fem" % (fsize / psize)
|
cssdict['font-size'] = "%0.5fem" % (fsize / psize)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
cssdict['font-size'] = '%.1fpt'%fsize
|
||||||
psize = fsize
|
psize = fsize
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -162,7 +162,7 @@ class DeviceManager(Thread): # {{{
|
|||||||
try:
|
try:
|
||||||
dev.reset(detected_device=detected_device,
|
dev.reset(detected_device=detected_device,
|
||||||
report_progress=self.report_progress)
|
report_progress=self.report_progress)
|
||||||
dev.open(self.current_library_uuid)
|
dev.open(detected_device, self.current_library_uuid)
|
||||||
except OpenFeedback as e:
|
except OpenFeedback as e:
|
||||||
if dev not in self.ejected_devices:
|
if dev not in self.ejected_devices:
|
||||||
self.open_feedback_msg(dev.get_gui_name(), e)
|
self.open_feedback_msg(dev.get_gui_name(), e)
|
||||||
|
@ -164,7 +164,14 @@ class MyBlockingBusy(QDialog): # {{{
|
|||||||
self.db.set_title(id, titlecase(title), notify=False)
|
self.db.set_title(id, titlecase(title), notify=False)
|
||||||
if do_title_sort:
|
if do_title_sort:
|
||||||
title = self.db.title(id, index_is_id=True)
|
title = self.db.title(id, index_is_id=True)
|
||||||
self.db.set_title_sort(id, title_sort(title), notify=False)
|
if languages:
|
||||||
|
lang = languages[0]
|
||||||
|
else:
|
||||||
|
lang = self.db.languages(id, index_is_id=True)
|
||||||
|
if lang:
|
||||||
|
lang = lang.partition(',')[0]
|
||||||
|
self.db.set_title_sort(id, title_sort(title, lang=lang),
|
||||||
|
notify=False)
|
||||||
if au:
|
if au:
|
||||||
self.db.set_authors(id, string_to_authors(au), notify=False)
|
self.db.set_authors(id, string_to_authors(au), notify=False)
|
||||||
if cover_action == 'remove':
|
if cover_action == 'remove':
|
||||||
@ -736,6 +743,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
else:
|
else:
|
||||||
flags = re.I
|
flags = re.I
|
||||||
|
|
||||||
|
flags |= re.UNICODE
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.search_mode.currentIndex() == 0:
|
if self.search_mode.currentIndex() == 0:
|
||||||
self.s_r_obj = re.compile(re.escape(unicode(self.search_for.text())), flags)
|
self.s_r_obj = re.compile(re.escape(unicode(self.search_for.text())), flags)
|
||||||
|
@ -22,7 +22,8 @@ from calibre.gui2.store.stores.mobileread.store_dialog import MobileReadStoreDia
|
|||||||
|
|
||||||
class MobileReadStore(BasicStoreConfig, StorePlugin):
|
class MobileReadStore(BasicStoreConfig, StorePlugin):
|
||||||
|
|
||||||
def genesis(self):
|
def __init__(self, *args, **kwargs):
|
||||||
|
StorePlugin.__init__(self, *args, **kwargs)
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
|
|
||||||
def open(self, parent=None, detail_item=None, external=False):
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
@ -56,7 +57,8 @@ class MobileReadStore(BasicStoreConfig, StorePlugin):
|
|||||||
book.drm = SearchResult.DRM_UNLOCKED
|
book.drm = SearchResult.DRM_UNLOCKED
|
||||||
yield book
|
yield book
|
||||||
|
|
||||||
def update_cache(self, parent=None, timeout=10, force=False, suppress_progress=False):
|
def update_cache(self, parent=None, timeout=10, force=False,
|
||||||
|
suppress_progress=False):
|
||||||
if self.lock.acquire(False):
|
if self.lock.acquire(False):
|
||||||
try:
|
try:
|
||||||
update_thread = CacheUpdateThread(self.config, self.seralize_books, timeout)
|
update_thread = CacheUpdateThread(self.config, self.seralize_books, timeout)
|
||||||
|
@ -61,6 +61,12 @@ class WoblinkStore(BasicStoreConfig, StorePlugin):
|
|||||||
price = ''.join(data.xpath('.//div[@class="prices"]/p[1]/span/text()'))
|
price = ''.join(data.xpath('.//div[@class="prices"]/p[1]/span/text()'))
|
||||||
price = re.sub('PLN', ' zł', price)
|
price = re.sub('PLN', ' zł', price)
|
||||||
price = re.sub('\.', ',', price)
|
price = re.sub('\.', ',', price)
|
||||||
|
formats = ', '.join(data.xpath('.//p[3]/img/@src'))
|
||||||
|
formats = formats[8:-4].upper()
|
||||||
|
if formats == 'EPUB':
|
||||||
|
formats = 'WOBLINK'
|
||||||
|
if 'E Ink' in data.xpath('.//div[@class="prices"]/img/@title'):
|
||||||
|
formats += ', EPUB'
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
@ -71,6 +77,6 @@ class WoblinkStore(BasicStoreConfig, StorePlugin):
|
|||||||
s.price = price
|
s.price = price
|
||||||
s.detail_item = id.strip()
|
s.detail_item = id.strip()
|
||||||
s.drm = SearchResult.DRM_LOCKED
|
s.drm = SearchResult.DRM_LOCKED
|
||||||
s.formats = 'EPUB'
|
s.formats = formats
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
@ -195,7 +195,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
|
|
||||||
for ac in self.iactions.values():
|
for ac in self.iactions.values():
|
||||||
ac.do_genesis()
|
ac.do_genesis()
|
||||||
self.donate_action = QAction(QIcon(I('donate.png')), _('&Donate to support calibre'), self)
|
self.donate_action = QAction(QIcon(I('donate.png')),
|
||||||
|
_('&Donate to support calibre'), self)
|
||||||
for st in self.istores.values():
|
for st in self.istores.values():
|
||||||
st.do_genesis()
|
st.do_genesis()
|
||||||
MainWindowMixin.__init__(self, db)
|
MainWindowMixin.__init__(self, db)
|
||||||
|
@ -195,14 +195,25 @@ class ContentServer(object):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_format(self, id, format):
|
def get_format(self, id, format):
|
||||||
format = format.upper()
|
format = format.upper()
|
||||||
|
fm = self.db.format_metadata(id, format, allow_cache=False)
|
||||||
|
if not fm:
|
||||||
|
raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format))
|
||||||
|
mi = newmi = self.db.get_metadata(id, index_is_id=True)
|
||||||
|
|
||||||
|
cherrypy.response.headers['Last-Modified'] = \
|
||||||
|
self.last_modified(max(fm['mtime'], mi.last_modified))
|
||||||
|
|
||||||
fmt = self.db.format(id, format, index_is_id=True, as_file=True,
|
fmt = self.db.format(id, format, index_is_id=True, as_file=True,
|
||||||
mode='rb')
|
mode='rb')
|
||||||
if fmt is None:
|
if fmt is None:
|
||||||
raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format))
|
raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format))
|
||||||
mi = newmi = self.db.get_metadata(id, index_is_id=True)
|
mt = guess_type('dummy.'+format.lower())[0]
|
||||||
|
if mt is None:
|
||||||
|
mt = 'application/octet-stream'
|
||||||
|
cherrypy.response.headers['Content-Type'] = mt
|
||||||
|
|
||||||
if format == 'EPUB':
|
if format == 'EPUB':
|
||||||
# Get the original metadata
|
# Get the original metadata
|
||||||
|
|
||||||
@ -221,19 +232,19 @@ class ContentServer(object):
|
|||||||
set_metadata(fmt, newmi, format.lower())
|
set_metadata(fmt, newmi, format.lower())
|
||||||
fmt.seek(0)
|
fmt.seek(0)
|
||||||
|
|
||||||
mt = guess_type('dummy.'+format.lower())[0]
|
fmt.seek(0, 2)
|
||||||
if mt is None:
|
cherrypy.response.headers['Content-Length'] = fmt.tell()
|
||||||
mt = 'application/octet-stream'
|
fmt.seek(0)
|
||||||
au = authors_to_string(mi.authors if mi.authors else [_('Unknown')])
|
|
||||||
title = mi.title if mi.title else _('Unknown')
|
au = authors_to_string(newmi.authors if newmi.authors else
|
||||||
|
[_('Unknown')])
|
||||||
|
title = newmi.title if newmi.title else _('Unknown')
|
||||||
fname = u'%s - %s_%s.%s'%(title[:30], au[:30], id, format.lower())
|
fname = u'%s - %s_%s.%s'%(title[:30], au[:30], id, format.lower())
|
||||||
fname = ascii_filename(fname).replace('"', '_')
|
fname = ascii_filename(fname).replace('"', '_')
|
||||||
cherrypy.response.headers['Content-Type'] = mt
|
|
||||||
cherrypy.response.headers['Content-Disposition'] = \
|
cherrypy.response.headers['Content-Disposition'] = \
|
||||||
b'attachment; filename="%s"'%fname
|
b'attachment; filename="%s"'%fname
|
||||||
|
cherrypy.response.body = fmt
|
||||||
cherrypy.response.timeout = 3600
|
cherrypy.response.timeout = 3600
|
||||||
cherrypy.response.headers['Last-Modified'] = \
|
|
||||||
self.last_modified(self.db.format_last_modified(id, format))
|
|
||||||
return fmt
|
return fmt
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ How do I use |app| with my Android phone/tablet?
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
There are two ways that you can connect your Android device to calibre. Using a USB cable-- or wirelessly, over the air.
|
There are two ways that you can connect your Android device to calibre. Using a USB cable-- or wirelessly, over the air.
|
||||||
The USB cable method only works if your Android device can act as a USB disk, which some Android tablets cannot.
|
**The USB cable method only works if your Android device can act as a USB disk, that means in windows it must have a drive letter, like K:**.
|
||||||
|
|
||||||
Using a USB cable
|
Using a USB cable
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -374,6 +374,8 @@ any |app| developers will ever feel motivated enough to support it. There is how
|
|||||||
that allows you to create collections on your Kindle from the |app| metadata. It is available
|
that allows you to create collections on your Kindle from the |app| metadata. It is available
|
||||||
`from here <http://www.mobileread.com/forums/showthread.php?t=118635>`_.
|
`from here <http://www.mobileread.com/forums/showthread.php?t=118635>`_.
|
||||||
|
|
||||||
|
.. note:: Amazon have removed the ability to manipulate collections completely in their newer models, like the Kindle Touch and Kindle Fire, making even the above plugin useless. If you really want the ability to manage collections on your Kindle via a USB connection, we encourage you to complain to Amazon about it, or get a reader where this is supported, like the SONY Readers.
|
||||||
|
|
||||||
Library Management
|
Library Management
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -1398,6 +1398,8 @@ class BasicNewsRecipe(Recipe):
|
|||||||
oldest_article=self.oldest_article,
|
oldest_article=self.oldest_article,
|
||||||
max_articles_per_feed=self.max_articles_per_feed,
|
max_articles_per_feed=self.max_articles_per_feed,
|
||||||
get_article_url=self.get_article_url))
|
get_article_url=self.get_article_url))
|
||||||
|
if (self.delay > 0):
|
||||||
|
time.sleep(self.delay)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
feed = Feed()
|
feed = Feed()
|
||||||
msg = 'Failed feed: %s'%(title if title else url)
|
msg = 'Failed feed: %s'%(title if title else url)
|
||||||
|
@ -13,7 +13,7 @@ from datetime import timedelta
|
|||||||
from lxml import etree
|
from lxml import etree
|
||||||
from lxml.builder import ElementMaker
|
from lxml.builder import ElementMaker
|
||||||
|
|
||||||
from calibre import browser
|
from calibre import browser, force_unicode
|
||||||
from calibre.utils.date import parse_date, now as nowf, utcnow, tzlocal, \
|
from calibre.utils.date import parse_date, now as nowf, utcnow, tzlocal, \
|
||||||
isoformat, fromordinal
|
isoformat, fromordinal
|
||||||
|
|
||||||
@ -66,8 +66,9 @@ def serialize_collection(mapping_of_recipe_classes):
|
|||||||
x.title.decode('ascii')
|
x.title.decode('ascii')
|
||||||
'''
|
'''
|
||||||
for urn in sorted(mapping_of_recipe_classes.keys(),
|
for urn in sorted(mapping_of_recipe_classes.keys(),
|
||||||
key=lambda key: getattr(mapping_of_recipe_classes[key], 'title',
|
key=lambda key: force_unicode(
|
||||||
'zzz')):
|
getattr(mapping_of_recipe_classes[key], 'title', 'zzz'),
|
||||||
|
'utf-8')):
|
||||||
recipe = serialize_recipe(urn, mapping_of_recipe_classes[urn])
|
recipe = serialize_recipe(urn, mapping_of_recipe_classes[urn])
|
||||||
collection.append(recipe)
|
collection.append(recipe)
|
||||||
collection.set('count', str(len(collection)))
|
collection.set('count', str(len(collection)))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user