mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from custcol trunk
This commit is contained in:
commit
866fda04fe
BIN
resources/images/news/aif_ru.png
Normal file
BIN
resources/images/news/aif_ru.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1003 B |
BIN
resources/images/news/izvestia.png
Normal file
BIN
resources/images/news/izvestia.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 475 B |
BIN
resources/images/news/kommersant.png
Normal file
BIN
resources/images/news/kommersant.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 353 B |
BIN
resources/images/news/ria_ru.png
Normal file
BIN
resources/images/news/ria_ru.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 534 B |
31
resources/recipes/aif_ru.recipe
Normal file
31
resources/recipes/aif_ru.recipe
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
www.aif.ru
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AIF_ru(BasicNewsRecipe):
|
||||||
|
title = 'Arguments & Facts - Russian'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from Russia'
|
||||||
|
publisher = 'AIF'
|
||||||
|
category = 'news, politics, Russia'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'cp1251'
|
||||||
|
language = 'ru'
|
||||||
|
publication_type = 'magazine'
|
||||||
|
extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Verdana,Arial,Helvetica,sans1,sans-serif} '
|
||||||
|
keep_only_tags = [dict(name='div',attrs={'id':'inner'})]
|
||||||
|
remove_tags = [
|
||||||
|
dict(name=['iframe','object','link','base','input','img'])
|
||||||
|
,dict(name='div',attrs={'class':'photo'})
|
||||||
|
,dict(name='p',attrs={'class':'resizefont'})
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [(u'News', u'http://www.aif.ru/rss/all.php')]
|
||||||
|
|
28
resources/recipes/izvestia.recipe
Normal file
28
resources/recipes/izvestia.recipe
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
izvestia.ru
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Izvestia(BasicNewsRecipe):
|
||||||
|
title = 'Izvestia'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from Russia'
|
||||||
|
publisher = 'Izvestia'
|
||||||
|
category = 'news, politics, Russia'
|
||||||
|
oldest_article = 5
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'cp1251'
|
||||||
|
language = 'ru'
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://images.izvestia.ru/izv/sys/logo.gif'
|
||||||
|
extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Verdana,Arial,Helvetica,sans1,sans-serif} '
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'class':'newsFull'})]
|
||||||
|
remove_tags = [dict(name=['iframe','object','img','link','base'])]
|
||||||
|
remove_tags_before = dict(name='h1', attrs={'class':'statya'})
|
||||||
|
|
||||||
|
feeds = [(u'Daily edition', u'http://rss.feedsportal.com/c/32171/f/424076/index.rss')]
|
42
resources/recipes/kommersant.recipe
Normal file
42
resources/recipes/kommersant.recipe
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
www.kommersant.ru
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Kommersant_ru(BasicNewsRecipe):
|
||||||
|
title = 'Kommersant'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from Russia'
|
||||||
|
publisher = 'Kommersant'
|
||||||
|
category = 'news, politics, Russia'
|
||||||
|
oldest_article = 5
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'cp1251'
|
||||||
|
language = 'ru'
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://www.kommersant.ru/CorpPics/logo_daily_1.gif'
|
||||||
|
extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Arial, sans1, sans-serif} span#ctl00_ContentPlaceHolderStyle_LabelSubTitle{margin-bottom: 1em; display: block} .author{margin-bottom: 1em; display: block} .paragraph{margin-bottom: 1em; display: block} .vvodka{font-weight: bold; margin-bottom: 1em} '
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(attrs={'id':'ctl00_ContentPlaceHolderStyle_PanelHeader'})
|
||||||
|
,dict(attrs={'class':['vvodka','paragraph','author']})
|
||||||
|
]
|
||||||
|
remove_tags = [dict(name=['iframe','object','link','img','base'])]
|
||||||
|
|
||||||
|
feeds = [(u'Articles', u'http://feeds.kommersant.ru/RSS_Export/RU/daily.xml')]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url.replace('doc-rss.aspx','doc.aspx') + '&print=true'
|
||||||
|
|
43
resources/recipes/ria_ru.recipe
Normal file
43
resources/recipes/ria_ru.recipe
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
www.rian.ru
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class RIANovosti(BasicNewsRecipe):
|
||||||
|
title = 'RIA Novosti - Russian'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from Russia'
|
||||||
|
publisher = 'RIA'
|
||||||
|
category = 'news, politics, Russia'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'utf8'
|
||||||
|
language = 'ru'
|
||||||
|
publication_type = 'newsportal'
|
||||||
|
masthead_url = 'http://img.beta.rian.ru/images/22868/43/228684314.jpg'
|
||||||
|
extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Arial,Helvetica,sans1,sans-serif} '
|
||||||
|
remove_tags_before = dict(name='h1')
|
||||||
|
remove_tags_after = dict(name='div', attrs={'class':'text'})
|
||||||
|
remove_tags = [dict(name=['iframe','object','link','img','base'])]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Frontpage', u'http://www.rian.ru/export/rss2/lenta/index.xml')
|
||||||
|
,(u'Politics', u'http://www.rian.ru/export/rss2/politics/index.xml')
|
||||||
|
,(u'World', u'http://www.rian.ru/export/rss2/world/index.xml')
|
||||||
|
,(u'Economy', u'http://www.rian.ru/export/rss2/economy/index.xml')
|
||||||
|
,(u'Society', u'http://www.rian.ru/export/rss2/society/index.xml')
|
||||||
|
,(u'Moscow', u'http://www.rian.ru/export/rss2/moscow/index.xml')
|
||||||
|
,(u'Defense', u'http://www.rian.ru/export/rss2/defense_safety/index.xml')
|
||||||
|
,(u'Science', u'http://www.rian.ru/export/rss2/science/index.xml')
|
||||||
|
,(u'Turism', u'http://www.rian.ru/export/rss2/tourism/index.xml')
|
||||||
|
,(u'Culture', u'http://www.rian.ru/export/rss2/culture/index.xml')
|
||||||
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url.replace('.html','-print.html')
|
||||||
|
|
@ -68,7 +68,7 @@ class PML2PMLZ(FileTypePlugin):
|
|||||||
|
|
||||||
of = self.temporary_file('_plugin_pml2pmlz.pmlz')
|
of = self.temporary_file('_plugin_pml2pmlz.pmlz')
|
||||||
pmlz = zipfile.ZipFile(of.name, 'w')
|
pmlz = zipfile.ZipFile(of.name, 'w')
|
||||||
pmlz.write(pmlfile, os.path.basename(pmlfile))
|
pmlz.write(pmlfile, os.path.basename(pmlfile), zipfile.ZIP_DEFLATED)
|
||||||
|
|
||||||
pml_img = os.path.splitext(pmlfile)[0] + '_img'
|
pml_img = os.path.splitext(pmlfile)[0] + '_img'
|
||||||
i_img = os.path.join(os.path.dirname(pmlfile),'images')
|
i_img = os.path.join(os.path.dirname(pmlfile),'images')
|
||||||
@ -450,7 +450,7 @@ from calibre.devices.eslick.driver import ESLICK
|
|||||||
from calibre.devices.nuut2.driver import NUUT2
|
from calibre.devices.nuut2.driver import NUUT2
|
||||||
from calibre.devices.iriver.driver import IRIVER_STORY
|
from calibre.devices.iriver.driver import IRIVER_STORY
|
||||||
from calibre.devices.binatone.driver import README
|
from calibre.devices.binatone.driver import README
|
||||||
from calibre.devices.hanvon.driver import N516, EB511, ALEX
|
from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA
|
||||||
from calibre.devices.edge.driver import EDGE
|
from calibre.devices.edge.driver import EDGE
|
||||||
from calibre.devices.teclast.driver import TECLAST_K3
|
from calibre.devices.teclast.driver import TECLAST_K3
|
||||||
from calibre.devices.sne.driver import SNE
|
from calibre.devices.sne.driver import SNE
|
||||||
@ -538,6 +538,7 @@ plugins += [
|
|||||||
ALEX,
|
ALEX,
|
||||||
PALMPRE,
|
PALMPRE,
|
||||||
KOBO,
|
KOBO,
|
||||||
|
AZBOOKA,
|
||||||
]
|
]
|
||||||
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
||||||
x.__name__.endswith('MetadataReader')]
|
x.__name__.endswith('MetadataReader')]
|
||||||
|
@ -27,7 +27,7 @@ class ANDROID(USBMS):
|
|||||||
0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]},
|
0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]},
|
||||||
|
|
||||||
# Samsung
|
# Samsung
|
||||||
0x04e8 : { 0x681d : [0x0222], 0x681c : [0x0222]},
|
0x04e8 : { 0x681d : [0x0222], 0x681c : [0x0222, 0x0224]},
|
||||||
|
|
||||||
# Acer
|
# Acer
|
||||||
0x502 : { 0x3203 : [0x0100]},
|
0x502 : { 0x3203 : [0x0100]},
|
||||||
@ -38,9 +38,9 @@ class ANDROID(USBMS):
|
|||||||
'be used')
|
'be used')
|
||||||
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN)
|
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN)
|
||||||
|
|
||||||
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER']
|
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER', 'GT-I5700']
|
||||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE']
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD']
|
||||||
|
|
||||||
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
||||||
|
|
||||||
|
@ -50,6 +50,20 @@ class ALEX(N516):
|
|||||||
EBOOK_DIR_MAIN = 'eBooks'
|
EBOOK_DIR_MAIN = 'eBooks'
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
class AZBOOKA(ALEX):
|
||||||
|
|
||||||
|
name = 'Azbooka driver'
|
||||||
|
gui_name = 'Azbooka'
|
||||||
|
description = _('Communicate with the Azbooka')
|
||||||
|
|
||||||
|
VENDOR_NAME = 'LINUX'
|
||||||
|
WINDOWS_MAIN_MEM = 'FILE-STOR_GADGET'
|
||||||
|
|
||||||
|
MAIN_MEMORY_VOLUME_LABEL = 'Azbooka Internal Memory'
|
||||||
|
|
||||||
|
EBOOK_DIR_MAIN = ''
|
||||||
|
|
||||||
|
|
||||||
class EB511(USBMS):
|
class EB511(USBMS):
|
||||||
name = 'Elonex EB 511 driver'
|
name = 'Elonex EB 511 driver'
|
||||||
gui_name = 'EB 511'
|
gui_name = 'EB 511'
|
||||||
|
@ -176,6 +176,9 @@ class Stylizer(object):
|
|||||||
class_sel_pat = re.compile(r'\.[a-z]+', re.IGNORECASE)
|
class_sel_pat = re.compile(r'\.[a-z]+', re.IGNORECASE)
|
||||||
capital_sel_pat = re.compile(r'h|[A-Z]+')
|
capital_sel_pat = re.compile(r'h|[A-Z]+')
|
||||||
for _, _, cssdict, text, _ in rules:
|
for _, _, cssdict, text, _ in rules:
|
||||||
|
fl = ':first-letter' in text
|
||||||
|
if fl:
|
||||||
|
text = text.replace(':first-letter', '')
|
||||||
try:
|
try:
|
||||||
selector = CSSSelector(text)
|
selector = CSSSelector(text)
|
||||||
except (AssertionError, ExpressionError, etree.XPathSyntaxError,
|
except (AssertionError, ExpressionError, etree.XPathSyntaxError,
|
||||||
@ -202,8 +205,21 @@ class Stylizer(object):
|
|||||||
if found:
|
if found:
|
||||||
self.logger.warn('Ignoring case mismatches for CSS selector: %s in %s'
|
self.logger.warn('Ignoring case mismatches for CSS selector: %s in %s'
|
||||||
%(text, item.href))
|
%(text, item.href))
|
||||||
for elem in matches:
|
if fl:
|
||||||
self.style(elem)._update_cssdict(cssdict)
|
from lxml.builder import ElementMaker
|
||||||
|
E = ElementMaker(namespace=XHTML_NS)
|
||||||
|
for elem in matches:
|
||||||
|
for x in elem.iter():
|
||||||
|
if x.text:
|
||||||
|
span = E.span(x.text[0])
|
||||||
|
span.tail = x.text[1:]
|
||||||
|
x.text = None
|
||||||
|
x.insert(0, span)
|
||||||
|
self.style(span)._update_cssdict(cssdict)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
for elem in matches:
|
||||||
|
self.style(elem)._update_cssdict(cssdict)
|
||||||
for elem in xpath(tree, '//h:img[@width or @height]'):
|
for elem in xpath(tree, '//h:img[@width or @height]'):
|
||||||
base = elem.get('style', '').strip()
|
base = elem.get('style', '').strip()
|
||||||
if base:
|
if base:
|
||||||
|
@ -384,25 +384,25 @@ class Document(QWebPage):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def height(self):
|
def height(self):
|
||||||
ans = self.javascript('document.body.offsetHeight', 'int') # contentsSize gives inaccurate results
|
j = self.javascript('document.body.offsetHeight', 'int')
|
||||||
if ans == 0:
|
q = self.mainFrame().contentsSize().height()
|
||||||
ans = self.mainFrame().contentsSize().height()
|
if q == j:
|
||||||
return ans
|
return j
|
||||||
|
if min(j, q) <= 0:
|
||||||
|
return max(j, q)
|
||||||
|
window_height = self.window_height
|
||||||
|
if j == window_height:
|
||||||
|
return j if q < 1.2*j else q
|
||||||
|
return j
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width(self):
|
def width(self):
|
||||||
return self.mainFrame().contentsSize().width() # offsetWidth gives inaccurate results
|
return self.mainFrame().contentsSize().width() # offsetWidth gives inaccurate results
|
||||||
|
|
||||||
def set_bottom_padding(self, amount):
|
def set_bottom_padding(self, amount):
|
||||||
body = self.mainFrame().documentElement().findFirst('body')
|
s = QSize(-1, -1) if amount == 0 else QSize(self.width,
|
||||||
if body.isNull():
|
self.height+amount)
|
||||||
return
|
self.setPreferredContentsSize(s)
|
||||||
old_padding = unicode(body.styleProperty('padding-bottom',
|
|
||||||
body.ComputedStyle)).strip()
|
|
||||||
padding = u'%dpx'%amount
|
|
||||||
if old_padding != padding:
|
|
||||||
body.setStyleProperty('padding-bottom', padding + ' !important')
|
|
||||||
|
|
||||||
|
|
||||||
class EntityDeclarationProcessor(object):
|
class EntityDeclarationProcessor(object):
|
||||||
|
|
||||||
@ -705,13 +705,21 @@ class DocumentView(QWebView):
|
|||||||
|
|
||||||
def next_page(self):
|
def next_page(self):
|
||||||
window_height = self.document.window_height
|
window_height = self.document.window_height
|
||||||
|
document_height = self.document.height
|
||||||
|
ddelta = document_height - window_height
|
||||||
|
#print '\nWindow height:', window_height
|
||||||
|
#print 'Document height:', self.document.height
|
||||||
|
|
||||||
delta_y = window_height - 25
|
delta_y = window_height - 25
|
||||||
if self.document.at_bottom:
|
if self.document.at_bottom or ddelta <= 0:
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.next_document()
|
self.manager.next_document()
|
||||||
|
elif ddelta < 25:
|
||||||
|
self.scroll_by(y=ddelta)
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
oopos = self.document.ypos
|
oopos = self.document.ypos
|
||||||
#print '\nOriginal position:', oopos
|
#print 'Original position:', oopos
|
||||||
self.document.set_bottom_padding(0)
|
self.document.set_bottom_padding(0)
|
||||||
opos = self.document.ypos
|
opos = self.document.ypos
|
||||||
#print 'After set padding=0:', self.document.ypos
|
#print 'After set padding=0:', self.document.ypos
|
||||||
@ -722,8 +730,14 @@ class DocumentView(QWebView):
|
|||||||
lower_limit = opos + delta_y # Max value of top y co-ord after scrolling
|
lower_limit = opos + delta_y # Max value of top y co-ord after scrolling
|
||||||
max_y = self.document.height - window_height # The maximum possible top y co-ord
|
max_y = self.document.height - window_height # The maximum possible top y co-ord
|
||||||
if max_y < lower_limit:
|
if max_y < lower_limit:
|
||||||
|
padding = lower_limit - max_y
|
||||||
|
if padding == window_height:
|
||||||
|
if self.manager is not None:
|
||||||
|
self.manager.next_document()
|
||||||
|
return
|
||||||
#print 'Setting padding to:', lower_limit - max_y
|
#print 'Setting padding to:', lower_limit - max_y
|
||||||
self.document.set_bottom_padding(lower_limit - max_y)
|
self.document.set_bottom_padding(lower_limit - max_y)
|
||||||
|
#print 'Document height:', self.document.height
|
||||||
max_y = self.document.height - window_height
|
max_y = self.document.height - window_height
|
||||||
lower_limit = min(max_y, lower_limit)
|
lower_limit = min(max_y, lower_limit)
|
||||||
#print 'Scroll to:', lower_limit
|
#print 'Scroll to:', lower_limit
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Ebook Viewer</string>
|
<string>E-book Viewer</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
9
src/calibre/library/db/__init__.py
Normal file
9
src/calibre/library/db/__init__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
|
|
37
src/calibre/library/db/base.py
Normal file
37
src/calibre/library/db/base.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
|
''' Design documentation {{{
|
||||||
|
|
||||||
|
Storage paradigm {{{
|
||||||
|
* Agnostic to storage paradigm (i.e. no book per folder assumptions)
|
||||||
|
* Two separate concepts: A store and collection
|
||||||
|
A store is a backend, like a sqlite database associated with a path on
|
||||||
|
the local filesystem, or a cloud based storage solution.
|
||||||
|
A collection is a user defined group of stores. Most of the logic for
|
||||||
|
data manipulation sorting/searching/restrictions should be in the collection
|
||||||
|
class. The collection class should transparently handle the
|
||||||
|
conversion from store name + id to row number in the collection.
|
||||||
|
* Not sure how feasible it is to allow many-many maps between stores
|
||||||
|
and collections.
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Event system {{{
|
||||||
|
* Comprehensive event system that other components can subscribe to
|
||||||
|
* Subscribers should be able to temporarily block receiving events
|
||||||
|
* Should event dispatch be asynchronous?
|
||||||
|
* Track last modified time for metadata and each format
|
||||||
|
}}}
|
||||||
|
}}}'''
|
||||||
|
|
||||||
|
# Imports {{{
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -313,7 +313,7 @@ class PostInstall:
|
|||||||
with open(os.path.join(base, '95-calibre.rules'), 'wb') as udev:
|
with open(os.path.join(base, '95-calibre.rules'), 'wb') as udev:
|
||||||
self.manifest.append(udev.name)
|
self.manifest.append(udev.name)
|
||||||
udev.write('''# Sony Reader PRS-500\n'''
|
udev.write('''# Sony Reader PRS-500\n'''
|
||||||
'''BUS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,)
|
'''SUBSYSTEMS=="usb", SYSFS{idProduct}=="029b", SYSFS{idVendor}=="054c", MODE="660", GROUP="%s"\n'''%(group,)
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
if self.opts.fatal_errors:
|
if self.opts.fatal_errors:
|
||||||
|
@ -152,7 +152,7 @@ Linux development environment
|
|||||||
used in windows and OS X. Alternatively, you can install |app| from source. Instructions for setting up a development
|
used in windows and OS X. Alternatively, you can install |app| from source. Instructions for setting up a development
|
||||||
environment from source are in the INSTALL file in the source tree. Here we will address using the binary a runtime.
|
environment from source are in the INSTALL file in the source tree. Here we will address using the binary a runtime.
|
||||||
|
|
||||||
Install the |app| using the binary installer. The opena terminal and change to the previously checked out |app| code directory, for example::
|
Install the |app| using the binary installer. Then open a terminal and change to the previously checked out |app| code directory, for example::
|
||||||
|
|
||||||
cd /home/kovid/work/calibre
|
cd /home/kovid/work/calibre
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user