mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
Apply the PEP8 blank line standards to the entire codebase
This commit is contained in:
parent
9306c28228
commit
e333001d31
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Aabenraa
|
||||
'''
|
||||
|
||||
|
||||
class AabenraaLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Aabenraa'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Aarhus
|
||||
'''
|
||||
|
||||
|
||||
class AarhusLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Aarhus'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Aarhus Midt
|
||||
'''
|
||||
|
||||
|
||||
class AarhusmidtLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Aarhus Midt'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Aarhus Nord
|
||||
'''
|
||||
|
||||
|
||||
class AarhusnordLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Aarhus Nord'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Aarhus Syd
|
||||
'''
|
||||
|
||||
|
||||
class AarhussydLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Aarhus Syd'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Aarhus Ves
|
||||
'''
|
||||
|
||||
|
||||
class AarhusvestLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Aarhus Ves'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Albertslund Posten
|
||||
'''
|
||||
|
||||
|
||||
class AlbertslundLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Albertslund Posten'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Allerød Nyt: RSS feed: Seneste nyt - alleroed.lokalavisen.dk
|
||||
'''
|
||||
|
||||
|
||||
class AlleroedLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Allerød Nyt - alleroed.lokalavisen.dk'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Alt om DATA, Datatid TechLife - Download, test, antivirus, netværk
|
||||
'''
|
||||
|
||||
|
||||
class WwwAltomdata_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Alt om DATA, Datatid TechLife - Download, test, antivirus, netværk'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Amagerbladet
|
||||
'''
|
||||
|
||||
|
||||
class Amagerbladet_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Amagerbladet'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Avisen.dk
|
||||
'''
|
||||
|
||||
|
||||
class WwwAvisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Avisen.dk'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
City Avisen
|
||||
'''
|
||||
|
||||
|
||||
class CityAvisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'City Avisen'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Computerworld.dk
|
||||
'''
|
||||
|
||||
|
||||
class WwwComputerworld_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Computerworld.dk'
|
||||
|
@ -1,5 +1,6 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class AdvancedUserRecipe(BasicNewsRecipe):
|
||||
title = u'Contropiano'
|
||||
oldest_article = 7
|
||||
|
@ -3,6 +3,7 @@
|
||||
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class AdvancedUserRecipe1468055030(BasicNewsRecipe):
|
||||
title = 'DataNews'
|
||||
__author__ = 'oCkz7bJ_'
|
||||
|
@ -4,6 +4,7 @@ from __future__ import unicode_literals, division, absolute_import, print_functi
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class AdvancedUserRecipe1467571059(BasicNewsRecipe):
|
||||
title = 'De Standaard'
|
||||
__author__ = 'Darko Miletic, Aimylios, oCkz7bJ_'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
DjurslandsPosten
|
||||
'''
|
||||
|
||||
|
||||
class DjurslandsPosten_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'DjurslandsPosten'
|
||||
|
@ -9,6 +9,7 @@ __copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
DR.dk
|
||||
'''
|
||||
|
||||
|
||||
class DRNyheder(BasicNewsRecipe):
|
||||
title = 'DR Nyheder'
|
||||
__author__ = 'Darko Miletic'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Adresseavisen Ebeltoft
|
||||
'''
|
||||
|
||||
|
||||
class EbeltoftLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Adresseavisen Ebeltoft'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Egedal
|
||||
'''
|
||||
|
||||
|
||||
class EgedalLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Egedal'
|
||||
|
@ -6,6 +6,7 @@ eluniversal.com.mx
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class ElUniversal(BasicNewsRecipe):
|
||||
title = 'El Universal'
|
||||
__author__ = 'Darko Miletic'
|
||||
|
@ -6,6 +6,7 @@ http://www.eltribuno.info/salta/edicion_impresa.aspx
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class ElTribunoSaltaImpreso(BasicNewsRecipe):
|
||||
title = 'El Tribuno Salta'
|
||||
__author__ = 'Darko Miletic'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Erhvervs•Avisen: RSS feed: Seneste nyt - erhvervsavisen.dk
|
||||
'''
|
||||
|
||||
|
||||
class Erhvervsavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Erhvervs Avisen'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Esbjerg: RSS feed: Seneste nyt - esbjerg.lokalavisen.dk
|
||||
'''
|
||||
|
||||
|
||||
class EsbjergLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Esbjerg'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Favrskov Avisen
|
||||
'''
|
||||
|
||||
|
||||
class FavrskovAvisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Favrskov Avisen'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Favrskovposten
|
||||
'''
|
||||
|
||||
|
||||
class FavrskovLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Favrskovposten'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Folkebladet
|
||||
'''
|
||||
|
||||
|
||||
class Folkebladet_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Folkebladet'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Folkebladet Djursland
|
||||
'''
|
||||
|
||||
|
||||
class FolkebladetDjursland_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Folkebladet Djursland'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
folketidende.dk
|
||||
'''
|
||||
|
||||
|
||||
class Folketidende_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'folketidende.dk'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Uge-Nyt
|
||||
'''
|
||||
|
||||
|
||||
class FredensborgLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Uge-Nyt'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Fredericia
|
||||
'''
|
||||
|
||||
|
||||
class FredericiaLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Fredericia'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Frederiksberg Bladet
|
||||
'''
|
||||
|
||||
|
||||
class FrederiksbergBladet_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Frederiksberg Bladet'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Frederikssund
|
||||
'''
|
||||
|
||||
|
||||
class FrederikssundLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Frederikssund'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Furesø Avis
|
||||
'''
|
||||
|
||||
|
||||
class FuresoeLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Furesø Avis'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Villabyerne
|
||||
'''
|
||||
|
||||
|
||||
class GentofteLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Villabyerne'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Grenaa
|
||||
'''
|
||||
|
||||
|
||||
class GrenaaLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Grenaa'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Ugeposten Gribskov
|
||||
'''
|
||||
|
||||
|
||||
class GribskovLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Ugeposten Gribskov'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Haderslev
|
||||
'''
|
||||
|
||||
|
||||
class HaderslevLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Haderslev'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Halsnæs Avis
|
||||
'''
|
||||
|
||||
|
||||
class HalsnaesLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Halsnæs Avis'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Hillerød Posten
|
||||
'''
|
||||
|
||||
|
||||
class HilleroedLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Hillerød Posten'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Ugebladet
|
||||
'''
|
||||
|
||||
|
||||
class HoersholmLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Ugebladet'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Hornsherred
|
||||
'''
|
||||
|
||||
|
||||
class HornsherredLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Hornsherred'
|
||||
|
@ -3,6 +3,7 @@
|
||||
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class Hvidovre_Avis_dk(BasicNewsRecipe):
|
||||
title = 'Hvidovre avis'
|
||||
language = 'da'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Hvidovre Avis
|
||||
'''
|
||||
|
||||
|
||||
class HvidovreLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Hvidovre Avis'
|
||||
|
@ -6,6 +6,8 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
'''
|
||||
Ingeniøren.dk
|
||||
'''
|
||||
|
||||
|
||||
class Ing_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Ingeniøren'
|
||||
|
@ -12,6 +12,7 @@ http://www.jotdown.es/
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class jotdown(BasicNewsRecipe):
|
||||
author = 'desUBIKado'
|
||||
description = 'Revista digital con magníficos y extensos artículos'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
JydskeVestkysten | JV.dk | jv.dk
|
||||
'''
|
||||
|
||||
|
||||
class WwwJv_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'JydskeVestkysten | JV.dk | jv.dk'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Kalø Vig
|
||||
'''
|
||||
|
||||
|
||||
class KaloevigLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Kalø Vig'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Kgs. Enghave Bladet
|
||||
'''
|
||||
|
||||
|
||||
class KgsEnghaveBladet_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Kgs. Enghave Bladet'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lørdagsavisen
|
||||
'''
|
||||
|
||||
|
||||
class KoegeLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lørdagsavisen'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Kolding
|
||||
'''
|
||||
|
||||
|
||||
class KoldingLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Kolding'
|
||||
|
@ -3,6 +3,7 @@
|
||||
from __future__ import unicode_literals, division, absolute_import, print_function
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class KristeligtDagblad(BasicNewsRecipe):
|
||||
title = 'Kristeligt Dagblad'
|
||||
language = 'da'
|
||||
|
@ -4,6 +4,7 @@ __author__ = 'Daniele Forsi'
|
||||
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
|
||||
class LeScienze(BasicNewsRecipe):
|
||||
title = 'Le Scienze'
|
||||
description = 'Edizione italiana di Scientific American'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Det grønne område
|
||||
'''
|
||||
|
||||
|
||||
class Lyngby_taarbaekLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Det grønne område'
|
||||
|
@ -7,6 +7,7 @@ www.nakedcapitalism.com
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class nakedcapitalism(BasicNewsRecipe):
|
||||
title = 'Naked Capitalism'
|
||||
__author__ = 'Darko Miletic'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Nørrebro Nordvest bladet
|
||||
'''
|
||||
|
||||
|
||||
class Minby_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Nørrebro Nordvest bladet'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Norddjurs
|
||||
'''
|
||||
|
||||
|
||||
class NorddjursLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Norddjurs'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Nordjyske.dk
|
||||
'''
|
||||
|
||||
|
||||
class Nordjyske_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Nordjyske.dk'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Odense
|
||||
'''
|
||||
|
||||
|
||||
class OdenseLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Odense'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Østerbro Avis
|
||||
'''
|
||||
|
||||
|
||||
class OesterbroAvis_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Østerbro Avis'
|
||||
|
@ -1,6 +1,7 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class AdvancedUserRecipe1359406781(BasicNewsRecipe):
|
||||
title = u'Private Eye'
|
||||
publication_type = 'magazine'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Din avis Randers
|
||||
'''
|
||||
|
||||
|
||||
class RandersLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Din avis Randers'
|
||||
|
@ -14,6 +14,7 @@ import lxml
|
||||
from lxml.builder import E
|
||||
respekt_url = 'http://www.respekt.cz'
|
||||
|
||||
|
||||
class respektRecipe(BasicNewsRecipe):
|
||||
__author__ = 'Tomáš Hnyk'
|
||||
publisher = u'Respekt Publishing a. s.'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Roskilde Avis
|
||||
'''
|
||||
|
||||
|
||||
class RoskildeLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Roskilde Avis'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Rudersdal Avis
|
||||
'''
|
||||
|
||||
|
||||
class RudersdalLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Rudersdal Avis'
|
||||
|
@ -7,6 +7,7 @@ sciencedaily.com
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class ScienceDaily(BasicNewsRecipe):
|
||||
title = u'ScienceDaily'
|
||||
__author__ = u'Darko Miletic'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Skanderborg
|
||||
'''
|
||||
|
||||
|
||||
class SkanderborgLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Skanderborg'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
sn.dk
|
||||
'''
|
||||
|
||||
|
||||
class Sn_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'sn.dk'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Sønderborg
|
||||
'''
|
||||
|
||||
|
||||
class SoenderborgLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Sønderborg'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Adresseavisen Syddjurs
|
||||
'''
|
||||
|
||||
|
||||
class SyddjursLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Adresseavisen Syddjurs'
|
||||
|
@ -9,11 +9,13 @@ http://www.theaustralian.news.com.au/
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
def classes(classes):
|
||||
q = frozenset(classes.split(' '))
|
||||
return dict(attrs={
|
||||
'class': lambda x: x and frozenset(x.split()).intersection(q)})
|
||||
|
||||
|
||||
class DailyTelegraph(BasicNewsRecipe):
|
||||
title = u'The Australian'
|
||||
__author__ = u'Kovid Goyal'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Valby Bladet
|
||||
'''
|
||||
|
||||
|
||||
class ValbyBladet_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Valby Bladet'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Vanløse Bladet
|
||||
'''
|
||||
|
||||
|
||||
class VanloeseBladet_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Vanløse Bladet'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Varde
|
||||
'''
|
||||
|
||||
|
||||
class VardeLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Varde'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Lokalavisen Vejle
|
||||
'''
|
||||
|
||||
|
||||
class VejleLokalavisen_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Lokalavisen Vejle'
|
||||
|
@ -7,6 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
Vesterbro Bladet
|
||||
'''
|
||||
|
||||
|
||||
class VesterbroBladet_dk(BasicNewsRecipe):
|
||||
__author__ = 'CoderAllan.github.com'
|
||||
title = 'Vesterbro Bladet'
|
||||
|
@ -11,6 +11,7 @@ http://www.weblogssl.com/
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class weblogssl(BasicNewsRecipe):
|
||||
__author__ = 'desUBIKado'
|
||||
description = u'Weblogs colectivos dedicados a seguir la actualidad sobre tecnologia, entretenimiento, estilos de vida, motor, deportes y economia.'
|
||||
|
@ -1,4 +1,4 @@
|
||||
[flake8]
|
||||
max-line-length = 160
|
||||
builtins = _,dynamic_property,__,P,I,lopen,icu_lower,icu_upper,icu_title,ngettext
|
||||
ignore = E12,E203,E22,E231,E241,E301,E302,E304,E401,E402,E731,W391
|
||||
ignore = E12,E203,E22,E231,E241,E401,E402,E731,W391
|
||||
|
@ -10,12 +10,14 @@ import sys, os, json, subprocess, errno, hashlib
|
||||
from setup import Command, build_cache_dir
|
||||
import __builtin__
|
||||
|
||||
|
||||
def set_builtins(builtins):
|
||||
for x in builtins:
|
||||
if not hasattr(__builtin__, x):
|
||||
setattr(__builtin__, x, True)
|
||||
yield x
|
||||
|
||||
|
||||
class Message:
|
||||
|
||||
def __init__(self, filename, lineno, msg):
|
||||
@ -24,6 +26,7 @@ class Message:
|
||||
def __str__(self):
|
||||
return '%s:%s: %s' % (self.filename, self.lineno, self.msg)
|
||||
|
||||
|
||||
class Check(Command):
|
||||
|
||||
description = 'Check for errors in the calibre source code'
|
||||
|
@ -29,18 +29,22 @@ if False:
|
||||
winerror, win32api, isbsd, config_dir
|
||||
|
||||
_mt_inited = False
|
||||
|
||||
|
||||
def _init_mimetypes():
|
||||
global _mt_inited
|
||||
import mimetypes
|
||||
mimetypes.init([P('mime.types')])
|
||||
_mt_inited = True
|
||||
|
||||
|
||||
def guess_type(*args, **kwargs):
|
||||
import mimetypes
|
||||
if not _mt_inited:
|
||||
_init_mimetypes()
|
||||
return mimetypes.guess_type(*args, **kwargs)
|
||||
|
||||
|
||||
def guess_all_extensions(*args, **kwargs):
|
||||
import mimetypes
|
||||
if not _mt_inited:
|
||||
@ -57,17 +61,20 @@ def guess_extension(*args, **kwargs):
|
||||
ext = '.pdb'
|
||||
return ext
|
||||
|
||||
|
||||
def get_types_map():
|
||||
import mimetypes
|
||||
if not _mt_inited:
|
||||
_init_mimetypes()
|
||||
return mimetypes.types_map
|
||||
|
||||
|
||||
def to_unicode(raw, encoding='utf-8', errors='strict'):
|
||||
if isinstance(raw, unicode):
|
||||
return raw
|
||||
return raw.decode(encoding, errors)
|
||||
|
||||
|
||||
def patheq(p1, p2):
|
||||
p = os.path
|
||||
d = lambda x : p.normcase(p.normpath(p.realpath(p.normpath(x))))
|
||||
@ -75,6 +82,7 @@ def patheq(p1, p2):
|
||||
return False
|
||||
return d(p1) == d(p2)
|
||||
|
||||
|
||||
def unicode_path(path, abs=False):
|
||||
if isinstance(path, bytes):
|
||||
path = path.decode(filesystem_encoding)
|
||||
@ -82,6 +90,7 @@ def unicode_path(path, abs=False):
|
||||
path = os.path.abspath(path)
|
||||
return path
|
||||
|
||||
|
||||
def osx_version():
|
||||
if isosx:
|
||||
import platform
|
||||
@ -90,6 +99,7 @@ def osx_version():
|
||||
if m:
|
||||
return int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||
|
||||
|
||||
def confirm_config_name(name):
|
||||
return name + '_again'
|
||||
|
||||
@ -97,6 +107,7 @@ _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]')
|
||||
_filename_sanitize_unicode = frozenset([u'\\', u'|', u'?', u'*', u'<',
|
||||
u'"', u':', u'>', u'+', u'/'] + list(map(unichr, xrange(32))))
|
||||
|
||||
|
||||
def sanitize_file_name(name, substitute='_', as_unicode=False):
|
||||
'''
|
||||
Sanitize the filename `name`. All invalid characters are replaced by `substitute`.
|
||||
@ -125,6 +136,7 @@ def sanitize_file_name(name, substitute='_', as_unicode=False):
|
||||
one = '_' + one[1:]
|
||||
return one
|
||||
|
||||
|
||||
def sanitize_file_name_unicode(name, substitute='_'):
|
||||
'''
|
||||
Sanitize the filename `name`. All invalid characters are replaced by `substitute`.
|
||||
@ -151,6 +163,7 @@ def sanitize_file_name_unicode(name, substitute='_'):
|
||||
one = '_' + one[1:]
|
||||
return one
|
||||
|
||||
|
||||
def sanitize_file_name2(name, substitute='_'):
|
||||
'''
|
||||
Sanitize filenames removing invalid chars. Keeps unicode names as unicode
|
||||
@ -160,6 +173,7 @@ def sanitize_file_name2(name, substitute='_'):
|
||||
return sanitize_file_name(name, substitute=substitute)
|
||||
return sanitize_file_name_unicode(name, substitute=substitute)
|
||||
|
||||
|
||||
def prints(*args, **kwargs):
|
||||
'''
|
||||
Print unicode arguments safely by encoding them to preferred_encoding
|
||||
@ -227,9 +241,11 @@ def prints(*args, **kwargs):
|
||||
count += len(sep)
|
||||
return count
|
||||
|
||||
|
||||
class CommandLineError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def setup_cli_handlers(logger, level):
|
||||
import logging
|
||||
if os.environ.get('CALIBRE_WORKER', None) is not None and logger.handlers:
|
||||
@ -261,6 +277,7 @@ def load_library(name, cdll):
|
||||
return cdll.LoadLibrary(name)
|
||||
return cdll.LoadLibrary(name+'.so')
|
||||
|
||||
|
||||
def filename_to_utf8(name):
|
||||
'''Return C{name} encoded in utf8. Unhandled characters are replaced. '''
|
||||
if isinstance(name, unicode):
|
||||
@ -268,6 +285,7 @@ def filename_to_utf8(name):
|
||||
codec = 'cp1252' if iswindows else 'utf8'
|
||||
return name.decode(codec, 'replace').encode('utf8')
|
||||
|
||||
|
||||
def extract(path, dir):
|
||||
extractor = None
|
||||
# First use the file header to identify its type
|
||||
@ -292,6 +310,7 @@ def extract(path, dir):
|
||||
raise Exception('Unknown archive type')
|
||||
extractor(path, dir)
|
||||
|
||||
|
||||
def get_proxies(debug=True):
|
||||
from urllib import getproxies
|
||||
proxies = getproxies()
|
||||
@ -315,6 +334,7 @@ def get_proxies(debug=True):
|
||||
prints('Using proxies:', proxies)
|
||||
return proxies
|
||||
|
||||
|
||||
def get_parsed_proxy(typ='http', debug=True):
|
||||
proxies = get_proxies(debug)
|
||||
proxy = proxies.get(typ, None)
|
||||
@ -346,6 +366,7 @@ def get_parsed_proxy(typ='http', debug=True):
|
||||
prints('Using http proxy', str(ans))
|
||||
return ans
|
||||
|
||||
|
||||
def get_proxy_info(proxy_scheme, proxy_string):
|
||||
'''
|
||||
Parse all proxy information from a proxy string (as returned by
|
||||
@ -372,6 +393,7 @@ def get_proxy_info(proxy_scheme, proxy_string):
|
||||
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko'
|
||||
USER_AGENT_MOBILE = 'Mozilla/5.0 (Windows; U; Windows CE 5.1; rv:1.8.1a3) Gecko/20060610 Minimo/0.016'
|
||||
|
||||
|
||||
def random_user_agent(choose=None):
|
||||
try:
|
||||
ua_list = random_user_agent.ua_list
|
||||
@ -398,6 +420,7 @@ def random_user_agent(choose=None):
|
||||
]
|
||||
return random.choice(ua_list) if choose is None else ua_list[choose]
|
||||
|
||||
|
||||
def browser(honor_time=True, max_time=2, mobile_browser=False, user_agent=None, use_robust_parser=False, verify_ssl_certificates=True):
|
||||
'''
|
||||
Create a mechanize browser for web scraping. The browser handles cookies,
|
||||
@ -431,6 +454,7 @@ def browser(honor_time=True, max_time=2, mobile_browser=False, user_agent=None,
|
||||
|
||||
return opener
|
||||
|
||||
|
||||
def fit_image(width, height, pwidth, pheight):
|
||||
'''
|
||||
Fit image in box of width pwidth and height pheight.
|
||||
@ -453,6 +477,7 @@ def fit_image(width, height, pwidth, pheight):
|
||||
|
||||
return scaled, int(width), int(height)
|
||||
|
||||
|
||||
class CurrentDir(object):
|
||||
|
||||
def __init__(self, path, workaround_temp_folder_permissions=False):
|
||||
@ -481,6 +506,8 @@ class CurrentDir(object):
|
||||
|
||||
|
||||
_ncpus = None
|
||||
|
||||
|
||||
def detect_ncpus():
|
||||
"""Detects the number of effective CPUs in the system"""
|
||||
global _ncpus
|
||||
@ -502,18 +529,22 @@ def detect_ncpus():
|
||||
|
||||
relpath = os.path.relpath
|
||||
_spat = re.compile(r'^the\s+|^a\s+|^an\s+', re.IGNORECASE)
|
||||
|
||||
|
||||
def english_sort(x, y):
|
||||
'''
|
||||
Comapare two english phrases ignoring starting prepositions.
|
||||
'''
|
||||
return cmp(_spat.sub('', x), _spat.sub('', y))
|
||||
|
||||
|
||||
def walk(dir):
|
||||
''' A nice interface to os.walk '''
|
||||
for record in os.walk(dir):
|
||||
for f in record[-1]:
|
||||
yield os.path.join(record[0], f)
|
||||
|
||||
|
||||
def strftime(fmt, t=None):
|
||||
''' A version of strftime that returns unicode strings and tries to handle dates
|
||||
before 1900 '''
|
||||
@ -542,12 +573,14 @@ def strftime(fmt, t=None):
|
||||
ans = ans.replace('_early year hack##', str(orig_year))
|
||||
return ans
|
||||
|
||||
|
||||
def my_unichr(num):
|
||||
try:
|
||||
return safe_chr(num)
|
||||
except (ValueError, OverflowError):
|
||||
return u'?'
|
||||
|
||||
|
||||
def entity_to_unicode(match, exceptions=[], encoding='cp1252',
|
||||
result_exceptions={}):
|
||||
'''
|
||||
@ -606,12 +639,15 @@ xml_entity_to_unicode = partial(entity_to_unicode, result_exceptions={
|
||||
'>' : '>',
|
||||
'&' : '&'})
|
||||
|
||||
|
||||
def replace_entities(raw, encoding='cp1252'):
|
||||
return _ent_pat.sub(partial(entity_to_unicode, encoding=encoding), raw)
|
||||
|
||||
|
||||
def xml_replace_entities(raw, encoding='cp1252'):
|
||||
return _ent_pat.sub(partial(xml_entity_to_unicode, encoding=encoding), raw)
|
||||
|
||||
|
||||
def prepare_string_for_xml(raw, attribute=False):
|
||||
raw = _ent_pat.sub(entity_to_unicode, raw)
|
||||
raw = raw.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||
@ -619,9 +655,11 @@ def prepare_string_for_xml(raw, attribute=False):
|
||||
raw = raw.replace('"', '"').replace("'", ''')
|
||||
return raw
|
||||
|
||||
|
||||
def isbytestring(obj):
|
||||
return isinstance(obj, (str, bytes))
|
||||
|
||||
|
||||
def force_unicode(obj, enc=preferred_encoding):
|
||||
if isbytestring(obj):
|
||||
try:
|
||||
@ -639,6 +677,7 @@ def force_unicode(obj, enc=preferred_encoding):
|
||||
obj = obj.decode('utf-8')
|
||||
return obj
|
||||
|
||||
|
||||
def as_unicode(obj, enc=preferred_encoding):
|
||||
if not isbytestring(obj):
|
||||
try:
|
||||
@ -650,12 +689,14 @@ def as_unicode(obj, enc=preferred_encoding):
|
||||
obj = repr(obj)
|
||||
return force_unicode(obj, enc=enc)
|
||||
|
||||
|
||||
def url_slash_cleaner(url):
|
||||
'''
|
||||
Removes redundant /'s from url's.
|
||||
'''
|
||||
return re.sub(r'(?<!:)/{2,}', '/', url)
|
||||
|
||||
|
||||
def human_readable(size, sep=' '):
|
||||
""" Convert a size in bytes into a human readable form """
|
||||
divisor, suffix = 1, "B"
|
||||
@ -670,6 +711,7 @@ def human_readable(size, sep=' '):
|
||||
size = size[:-2]
|
||||
return size + sep + suffix
|
||||
|
||||
|
||||
def remove_bracketed_text(src,
|
||||
brackets={u'(':u')', u'[':u']', u'{':u'}'}):
|
||||
from collections import Counter
|
||||
@ -688,10 +730,12 @@ def remove_bracketed_text(src,
|
||||
buf.append(char)
|
||||
return u''.join(buf)
|
||||
|
||||
|
||||
def ipython(user_ns=None):
|
||||
from calibre.utils.ipython import ipython
|
||||
ipython(user_ns=user_ns)
|
||||
|
||||
|
||||
def fsync(fileobj):
|
||||
fileobj.flush()
|
||||
os.fsync(fileobj.fileno())
|
||||
|
@ -45,6 +45,8 @@ win32api = importlib.import_module('win32api') if iswindows else None
|
||||
fcntl = None if iswindows else importlib.import_module('fcntl')
|
||||
|
||||
_osx_ver = None
|
||||
|
||||
|
||||
def get_osx_version():
|
||||
global _osx_ver
|
||||
if _osx_ver is None:
|
||||
@ -77,12 +79,14 @@ else:
|
||||
|
||||
DEBUG = False
|
||||
|
||||
|
||||
def debug():
|
||||
global DEBUG
|
||||
DEBUG = True
|
||||
|
||||
_cache_dir = None
|
||||
|
||||
|
||||
def _get_cache_dir():
|
||||
confcache = os.path.join(config_dir, u'caches')
|
||||
if isportable:
|
||||
@ -114,6 +118,7 @@ def _get_cache_dir():
|
||||
candidate = confcache
|
||||
return candidate
|
||||
|
||||
|
||||
def cache_dir():
|
||||
global _cache_dir
|
||||
if _cache_dir is None:
|
||||
@ -122,6 +127,7 @@ def cache_dir():
|
||||
|
||||
# plugins {{{
|
||||
|
||||
|
||||
class Plugins(collections.Mapping):
|
||||
|
||||
def __init__(self):
|
||||
@ -231,6 +237,7 @@ else:
|
||||
print 'No write acces to', config_dir, 'using a temporary dir instead'
|
||||
import tempfile, atexit
|
||||
config_dir = tempfile.mkdtemp(prefix='calibre-config-')
|
||||
|
||||
def cleanup_cdir():
|
||||
try:
|
||||
import shutil
|
||||
@ -240,6 +247,7 @@ else:
|
||||
atexit.register(cleanup_cdir)
|
||||
# }}}
|
||||
|
||||
|
||||
def get_version():
|
||||
'''Return version string for display to user '''
|
||||
dv = os.environ.get('CALIBRE_DEVELOP_FROM', None)
|
||||
@ -253,11 +261,13 @@ def get_version():
|
||||
|
||||
return v
|
||||
|
||||
|
||||
def get_portable_base():
|
||||
'Return path to the directory that contains calibre-portable.exe or None'
|
||||
if isportable:
|
||||
return os.path.dirname(os.path.dirname(os.environ['CALIBRE_PORTABLE_BUILD']))
|
||||
|
||||
|
||||
def get_unicode_windows_env_var(name):
|
||||
import ctypes
|
||||
name = unicode(name)
|
||||
@ -268,6 +278,7 @@ def get_unicode_windows_env_var(name):
|
||||
ctypes.windll.kernel32.GetEnvironmentVariableW(name, buf, n)
|
||||
return buf.value
|
||||
|
||||
|
||||
def get_windows_username():
|
||||
'''
|
||||
Return the user name of the currently loggen in user as a unicode string.
|
||||
@ -288,6 +299,7 @@ def get_windows_username():
|
||||
|
||||
return get_unicode_windows_env_var(u'USERNAME')
|
||||
|
||||
|
||||
def get_windows_temp_path():
|
||||
import ctypes
|
||||
n = ctypes.windll.kernel32.GetTempPathW(0, None)
|
||||
@ -298,6 +310,7 @@ def get_windows_temp_path():
|
||||
ans = buf.value
|
||||
return ans if ans else None
|
||||
|
||||
|
||||
def get_windows_user_locale_name():
|
||||
import ctypes
|
||||
k32 = ctypes.windll.kernel32
|
||||
@ -310,6 +323,7 @@ def get_windows_user_locale_name():
|
||||
|
||||
number_formats = None
|
||||
|
||||
|
||||
def get_windows_number_formats():
|
||||
# This can be changed to use localeconv() once we switch to Visual Studio
|
||||
# 2015 as localeconv() in that version has unicode variants for all strings.
|
||||
|
@ -17,6 +17,7 @@ elif isosx:
|
||||
class PluginNotFound(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidPlugin(ValueError):
|
||||
pass
|
||||
|
||||
@ -312,6 +313,7 @@ class Plugin(object): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class FileTypePlugin(Plugin): # {{{
|
||||
'''
|
||||
A plugin that is associated with a particular set of file types.
|
||||
@ -392,6 +394,7 @@ class FileTypePlugin(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class MetadataReaderPlugin(Plugin): # {{{
|
||||
'''
|
||||
A plugin that implements reading metadata from a set of file types.
|
||||
@ -422,6 +425,7 @@ class MetadataReaderPlugin(Plugin): # {{{
|
||||
return None
|
||||
# }}}
|
||||
|
||||
|
||||
class MetadataWriterPlugin(Plugin): # {{{
|
||||
'''
|
||||
A plugin that implements reading metadata from a set of file types.
|
||||
@ -453,6 +457,7 @@ class MetadataWriterPlugin(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class CatalogPlugin(Plugin): # {{{
|
||||
'''
|
||||
A plugin that implements a catalog generator.
|
||||
@ -580,6 +585,7 @@ class CatalogPlugin(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class InterfaceActionBase(Plugin): # {{{
|
||||
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
@ -607,6 +613,7 @@ class InterfaceActionBase(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class PreferencesPlugin(Plugin): # {{{
|
||||
|
||||
'''
|
||||
@ -666,6 +673,7 @@ class PreferencesPlugin(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class StoreBase(Plugin): # {{{
|
||||
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
@ -715,6 +723,7 @@ class StoreBase(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class ViewerPlugin(Plugin): # {{{
|
||||
|
||||
type = _('Viewer')
|
||||
@ -774,6 +783,7 @@ class ViewerPlugin(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class EditBookToolPlugin(Plugin): # {{{
|
||||
|
||||
type = _('Edit Book Tool')
|
||||
@ -781,6 +791,7 @@ class EditBookToolPlugin(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class LibraryClosedPlugin(Plugin): # {{{
|
||||
'''
|
||||
LibraryClosedPlugins are run when a library is closed, either at shutdown,
|
||||
|
@ -15,6 +15,7 @@ plugins = []
|
||||
|
||||
# To archive plugins {{{
|
||||
|
||||
|
||||
class PML2PMLZ(FileTypePlugin):
|
||||
name = 'PML to PMLZ'
|
||||
author = 'John Schember'
|
||||
@ -45,6 +46,7 @@ class PML2PMLZ(FileTypePlugin):
|
||||
|
||||
return of.name
|
||||
|
||||
|
||||
class TXT2TXTZ(FileTypePlugin):
|
||||
name = 'TXT to TXTZ'
|
||||
author = 'John Schember'
|
||||
@ -124,6 +126,8 @@ plugins += [HTML2ZIP, PML2PMLZ, TXT2TXTZ, ArchiveExtract,]
|
||||
# }}}
|
||||
|
||||
# Metadata reader plugins {{{
|
||||
|
||||
|
||||
class ComicMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read comic metadata'
|
||||
@ -164,6 +168,7 @@ class ComicMetadataReader(MetadataReaderPlugin):
|
||||
mi.cover_data = (ext.lower(), data)
|
||||
return mi
|
||||
|
||||
|
||||
class CHMMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read CHM metadata'
|
||||
@ -187,6 +192,7 @@ class EPUBMetadataReader(MetadataReaderPlugin):
|
||||
return get_quick_metadata(stream)
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class FB2MetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read FB2 metadata'
|
||||
@ -197,6 +203,7 @@ class FB2MetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.fb2 import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class HTMLMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read HTML metadata'
|
||||
@ -207,6 +214,7 @@ class HTMLMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.html import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class HTMLZMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read HTMLZ metadata'
|
||||
@ -218,6 +226,7 @@ class HTMLZMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.extz import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class IMPMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read IMP metadata'
|
||||
@ -229,6 +238,7 @@ class IMPMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.imp import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class LITMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read LIT metadata'
|
||||
@ -239,6 +249,7 @@ class LITMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.lit import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class LRFMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read LRF metadata'
|
||||
@ -249,6 +260,7 @@ class LRFMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.lrf.meta import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class LRXMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read LRX metadata'
|
||||
@ -259,6 +271,7 @@ class LRXMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.lrx import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class MOBIMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read MOBI metadata'
|
||||
@ -269,6 +282,7 @@ class MOBIMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.mobi import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class ODTMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read ODT metadata'
|
||||
@ -279,6 +293,7 @@ class ODTMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.odt import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class DocXMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read DOCX metadata'
|
||||
@ -289,6 +304,7 @@ class DocXMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.docx import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class OPFMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read OPF metadata'
|
||||
@ -299,6 +315,7 @@ class OPFMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.opf import get_metadata
|
||||
return get_metadata(stream)[0]
|
||||
|
||||
|
||||
class PDBMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read PDB metadata'
|
||||
@ -310,6 +327,7 @@ class PDBMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.pdb import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class PDFMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read PDF metadata'
|
||||
@ -322,6 +340,7 @@ class PDFMetadataReader(MetadataReaderPlugin):
|
||||
return get_quick_metadata(stream)
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class PMLMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read PML metadata'
|
||||
@ -333,6 +352,7 @@ class PMLMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.pml import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class RARMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read RAR metadata'
|
||||
@ -343,6 +363,7 @@ class RARMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.rar import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class RBMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read RB metadata'
|
||||
@ -354,6 +375,7 @@ class RBMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.rb import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class RTFMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read RTF metadata'
|
||||
@ -364,6 +386,7 @@ class RTFMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.rtf import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class SNBMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read SNB metadata'
|
||||
@ -375,6 +398,7 @@ class SNBMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.snb import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class TOPAZMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read Topaz metadata'
|
||||
@ -385,6 +409,7 @@ class TOPAZMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.topaz import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class TXTMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read TXT metadata'
|
||||
@ -396,6 +421,7 @@ class TXTMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.txt import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class TXTZMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read TXTZ metadata'
|
||||
@ -407,6 +433,7 @@ class TXTZMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.extz import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class ZipMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read ZIP metadata'
|
||||
@ -424,6 +451,7 @@ plugins += [x for x in list(locals().values()) if isinstance(x, type) and
|
||||
|
||||
# Metadata writer plugins {{{
|
||||
|
||||
|
||||
class EPUBMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set EPUB metadata'
|
||||
@ -442,6 +470,7 @@ class EPUBMetadataWriter(MetadataWriterPlugin):
|
||||
return _('Enter {0} below to have the EPUB metadata writer plugin not'
|
||||
' add cover images to EPUB files that have no existing cover image.').format(h)
|
||||
|
||||
|
||||
class FB2MetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set FB2 metadata'
|
||||
@ -452,6 +481,7 @@ class FB2MetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.metadata.fb2 import set_metadata
|
||||
set_metadata(stream, mi, apply_null=self.apply_null)
|
||||
|
||||
|
||||
class HTMLZMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set HTMLZ metadata'
|
||||
@ -463,6 +493,7 @@ class HTMLZMetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.metadata.extz import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
class LRFMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set LRF metadata'
|
||||
@ -473,6 +504,7 @@ class LRFMetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.lrf.meta import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
class MOBIMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set MOBI metadata'
|
||||
@ -484,6 +516,7 @@ class MOBIMetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.metadata.mobi import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
class PDBMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set PDB metadata'
|
||||
@ -495,6 +528,7 @@ class PDBMetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.metadata.pdb import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
class PDFMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set PDF metadata'
|
||||
@ -506,6 +540,7 @@ class PDFMetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.metadata.pdf import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
class RTFMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set RTF metadata'
|
||||
@ -516,6 +551,7 @@ class RTFMetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.metadata.rtf import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
class TOPAZMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set TOPAZ metadata'
|
||||
@ -527,6 +563,7 @@ class TOPAZMetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.metadata.topaz import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
class TXTZMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set TXTZ metadata'
|
||||
@ -538,6 +575,7 @@ class TXTZMetadataWriter(MetadataWriterPlugin):
|
||||
from calibre.ebooks.metadata.extz import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
class DocXMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set DOCX metadata'
|
||||
@ -790,180 +828,215 @@ plugins += [GoogleBooks, GoogleImages, Amazon, Edelweiss, OpenLibrary, ISBNDB, O
|
||||
|
||||
# Interface Actions {{{
|
||||
|
||||
|
||||
class ActionAdd(InterfaceActionBase):
|
||||
name = 'Add Books'
|
||||
actual_plugin = 'calibre.gui2.actions.add:AddAction'
|
||||
description = _('Add books to calibre or the connected device')
|
||||
|
||||
|
||||
class ActionFetchAnnotations(InterfaceActionBase):
|
||||
name = 'Fetch Annotations'
|
||||
actual_plugin = 'calibre.gui2.actions.annotate:FetchAnnotationsAction'
|
||||
description = _('Fetch annotations from a connected Kindle (experimental)')
|
||||
|
||||
|
||||
class ActionGenerateCatalog(InterfaceActionBase):
|
||||
name = 'Generate Catalog'
|
||||
actual_plugin = 'calibre.gui2.actions.catalog:GenerateCatalogAction'
|
||||
description = _('Generate a catalog of the books in your calibre library')
|
||||
|
||||
|
||||
class ActionConvert(InterfaceActionBase):
|
||||
name = 'Convert Books'
|
||||
actual_plugin = 'calibre.gui2.actions.convert:ConvertAction'
|
||||
description = _('Convert books to various ebook formats')
|
||||
|
||||
|
||||
class ActionPolish(InterfaceActionBase):
|
||||
name = 'Polish Books'
|
||||
actual_plugin = 'calibre.gui2.actions.polish:PolishAction'
|
||||
description = _('Fine tune your ebooks')
|
||||
|
||||
|
||||
class ActionEditToC(InterfaceActionBase):
|
||||
name = 'Edit ToC'
|
||||
actual_plugin = 'calibre.gui2.actions.toc_edit:ToCEditAction'
|
||||
description = _('Edit the Table of Contents in your books')
|
||||
|
||||
|
||||
class ActionDelete(InterfaceActionBase):
|
||||
name = 'Remove Books'
|
||||
actual_plugin = 'calibre.gui2.actions.delete:DeleteAction'
|
||||
description = _('Delete books from your calibre library or connected device')
|
||||
|
||||
|
||||
class ActionEmbed(InterfaceActionBase):
|
||||
name = 'Embed Metadata'
|
||||
actual_plugin = 'calibre.gui2.actions.embed:EmbedAction'
|
||||
description = _('Embed updated metadata into the actual book files in your calibre library')
|
||||
|
||||
|
||||
class ActionEditMetadata(InterfaceActionBase):
|
||||
name = 'Edit Metadata'
|
||||
actual_plugin = 'calibre.gui2.actions.edit_metadata:EditMetadataAction'
|
||||
description = _('Edit the metadata of books in your calibre library')
|
||||
|
||||
|
||||
class ActionView(InterfaceActionBase):
|
||||
name = 'View'
|
||||
actual_plugin = 'calibre.gui2.actions.view:ViewAction'
|
||||
description = _('Read books in your calibre library')
|
||||
|
||||
|
||||
class ActionFetchNews(InterfaceActionBase):
|
||||
name = 'Fetch News'
|
||||
actual_plugin = 'calibre.gui2.actions.fetch_news:FetchNewsAction'
|
||||
description = _('Download news from the internet in ebook form')
|
||||
|
||||
|
||||
class ActionQuickview(InterfaceActionBase):
|
||||
name = 'Show Quickview'
|
||||
actual_plugin = 'calibre.gui2.actions.show_quickview:ShowQuickviewAction'
|
||||
description = _('Show a list of related books quickly')
|
||||
|
||||
|
||||
class ActionTagMapper(InterfaceActionBase):
|
||||
name = 'Tag Mapper'
|
||||
actual_plugin = 'calibre.gui2.actions.tag_mapper:TagMapAction'
|
||||
description = _('Filter/transform the tags for books in the library')
|
||||
|
||||
|
||||
class ActionTemplateTester(InterfaceActionBase):
|
||||
name = 'Template Tester'
|
||||
actual_plugin = 'calibre.gui2.actions.show_template_tester:ShowTemplateTesterAction'
|
||||
description = _('Show an editor for testing templates')
|
||||
|
||||
|
||||
class ActionSaveToDisk(InterfaceActionBase):
|
||||
name = 'Save To Disk'
|
||||
actual_plugin = 'calibre.gui2.actions.save_to_disk:SaveToDiskAction'
|
||||
description = _('Export books from your calibre library to the hard disk')
|
||||
|
||||
|
||||
class ActionShowBookDetails(InterfaceActionBase):
|
||||
name = 'Show Book Details'
|
||||
actual_plugin = 'calibre.gui2.actions.show_book_details:ShowBookDetailsAction'
|
||||
description = _('Show book details in a separate popup')
|
||||
|
||||
|
||||
class ActionRestart(InterfaceActionBase):
|
||||
name = 'Restart'
|
||||
actual_plugin = 'calibre.gui2.actions.restart:RestartAction'
|
||||
description = _('Restart calibre')
|
||||
|
||||
|
||||
class ActionOpenFolder(InterfaceActionBase):
|
||||
name = 'Open Folder'
|
||||
actual_plugin = 'calibre.gui2.actions.open:OpenFolderAction'
|
||||
description = _('Open the folder that contains the book files in your'
|
||||
' calibre library')
|
||||
|
||||
|
||||
class ActionSendToDevice(InterfaceActionBase):
|
||||
name = 'Send To Device'
|
||||
actual_plugin = 'calibre.gui2.actions.device:SendToDeviceAction'
|
||||
description = _('Send books to the connected device')
|
||||
|
||||
|
||||
class ActionConnectShare(InterfaceActionBase):
|
||||
name = 'Connect Share'
|
||||
actual_plugin = 'calibre.gui2.actions.device:ConnectShareAction'
|
||||
description = _('Send books via email or the web also connect to iTunes or'
|
||||
' folders on your computer as if they are devices')
|
||||
|
||||
|
||||
class ActionHelp(InterfaceActionBase):
|
||||
name = 'Help'
|
||||
actual_plugin = 'calibre.gui2.actions.help:HelpAction'
|
||||
description = _('Browse the calibre User Manual')
|
||||
|
||||
|
||||
class ActionPreferences(InterfaceActionBase):
|
||||
name = 'Preferences'
|
||||
actual_plugin = 'calibre.gui2.actions.preferences:PreferencesAction'
|
||||
description = _('Customize calibre')
|
||||
|
||||
|
||||
class ActionSimilarBooks(InterfaceActionBase):
|
||||
name = 'Similar Books'
|
||||
actual_plugin = 'calibre.gui2.actions.similar_books:SimilarBooksAction'
|
||||
description = _('Easily find books similar to the currently selected one')
|
||||
|
||||
|
||||
class ActionChooseLibrary(InterfaceActionBase):
|
||||
name = 'Choose Library'
|
||||
actual_plugin = 'calibre.gui2.actions.choose_library:ChooseLibraryAction'
|
||||
description = _('Switch between different calibre libraries and perform'
|
||||
' maintenance on them')
|
||||
|
||||
|
||||
class ActionAddToLibrary(InterfaceActionBase):
|
||||
name = 'Add To Library'
|
||||
actual_plugin = 'calibre.gui2.actions.add_to_library:AddToLibraryAction'
|
||||
description = _('Copy books from the device to your calibre library')
|
||||
|
||||
|
||||
class ActionEditCollections(InterfaceActionBase):
|
||||
name = 'Edit Collections'
|
||||
actual_plugin = 'calibre.gui2.actions.edit_collections:EditCollectionsAction'
|
||||
description = _('Edit the collections in which books are placed on your device')
|
||||
|
||||
|
||||
class ActionMatchBooks(InterfaceActionBase):
|
||||
name = 'Match Books'
|
||||
actual_plugin = 'calibre.gui2.actions.match_books:MatchBookAction'
|
||||
description = _('Match book on the devices to books in the library')
|
||||
|
||||
|
||||
class ActionCopyToLibrary(InterfaceActionBase):
|
||||
name = 'Copy To Library'
|
||||
actual_plugin = 'calibre.gui2.actions.copy_to_library:CopyToLibraryAction'
|
||||
description = _('Copy a book from one calibre library to another')
|
||||
|
||||
|
||||
class ActionTweakEpub(InterfaceActionBase):
|
||||
name = 'Tweak ePub'
|
||||
actual_plugin = 'calibre.gui2.actions.tweak_epub:TweakEpubAction'
|
||||
description = _('Edit ebooks in the epub or azw3 formats')
|
||||
|
||||
|
||||
class ActionUnpackBook(InterfaceActionBase):
|
||||
name = 'Unpack Book'
|
||||
actual_plugin = 'calibre.gui2.actions.unpack_book:UnpackBookAction'
|
||||
description = _('Make small changes to epub or htmlz files in your calibre library')
|
||||
|
||||
|
||||
class ActionNextMatch(InterfaceActionBase):
|
||||
name = 'Next Match'
|
||||
actual_plugin = 'calibre.gui2.actions.next_match:NextMatchAction'
|
||||
description = _('Find the next or previous match when searching in '
|
||||
'your calibre library in highlight mode')
|
||||
|
||||
|
||||
class ActionPickRandom(InterfaceActionBase):
|
||||
name = 'Pick Random Book'
|
||||
actual_plugin = 'calibre.gui2.actions.random:PickRandomAction'
|
||||
description = _('Choose a random book from your calibre library')
|
||||
|
||||
|
||||
class ActionSortBy(InterfaceActionBase):
|
||||
name = 'Sort By'
|
||||
actual_plugin = 'calibre.gui2.actions.sort:SortByAction'
|
||||
description = _('Sort the list of books')
|
||||
|
||||
|
||||
class ActionMarkBooks(InterfaceActionBase):
|
||||
name = 'Mark Books'
|
||||
actual_plugin = 'calibre.gui2.actions.mark_books:MarkBooksAction'
|
||||
description = _('Temporarily mark books')
|
||||
|
||||
|
||||
class ActionStore(InterfaceActionBase):
|
||||
name = 'Store'
|
||||
author = 'John Schember'
|
||||
@ -981,6 +1054,7 @@ class ActionStore(InterfaceActionBase):
|
||||
from calibre.gui2.store.config.store import save_settings as save
|
||||
save(config_widget)
|
||||
|
||||
|
||||
class ActionPluginUpdater(InterfaceActionBase):
|
||||
name = 'Plugin Updater'
|
||||
author = 'Grant Drake'
|
||||
@ -1001,6 +1075,7 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
|
||||
|
||||
# Preferences Plugins {{{
|
||||
|
||||
|
||||
class LookAndFeel(PreferencesPlugin):
|
||||
name = 'Look & Feel'
|
||||
icon = I('lookfeel.png')
|
||||
@ -1013,6 +1088,7 @@ class LookAndFeel(PreferencesPlugin):
|
||||
description = _('Adjust the look and feel of the calibre interface'
|
||||
' to suit your tastes')
|
||||
|
||||
|
||||
class Behavior(PreferencesPlugin):
|
||||
name = 'Behavior'
|
||||
icon = I('config.png')
|
||||
@ -1024,6 +1100,7 @@ class Behavior(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.behavior'
|
||||
description = _('Change the way calibre behaves')
|
||||
|
||||
|
||||
class Columns(PreferencesPlugin):
|
||||
name = 'Custom Columns'
|
||||
icon = I('column.png')
|
||||
@ -1035,6 +1112,7 @@ class Columns(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.columns'
|
||||
description = _('Add/remove your own columns to the calibre book list')
|
||||
|
||||
|
||||
class Toolbar(PreferencesPlugin):
|
||||
name = 'Toolbar'
|
||||
icon = I('wizard.png')
|
||||
@ -1047,6 +1125,7 @@ class Toolbar(PreferencesPlugin):
|
||||
description = _('Customize the toolbars and context menus, changing which'
|
||||
' actions are available in each')
|
||||
|
||||
|
||||
class Search(PreferencesPlugin):
|
||||
name = 'Search'
|
||||
icon = I('search.png')
|
||||
@ -1058,6 +1137,7 @@ class Search(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.search'
|
||||
description = _('Customize the way searching for books works in calibre')
|
||||
|
||||
|
||||
class InputOptions(PreferencesPlugin):
|
||||
name = 'Input Options'
|
||||
icon = I('arrow-down.png')
|
||||
@ -1074,6 +1154,7 @@ class InputOptions(PreferencesPlugin):
|
||||
self.config_widget = 'calibre.gui2.preferences.conversion:InputOptions'
|
||||
return PreferencesPlugin.create_widget(self, *args, **kwargs)
|
||||
|
||||
|
||||
class CommonOptions(PreferencesPlugin):
|
||||
name = 'Common Options'
|
||||
icon = I('convert.png')
|
||||
@ -1085,6 +1166,7 @@ class CommonOptions(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.conversion:CommonOptions'
|
||||
description = _('Set conversion options common to all formats')
|
||||
|
||||
|
||||
class OutputOptions(PreferencesPlugin):
|
||||
name = 'Output Options'
|
||||
icon = I('arrow-up.png')
|
||||
@ -1096,6 +1178,7 @@ class OutputOptions(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.conversion:OutputOptions'
|
||||
description = _('Set conversion options specific to each output format')
|
||||
|
||||
|
||||
class Adding(PreferencesPlugin):
|
||||
name = 'Adding'
|
||||
icon = I('add_book.png')
|
||||
@ -1108,6 +1191,7 @@ class Adding(PreferencesPlugin):
|
||||
description = _('Control how calibre reads metadata from files when '
|
||||
'adding books')
|
||||
|
||||
|
||||
class Saving(PreferencesPlugin):
|
||||
name = 'Saving'
|
||||
icon = I('save.png')
|
||||
@ -1120,6 +1204,7 @@ class Saving(PreferencesPlugin):
|
||||
description = _('Control how calibre exports files from its database '
|
||||
'to disk when using Save to disk')
|
||||
|
||||
|
||||
class Sending(PreferencesPlugin):
|
||||
name = 'Sending'
|
||||
icon = I('sync.png')
|
||||
@ -1132,6 +1217,7 @@ class Sending(PreferencesPlugin):
|
||||
description = _('Control how calibre transfers files to your '
|
||||
'ebook reader')
|
||||
|
||||
|
||||
class Plugboard(PreferencesPlugin):
|
||||
name = 'Plugboard'
|
||||
icon = I('plugboard.png')
|
||||
@ -1143,6 +1229,7 @@ class Plugboard(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.plugboard'
|
||||
description = _('Change metadata fields before saving/sending')
|
||||
|
||||
|
||||
class TemplateFunctions(PreferencesPlugin):
|
||||
name = 'TemplateFunctions'
|
||||
icon = I('template_funcs.png')
|
||||
@ -1154,6 +1241,7 @@ class TemplateFunctions(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.template_functions'
|
||||
description = _('Create your own template functions')
|
||||
|
||||
|
||||
class Email(PreferencesPlugin):
|
||||
name = 'Email'
|
||||
icon = I('mail.png')
|
||||
@ -1166,6 +1254,7 @@ class Email(PreferencesPlugin):
|
||||
description = _('Setup sharing of books via email. Can be used '
|
||||
'for automatic sending of downloaded news to your devices')
|
||||
|
||||
|
||||
class Server(PreferencesPlugin):
|
||||
name = 'Server'
|
||||
icon = I('network-server.png')
|
||||
@ -1179,6 +1268,7 @@ class Server(PreferencesPlugin):
|
||||
'give you access to your calibre library from anywhere, '
|
||||
'on any device, over the internet')
|
||||
|
||||
|
||||
class MetadataSources(PreferencesPlugin):
|
||||
name = 'Metadata download'
|
||||
icon = I('download-metadata.png')
|
||||
@ -1190,6 +1280,7 @@ class MetadataSources(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.metadata_sources'
|
||||
description = _('Control how calibre downloads ebook metadata from the net')
|
||||
|
||||
|
||||
class IgnoredDevices(PreferencesPlugin):
|
||||
name = 'Ignored Devices'
|
||||
icon = I('reader.png')
|
||||
@ -1215,6 +1306,7 @@ class Plugins(PreferencesPlugin):
|
||||
description = _('Add/remove/customize various bits of calibre '
|
||||
'functionality')
|
||||
|
||||
|
||||
class Tweaks(PreferencesPlugin):
|
||||
name = 'Tweaks'
|
||||
icon = I('tweaks.png')
|
||||
@ -1226,6 +1318,7 @@ class Tweaks(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.tweaks'
|
||||
description = _('Fine tune how calibre behaves in various contexts')
|
||||
|
||||
|
||||
class Keyboard(PreferencesPlugin):
|
||||
name = 'Keyboard'
|
||||
icon = I('keyboard-prefs.png')
|
||||
@ -1237,6 +1330,7 @@ class Keyboard(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.keyboard'
|
||||
description = _('Customize the keyboard shortcuts used by calibre')
|
||||
|
||||
|
||||
class Misc(PreferencesPlugin):
|
||||
name = 'Misc'
|
||||
icon = I('exec.png')
|
||||
@ -1256,6 +1350,8 @@ plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions,
|
||||
# }}}
|
||||
|
||||
# Store plugins {{{
|
||||
|
||||
|
||||
class StoreAmazonKindleStore(StoreBase):
|
||||
name = 'Amazon Kindle'
|
||||
description = u'Kindle books from Amazon.'
|
||||
@ -1265,6 +1361,7 @@ class StoreAmazonKindleStore(StoreBase):
|
||||
formats = ['KINDLE']
|
||||
affiliate = False
|
||||
|
||||
|
||||
class StoreSonyStore(StoreBase):
|
||||
name = 'SONY Reader Store'
|
||||
description = u'SONY Reader books.'
|
||||
@ -1275,12 +1372,14 @@ class StoreSonyStore(StoreBase):
|
||||
formats = ['SONY']
|
||||
affiliate = False
|
||||
|
||||
|
||||
class StoreSonyAUStore(StoreSonyStore):
|
||||
name = 'SONY Reader (Australia) Store'
|
||||
description = u'SONY Reader books (Australia).'
|
||||
actual_plugin = 'calibre.gui2.store.stores.sony_au_plugin:SonyStore'
|
||||
headquarters = 'AU'
|
||||
|
||||
|
||||
class StoreAmazonAUKindleStore(StoreBase):
|
||||
name = 'Amazon AU Kindle'
|
||||
author = u'Kovid Goyal'
|
||||
@ -1290,6 +1389,7 @@ class StoreAmazonAUKindleStore(StoreBase):
|
||||
headquarters = 'AU'
|
||||
formats = ['KINDLE']
|
||||
|
||||
|
||||
class StoreAmazonCAKindleStore(StoreBase):
|
||||
name = 'Amazon CA Kindle'
|
||||
author = u'Kovid Goyal'
|
||||
@ -1299,6 +1399,7 @@ class StoreAmazonCAKindleStore(StoreBase):
|
||||
headquarters = 'CA'
|
||||
formats = ['KINDLE']
|
||||
|
||||
|
||||
class StoreAmazonINKindleStore(StoreBase):
|
||||
name = 'Amazon IN Kindle'
|
||||
author = u'Kovid Goyal'
|
||||
@ -1308,6 +1409,7 @@ class StoreAmazonINKindleStore(StoreBase):
|
||||
headquarters = 'IN'
|
||||
formats = ['KINDLE']
|
||||
|
||||
|
||||
class StoreAmazonDEKindleStore(StoreBase):
|
||||
name = 'Amazon DE Kindle'
|
||||
author = 'Kovid Goyal'
|
||||
@ -1317,6 +1419,7 @@ class StoreAmazonDEKindleStore(StoreBase):
|
||||
headquarters = 'DE'
|
||||
formats = ['KINDLE']
|
||||
|
||||
|
||||
class StoreAmazonFRKindleStore(StoreBase):
|
||||
name = 'Amazon FR Kindle'
|
||||
author = 'Kovid Goyal'
|
||||
@ -1326,6 +1429,7 @@ class StoreAmazonFRKindleStore(StoreBase):
|
||||
headquarters = 'FR'
|
||||
formats = ['KINDLE']
|
||||
|
||||
|
||||
class StoreAmazonITKindleStore(StoreBase):
|
||||
name = 'Amazon IT Kindle'
|
||||
author = 'Kovid Goyal'
|
||||
@ -1335,6 +1439,7 @@ class StoreAmazonITKindleStore(StoreBase):
|
||||
headquarters = 'IT'
|
||||
formats = ['KINDLE']
|
||||
|
||||
|
||||
class StoreAmazonESKindleStore(StoreBase):
|
||||
name = 'Amazon ES Kindle'
|
||||
author = 'Kovid Goyal'
|
||||
@ -1344,6 +1449,7 @@ class StoreAmazonESKindleStore(StoreBase):
|
||||
headquarters = 'ES'
|
||||
formats = ['KINDLE']
|
||||
|
||||
|
||||
class StoreAmazonUKKindleStore(StoreBase):
|
||||
name = 'Amazon UK Kindle'
|
||||
author = 'Kovid Goyal'
|
||||
@ -1353,6 +1459,7 @@ class StoreAmazonUKKindleStore(StoreBase):
|
||||
headquarters = 'UK'
|
||||
formats = ['KINDLE']
|
||||
|
||||
|
||||
class StoreArchiveOrgStore(StoreBase):
|
||||
name = 'Archive.org'
|
||||
description = u'An Internet library offering permanent access for researchers, historians, scholars, people with disabilities, and the general public to historical collections that exist in digital format.' # noqa
|
||||
@ -1362,6 +1469,7 @@ class StoreArchiveOrgStore(StoreBase):
|
||||
headquarters = 'US'
|
||||
formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT']
|
||||
|
||||
|
||||
class StoreBubokPublishingStore(StoreBase):
|
||||
name = 'Bubok Spain'
|
||||
description = u'Bubok Publishing is a publisher, library and store of books of authors from all around the world. They have a big amount of books of a lot of topics' # noqa
|
||||
@ -1371,6 +1479,7 @@ class StoreBubokPublishingStore(StoreBase):
|
||||
headquarters = 'ES'
|
||||
formats = ['EPUB', 'PDF']
|
||||
|
||||
|
||||
class StoreBubokPortugalStore(StoreBase):
|
||||
name = 'Bubok Portugal'
|
||||
description = u'Bubok Publishing Portugal is a publisher, library and store of books of authors from Portugal. They have a big amount of books of a lot of topics' # noqa
|
||||
@ -1380,6 +1489,7 @@ class StoreBubokPortugalStore(StoreBase):
|
||||
headquarters = 'PT'
|
||||
formats = ['EPUB', 'PDF']
|
||||
|
||||
|
||||
class StoreBaenWebScriptionStore(StoreBase):
|
||||
name = 'Baen Ebooks'
|
||||
description = u'Sci-Fi & Fantasy brought to you by Jim Baen.'
|
||||
@ -1389,6 +1499,7 @@ class StoreBaenWebScriptionStore(StoreBase):
|
||||
headquarters = 'US'
|
||||
formats = ['EPUB', 'LIT', 'LRF', 'MOBI', 'RB', 'RTF', 'ZIP']
|
||||
|
||||
|
||||
class StoreBNStore(StoreBase):
|
||||
name = 'Barnes and Noble'
|
||||
description = u'The world\'s largest book seller. As the ultimate destination for book lovers, Barnes & Noble.com offers an incredible array of content.'
|
||||
@ -1397,6 +1508,7 @@ class StoreBNStore(StoreBase):
|
||||
headquarters = 'US'
|
||||
formats = ['NOOK']
|
||||
|
||||
|
||||
class StoreBeamEBooksDEStore(StoreBase):
|
||||
name = 'Beam EBooks DE'
|
||||
author = 'Charles Haley'
|
||||
@ -1408,6 +1520,7 @@ class StoreBeamEBooksDEStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreBiblioStore(StoreBase):
|
||||
name = u'Библио.бг'
|
||||
author = 'Alex Stanev'
|
||||
@ -1417,6 +1530,7 @@ class StoreBiblioStore(StoreBase):
|
||||
headquarters = 'BG'
|
||||
formats = ['EPUB, PDF']
|
||||
|
||||
|
||||
class StoreCdpStore(StoreBase):
|
||||
name = 'Cdp.pl'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1428,6 +1542,7 @@ class StoreCdpStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreChitankaStore(StoreBase):
|
||||
name = u'Моята библиотека'
|
||||
author = 'Alex Stanev'
|
||||
@ -1438,6 +1553,7 @@ class StoreChitankaStore(StoreBase):
|
||||
headquarters = 'BG'
|
||||
formats = ['FB2', 'EPUB', 'TXT', 'SFB']
|
||||
|
||||
|
||||
class StoreEbookNLStore(StoreBase):
|
||||
name = 'eBook.nl'
|
||||
description = u'De eBookwinkel van Nederland'
|
||||
@ -1447,6 +1563,7 @@ class StoreEbookNLStore(StoreBase):
|
||||
formats = ['EPUB', 'PDF']
|
||||
affiliate = False
|
||||
|
||||
|
||||
class StoreEbookpointStore(StoreBase):
|
||||
name = 'Ebookpoint'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1458,6 +1575,7 @@ class StoreEbookpointStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreEbookscomStore(StoreBase):
|
||||
name = 'eBooks.com'
|
||||
description = u'Sells books in multiple electronic formats in all categories. Technical infrastructure is cutting edge, robust and scalable, with servers in the US and Europe.' # noqa
|
||||
@ -1467,6 +1585,7 @@ class StoreEbookscomStore(StoreBase):
|
||||
formats = ['EPUB', 'LIT', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreEbooksGratuitsStore(StoreBase):
|
||||
name = 'EbooksGratuits.com'
|
||||
description = u'Ebooks Libres et Gratuits'
|
||||
@ -1486,6 +1605,7 @@ class StoreEbooksGratuitsStore(StoreBase):
|
||||
# formats = ['EPUB', 'PDF']
|
||||
# affiliate = True
|
||||
|
||||
|
||||
class StoreEKnigiStore(StoreBase):
|
||||
name = u'еКниги'
|
||||
author = 'Alex Stanev'
|
||||
@ -1496,6 +1616,7 @@ class StoreEKnigiStore(StoreBase):
|
||||
formats = ['EPUB', 'PDF', 'HTML']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreEmpikStore(StoreBase):
|
||||
name = 'Empik'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1506,6 +1627,7 @@ class StoreEmpikStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreFeedbooksStore(StoreBase):
|
||||
name = 'Feedbooks'
|
||||
description = u'Feedbooks is a cloud publishing and distribution service, connected to a large ecosystem of reading systems and social networks. Provides a variety of genres from independent and classic books.' # noqa
|
||||
@ -1514,6 +1636,7 @@ class StoreFeedbooksStore(StoreBase):
|
||||
headquarters = 'FR'
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
|
||||
|
||||
class StoreGoogleBooksStore(StoreBase):
|
||||
name = 'Google Books'
|
||||
description = u'Google Books'
|
||||
@ -1522,6 +1645,7 @@ class StoreGoogleBooksStore(StoreBase):
|
||||
headquarters = 'US'
|
||||
formats = ['EPUB', 'PDF', 'TXT']
|
||||
|
||||
|
||||
class StoreGutenbergStore(StoreBase):
|
||||
name = 'Project Gutenberg'
|
||||
description = u'The first producer of free ebooks. Free in the United States because their copyright has expired. They may not be free of copyright in other countries. Readers outside of the United States must check the copyright laws of their countries before downloading or redistributing our ebooks.' # noqa
|
||||
@ -1531,6 +1655,7 @@ class StoreGutenbergStore(StoreBase):
|
||||
headquarters = 'US'
|
||||
formats = ['EPUB', 'HTML', 'MOBI', 'PDB', 'TXT']
|
||||
|
||||
|
||||
class StoreKoboStore(StoreBase):
|
||||
name = 'Kobo'
|
||||
description = u'With over 2.3 million eBooks to browse we have engaged readers in over 200 countries in Kobo eReading. Our eBook listings include New York Times Bestsellers, award winners, classics and more!' # noqa
|
||||
@ -1540,6 +1665,7 @@ class StoreKoboStore(StoreBase):
|
||||
formats = ['EPUB']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreKoobeStore(StoreBase):
|
||||
name = 'Koobe'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1551,6 +1677,7 @@ class StoreKoobeStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreLegimiStore(StoreBase):
|
||||
name = 'Legimi'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1561,6 +1688,7 @@ class StoreLegimiStore(StoreBase):
|
||||
formats = ['EPUB', 'PDF', 'MOBI']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreLibreDEStore(StoreBase):
|
||||
name = 'ebook.de'
|
||||
author = 'Charles Haley'
|
||||
@ -1571,6 +1699,7 @@ class StoreLibreDEStore(StoreBase):
|
||||
formats = ['EPUB', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreLitResStore(StoreBase):
|
||||
name = 'LitRes'
|
||||
description = u'ebooks from LitRes.ru'
|
||||
@ -1582,6 +1711,7 @@ class StoreLitResStore(StoreBase):
|
||||
formats = ['EPUB', 'TXT', 'RTF', 'HTML', 'FB2', 'LRF', 'PDF', 'MOBI', 'LIT', 'ISILO3', 'JAR', 'RB', 'PRC']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreManyBooksStore(StoreBase):
|
||||
name = 'ManyBooks'
|
||||
description = u'Public domain and creative commons works from many sources.'
|
||||
@ -1591,6 +1721,7 @@ class StoreManyBooksStore(StoreBase):
|
||||
headquarters = 'US'
|
||||
formats = ['EPUB', 'FB2', 'JAR', 'LIT', 'LRF', 'MOBI', 'PDB', 'PDF', 'RB', 'RTF', 'TCR', 'TXT', 'ZIP']
|
||||
|
||||
|
||||
class StoreMillsBoonUKStore(StoreBase):
|
||||
name = 'Mills and Boon UK'
|
||||
author = 'Charles Haley'
|
||||
@ -1601,6 +1732,7 @@ class StoreMillsBoonUKStore(StoreBase):
|
||||
formats = ['EPUB']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreMobileReadStore(StoreBase):
|
||||
name = 'MobileRead'
|
||||
description = u'Ebooks handcrafted with the utmost care.'
|
||||
@ -1610,6 +1742,7 @@ class StoreMobileReadStore(StoreBase):
|
||||
headquarters = 'CH'
|
||||
formats = ['EPUB', 'IMP', 'LRF', 'LIT', 'MOBI', 'PDF']
|
||||
|
||||
|
||||
class StoreNextoStore(StoreBase):
|
||||
name = 'Nexto'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1620,6 +1753,7 @@ class StoreNextoStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreNookUKStore(StoreBase):
|
||||
name = 'Nook UK'
|
||||
author = 'Charles Haley'
|
||||
@ -1629,6 +1763,7 @@ class StoreNookUKStore(StoreBase):
|
||||
headquarters = 'UK'
|
||||
formats = ['NOOK']
|
||||
|
||||
|
||||
class StoreOpenBooksStore(StoreBase):
|
||||
name = 'Open Books'
|
||||
description = u'Comprehensive listing of DRM free ebooks from a variety of sources provided by users of calibre.'
|
||||
@ -1637,6 +1772,7 @@ class StoreOpenBooksStore(StoreBase):
|
||||
drm_free_only = True
|
||||
headquarters = 'US'
|
||||
|
||||
|
||||
class StoreOzonRUStore(StoreBase):
|
||||
name = 'OZON.ru'
|
||||
description = u'ebooks from OZON.ru'
|
||||
@ -1648,6 +1784,7 @@ class StoreOzonRUStore(StoreBase):
|
||||
formats = ['TXT', 'PDF', 'DJVU', 'RTF', 'DOC', 'JAR', 'FB2']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StorePragmaticBookshelfStore(StoreBase):
|
||||
name = 'Pragmatic Bookshelf'
|
||||
description = u'The Pragmatic Bookshelf\'s collection of programming and tech books avaliable as ebooks.'
|
||||
@ -1657,6 +1794,7 @@ class StorePragmaticBookshelfStore(StoreBase):
|
||||
headquarters = 'US'
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
|
||||
|
||||
class StorePublioStore(StoreBase):
|
||||
name = 'Publio'
|
||||
description = u'Publio.pl to księgarnia internetowa, w której mogą Państwo nabyć e-booki i audiobooki.'
|
||||
@ -1667,6 +1805,7 @@ class StorePublioStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreRW2010Store(StoreBase):
|
||||
name = 'RW2010'
|
||||
description = u'Polski serwis self-publishingowy. Pliki PDF, EPUB i MOBI. Maksymalna cena utworu nie przekracza u nas 10 złotych!'
|
||||
@ -1677,6 +1816,7 @@ class StoreRW2010Store(StoreBase):
|
||||
headquarters = 'PL'
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
|
||||
|
||||
class StoreSmashwordsStore(StoreBase):
|
||||
name = 'Smashwords'
|
||||
description = u'An ebook publishing and distribution platform for ebook authors, publishers and readers. Covers many genres and formats.'
|
||||
@ -1687,6 +1827,7 @@ class StoreSmashwordsStore(StoreBase):
|
||||
formats = ['EPUB', 'HTML', 'LRF', 'MOBI', 'PDB', 'RTF', 'TXT']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreVirtualoStore(StoreBase):
|
||||
name = 'Virtualo'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1697,6 +1838,7 @@ class StoreVirtualoStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class StoreWaterstonesUKStore(StoreBase):
|
||||
name = 'Waterstones UK'
|
||||
author = 'Charles Haley'
|
||||
@ -1707,6 +1849,7 @@ class StoreWaterstonesUKStore(StoreBase):
|
||||
formats = ['EPUB', 'PDF']
|
||||
affiliate = False
|
||||
|
||||
|
||||
class StoreWeightlessBooksStore(StoreBase):
|
||||
name = 'Weightless Books'
|
||||
description = u'An independent DRM-free ebooksite devoted to ebooks of all sorts.'
|
||||
@ -1716,6 +1859,7 @@ class StoreWeightlessBooksStore(StoreBase):
|
||||
headquarters = 'US'
|
||||
formats = ['EPUB', 'HTML', 'LIT', 'MOBI', 'PDF']
|
||||
|
||||
|
||||
class StoreWHSmithUKStore(StoreBase):
|
||||
name = 'WH Smith UK'
|
||||
author = 'Charles Haley'
|
||||
@ -1725,6 +1869,7 @@ class StoreWHSmithUKStore(StoreBase):
|
||||
headquarters = 'UK'
|
||||
formats = ['EPUB', 'PDF']
|
||||
|
||||
|
||||
class StoreWolneLekturyStore(StoreBase):
|
||||
name = 'Wolne Lektury'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1734,6 +1879,7 @@ class StoreWolneLekturyStore(StoreBase):
|
||||
headquarters = 'PL'
|
||||
formats = ['EPUB', 'MOBI', 'PDF', 'TXT', 'FB2']
|
||||
|
||||
|
||||
class StoreWoblinkStore(StoreBase):
|
||||
name = 'Woblink'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1744,6 +1890,7 @@ class StoreWoblinkStore(StoreBase):
|
||||
formats = ['EPUB', 'MOBI', 'PDF', 'WOBLINK']
|
||||
affiliate = True
|
||||
|
||||
|
||||
class XinXiiStore(StoreBase):
|
||||
name = 'XinXii'
|
||||
description = ''
|
||||
|
@ -7,6 +7,7 @@ import re, os, shutil
|
||||
from calibre import CurrentDir
|
||||
from calibre.customize import Plugin
|
||||
|
||||
|
||||
class ConversionOption(object):
|
||||
|
||||
'''
|
||||
@ -46,6 +47,7 @@ class ConversionOption(object):
|
||||
long_switch=self.long_switch, short_switch=self.short_switch,
|
||||
choices=self.choices)
|
||||
|
||||
|
||||
class OptionRecommendation(object):
|
||||
LOW = 1
|
||||
MED = 2
|
||||
@ -82,6 +84,7 @@ class OptionRecommendation(object):
|
||||
repr(self.recommended_value) +
|
||||
' is not a string or a number')
|
||||
|
||||
|
||||
class DummyReporter(object):
|
||||
|
||||
def __init__(self):
|
||||
@ -90,6 +93,7 @@ class DummyReporter(object):
|
||||
def __call__(self, percent, msg=''):
|
||||
pass
|
||||
|
||||
|
||||
def gui_configuration_widget(name, parent, get_option_by_name,
|
||||
get_option_help, db, book_id, for_output=True):
|
||||
import importlib
|
||||
|
@ -39,6 +39,8 @@ class Plugin(_Plugin):
|
||||
self.height_pts = self.height * 72./self.dpi
|
||||
|
||||
# Input profiles {{{
|
||||
|
||||
|
||||
class InputProfile(Plugin):
|
||||
|
||||
author = 'Kovid Goyal'
|
||||
@ -64,6 +66,7 @@ class SonyReaderInput(InputProfile):
|
||||
fbase = 12
|
||||
fsizes = [7.5, 9, 10, 12, 15.5, 20, 22, 24]
|
||||
|
||||
|
||||
class SonyReader300Input(SonyReaderInput):
|
||||
|
||||
name = 'Sony Reader 300'
|
||||
@ -72,6 +75,7 @@ class SonyReader300Input(SonyReaderInput):
|
||||
|
||||
dpi = 200
|
||||
|
||||
|
||||
class SonyReader900Input(SonyReaderInput):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -81,6 +85,7 @@ class SonyReader900Input(SonyReaderInput):
|
||||
|
||||
screen_size = (584, 978)
|
||||
|
||||
|
||||
class MSReaderInput(InputProfile):
|
||||
|
||||
name = 'Microsoft Reader'
|
||||
@ -92,6 +97,7 @@ class MSReaderInput(InputProfile):
|
||||
fbase = 13
|
||||
fsizes = [10, 11, 13, 16, 18, 20, 22, 26]
|
||||
|
||||
|
||||
class MobipocketInput(InputProfile):
|
||||
|
||||
name = 'Mobipocket Books'
|
||||
@ -105,6 +111,7 @@ class MobipocketInput(InputProfile):
|
||||
fbase = 18
|
||||
fsizes = [14, 14, 16, 18, 20, 22, 24, 26]
|
||||
|
||||
|
||||
class HanlinV3Input(InputProfile):
|
||||
|
||||
name = 'Hanlin V3'
|
||||
@ -117,6 +124,7 @@ class HanlinV3Input(InputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class HanlinV5Input(HanlinV3Input):
|
||||
|
||||
name = 'Hanlin V5'
|
||||
@ -127,6 +135,7 @@ class HanlinV5Input(HanlinV3Input):
|
||||
screen_size = (584, 754)
|
||||
dpi = 200
|
||||
|
||||
|
||||
class CybookG3Input(InputProfile):
|
||||
|
||||
name = 'Cybook G3'
|
||||
@ -139,6 +148,7 @@ class CybookG3Input(InputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class CybookOpusInput(InputProfile):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -152,6 +162,7 @@ class CybookOpusInput(InputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class KindleInput(InputProfile):
|
||||
|
||||
name = 'Kindle'
|
||||
@ -164,6 +175,7 @@ class KindleInput(InputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class IlliadInput(InputProfile):
|
||||
|
||||
name = 'Illiad'
|
||||
@ -175,6 +187,7 @@ class IlliadInput(InputProfile):
|
||||
fbase = 12
|
||||
fsizes = [7.5, 9, 10, 12, 15.5, 20, 22, 24]
|
||||
|
||||
|
||||
class IRexDR1000Input(InputProfile):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -188,6 +201,7 @@ class IRexDR1000Input(InputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class IRexDR800Input(InputProfile):
|
||||
|
||||
author = 'Eric Cronin'
|
||||
@ -200,6 +214,7 @@ class IRexDR800Input(InputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class NookInput(InputProfile):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -222,6 +237,7 @@ input_profiles.sort(cmp=lambda x,y:cmp(x.name.lower(), y.name.lower()))
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class OutputProfile(Plugin):
|
||||
|
||||
author = 'Kovid Goyal'
|
||||
@ -424,6 +440,7 @@ class iPadOutput(OutputProfile):
|
||||
'''
|
||||
# }}}
|
||||
|
||||
|
||||
class iPad3Output(iPadOutput):
|
||||
|
||||
screen_size = comic_screen_size = (2048, 1536)
|
||||
@ -433,6 +450,7 @@ class iPad3Output(iPadOutput):
|
||||
description = _('Intended for the iPad 3 and similar devices with a '
|
||||
'resolution of 1536x2048')
|
||||
|
||||
|
||||
class TabletOutput(iPadOutput):
|
||||
name = 'Tablet'
|
||||
short_name = 'tablet'
|
||||
@ -441,6 +459,7 @@ class TabletOutput(iPadOutput):
|
||||
screen_size = (10000, 10000)
|
||||
comic_screen_size = (10000, 10000)
|
||||
|
||||
|
||||
class SamsungGalaxy(TabletOutput):
|
||||
name = 'Samsung Galaxy'
|
||||
short_name = 'galaxy'
|
||||
@ -448,6 +467,7 @@ class SamsungGalaxy(TabletOutput):
|
||||
'a resolution of 600x1280')
|
||||
screen_size = comic_screen_size = (600, 1280)
|
||||
|
||||
|
||||
class NookHD(TabletOutput):
|
||||
name = 'Nook HD+'
|
||||
short_name = 'nook_hd_plus'
|
||||
@ -455,6 +475,7 @@ class NookHD(TabletOutput):
|
||||
'a resolution of 1280x1920')
|
||||
screen_size = comic_screen_size = (1280, 1920)
|
||||
|
||||
|
||||
class SonyReaderOutput(OutputProfile):
|
||||
|
||||
name = 'Sony Reader'
|
||||
@ -485,6 +506,7 @@ class KoboReaderOutput(OutputProfile):
|
||||
fbase = 12
|
||||
fsizes = [7.5, 9, 10, 12, 15.5, 20, 22, 24]
|
||||
|
||||
|
||||
class SonyReader300Output(SonyReaderOutput):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -494,6 +516,7 @@ class SonyReader300Output(SonyReaderOutput):
|
||||
|
||||
dpi = 200
|
||||
|
||||
|
||||
class SonyReader900Output(SonyReaderOutput):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -504,6 +527,7 @@ class SonyReader900Output(SonyReaderOutput):
|
||||
screen_size = (600, 999)
|
||||
comic_screen_size = screen_size
|
||||
|
||||
|
||||
class SonyReaderT3Output(SonyReaderOutput):
|
||||
|
||||
author = 'Kovid Goyal'
|
||||
@ -522,6 +546,7 @@ class GenericEink(SonyReaderOutput):
|
||||
description = _('Suitable for use with any e-ink device')
|
||||
epub_periodical_format = None
|
||||
|
||||
|
||||
class GenericEinkLarge(GenericEink):
|
||||
|
||||
name = 'Generic e-ink large'
|
||||
@ -531,6 +556,7 @@ class GenericEinkLarge(GenericEink):
|
||||
screen_size = (600, 999)
|
||||
comic_screen_size = screen_size
|
||||
|
||||
|
||||
class GenericEinkHD(GenericEink):
|
||||
|
||||
name = 'Generic e-ink HD'
|
||||
@ -550,6 +576,7 @@ class JetBook5Output(OutputProfile):
|
||||
screen_size = (480, 640)
|
||||
dpi = 168.451
|
||||
|
||||
|
||||
class SonyReaderLandscapeOutput(SonyReaderOutput):
|
||||
|
||||
name = 'Sony Reader Landscape'
|
||||
@ -561,6 +588,7 @@ class SonyReaderLandscapeOutput(SonyReaderOutput):
|
||||
screen_size = (784, 1012)
|
||||
comic_screen_size = (784, 1012)
|
||||
|
||||
|
||||
class MSReaderOutput(OutputProfile):
|
||||
|
||||
name = 'Microsoft Reader'
|
||||
@ -572,6 +600,7 @@ class MSReaderOutput(OutputProfile):
|
||||
fbase = 13
|
||||
fsizes = [10, 11, 13, 16, 18, 20, 22, 26]
|
||||
|
||||
|
||||
class MobipocketOutput(OutputProfile):
|
||||
|
||||
name = 'Mobipocket Books'
|
||||
@ -585,6 +614,7 @@ class MobipocketOutput(OutputProfile):
|
||||
fbase = 18
|
||||
fsizes = [14, 14, 16, 18, 20, 22, 24, 26]
|
||||
|
||||
|
||||
class HanlinV3Output(OutputProfile):
|
||||
|
||||
name = 'Hanlin V3'
|
||||
@ -597,6 +627,7 @@ class HanlinV3Output(OutputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class HanlinV5Output(HanlinV3Output):
|
||||
|
||||
name = 'Hanlin V5'
|
||||
@ -605,6 +636,7 @@ class HanlinV5Output(HanlinV3Output):
|
||||
|
||||
dpi = 200
|
||||
|
||||
|
||||
class CybookG3Output(OutputProfile):
|
||||
|
||||
name = 'Cybook G3'
|
||||
@ -618,6 +650,7 @@ class CybookG3Output(OutputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class CybookOpusOutput(SonyReaderOutput):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -632,6 +665,7 @@ class CybookOpusOutput(SonyReaderOutput):
|
||||
|
||||
epub_periodical_format = None
|
||||
|
||||
|
||||
class KindleOutput(OutputProfile):
|
||||
|
||||
name = 'Kindle'
|
||||
@ -651,6 +685,7 @@ class KindleOutput(OutputProfile):
|
||||
|
||||
mobi_ems_per_blockquote = 2.0
|
||||
|
||||
|
||||
class KindleDXOutput(OutputProfile):
|
||||
|
||||
name = 'Kindle DX'
|
||||
@ -668,6 +703,7 @@ class KindleDXOutput(OutputProfile):
|
||||
ratings_char = u'\u2605'
|
||||
mobi_ems_per_blockquote = 2.0
|
||||
|
||||
|
||||
class KindlePaperWhiteOutput(KindleOutput):
|
||||
|
||||
name = 'Kindle PaperWhite'
|
||||
@ -679,6 +715,7 @@ class KindlePaperWhiteOutput(KindleOutput):
|
||||
dpi = 212.0
|
||||
comic_screen_size = screen_size
|
||||
|
||||
|
||||
class KindleVoyageOutput(KindleOutput):
|
||||
|
||||
name = 'Kindle Voyage'
|
||||
@ -691,6 +728,7 @@ class KindleVoyageOutput(KindleOutput):
|
||||
dpi = 300.0
|
||||
comic_screen_size = screen_size
|
||||
|
||||
|
||||
class KindlePaperWhite3Output(KindleVoyageOutput):
|
||||
|
||||
name = 'Kindle PaperWhite 3'
|
||||
@ -713,6 +751,7 @@ class KindleFireOutput(KindleDXOutput):
|
||||
dpi = 169.0
|
||||
comic_screen_size = (570, 1016)
|
||||
|
||||
|
||||
class IlliadOutput(OutputProfile):
|
||||
|
||||
name = 'Illiad'
|
||||
@ -725,6 +764,7 @@ class IlliadOutput(OutputProfile):
|
||||
fbase = 12
|
||||
fsizes = [7.5, 9, 10, 12, 15.5, 20, 22, 24]
|
||||
|
||||
|
||||
class IRexDR1000Output(OutputProfile):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -739,6 +779,7 @@ class IRexDR1000Output(OutputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class IRexDR800Output(OutputProfile):
|
||||
|
||||
author = 'Eric Cronin'
|
||||
@ -753,6 +794,7 @@ class IRexDR800Output(OutputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class NookOutput(OutputProfile):
|
||||
|
||||
author = 'John Schember'
|
||||
@ -767,6 +809,7 @@ class NookOutput(OutputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
|
||||
class NookColorOutput(NookOutput):
|
||||
name = 'Nook Color'
|
||||
short_name = 'nook_color'
|
||||
@ -776,6 +819,7 @@ class NookColorOutput(NookOutput):
|
||||
comic_screen_size = (594, 900)
|
||||
dpi = 169
|
||||
|
||||
|
||||
class PocketBook900Output(OutputProfile):
|
||||
|
||||
author = 'Chris Lockfort'
|
||||
@ -787,6 +831,7 @@ class PocketBook900Output(OutputProfile):
|
||||
dpi = 150.0
|
||||
comic_screen_size = screen_size
|
||||
|
||||
|
||||
class PocketBookPro912Output(OutputProfile):
|
||||
|
||||
author = 'Daniele Pizzolli'
|
||||
|
@ -24,9 +24,11 @@ from calibre.constants import DEBUG
|
||||
|
||||
builtin_names = frozenset([p.name for p in builtin_plugins])
|
||||
|
||||
|
||||
class NameConflict(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def _config():
|
||||
c = Config('customize')
|
||||
c.add_opt('plugins', default={}, help=_('Installed plugins'))
|
||||
@ -58,6 +60,7 @@ def load_plugin(path_to_zip_file): # {{{
|
||||
|
||||
# Enable/disable plugins {{{
|
||||
|
||||
|
||||
def disable_plugin(plugin_or_name):
|
||||
x = getattr(plugin_or_name, 'name', plugin_or_name)
|
||||
plugin = find_plugin(x)
|
||||
@ -71,6 +74,7 @@ def disable_plugin(plugin_or_name):
|
||||
ep.remove(x)
|
||||
config['enabled_plugins'] = ep
|
||||
|
||||
|
||||
def enable_plugin(plugin_or_name):
|
||||
x = getattr(plugin_or_name, 'name', plugin_or_name)
|
||||
dp = config['disabled_plugins']
|
||||
@ -81,6 +85,7 @@ def enable_plugin(plugin_or_name):
|
||||
ep.add(x)
|
||||
config['enabled_plugins'] = ep
|
||||
|
||||
|
||||
def restore_plugin_state_to_default(plugin_or_name):
|
||||
x = getattr(plugin_or_name, 'name', plugin_or_name)
|
||||
dp = config['disabled_plugins']
|
||||
@ -96,6 +101,7 @@ default_disabled_plugins = set([
|
||||
'Overdrive', 'Douban Books', 'OZON.ru', 'Edelweiss', 'Google Images', 'Big Book Search',
|
||||
])
|
||||
|
||||
|
||||
def is_disabled(plugin):
|
||||
if plugin.name in config['enabled_plugins']:
|
||||
return False
|
||||
@ -111,6 +117,7 @@ _on_preprocess = {}
|
||||
_on_postprocess = {}
|
||||
_on_postadd = []
|
||||
|
||||
|
||||
def reread_filetype_plugins():
|
||||
global _on_import
|
||||
global _on_postimport
|
||||
@ -171,6 +178,7 @@ run_plugins_on_preprocess = functools.partial(_run_filetype_plugins,
|
||||
run_plugins_on_postprocess = functools.partial(_run_filetype_plugins,
|
||||
occasion='postprocess')
|
||||
|
||||
|
||||
def run_plugins_on_postimport(db, book_id, fmt):
|
||||
customization = config['plugin_customization']
|
||||
fmt = fmt.lower()
|
||||
@ -186,6 +194,7 @@ def run_plugins_on_postimport(db, book_id, fmt):
|
||||
plugin.name)
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def run_plugins_on_postadd(db, book_id, fmt_map):
|
||||
customization = config['plugin_customization']
|
||||
for plugin in _on_postadd:
|
||||
@ -203,22 +212,28 @@ def run_plugins_on_postadd(db, book_id, fmt_map):
|
||||
# }}}
|
||||
|
||||
# Plugin customization {{{
|
||||
|
||||
|
||||
def customize_plugin(plugin, custom):
|
||||
d = config['plugin_customization']
|
||||
d[plugin.name] = custom.strip()
|
||||
config['plugin_customization'] = d
|
||||
|
||||
|
||||
def plugin_customization(plugin):
|
||||
return config['plugin_customization'].get(plugin.name, '')
|
||||
|
||||
# }}}
|
||||
|
||||
# Input/Output profiles {{{
|
||||
|
||||
|
||||
def input_profiles():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, InputProfile):
|
||||
yield plugin
|
||||
|
||||
|
||||
def output_profiles():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, OutputProfile):
|
||||
@ -227,6 +242,7 @@ def output_profiles():
|
||||
|
||||
# Interface Actions # {{{
|
||||
|
||||
|
||||
def interface_actions():
|
||||
customization = config['plugin_customization']
|
||||
for plugin in _initialized_plugins:
|
||||
@ -238,6 +254,7 @@ def interface_actions():
|
||||
|
||||
# Preferences Plugins # {{{
|
||||
|
||||
|
||||
def preferences_plugins():
|
||||
customization = config['plugin_customization']
|
||||
for plugin in _initialized_plugins:
|
||||
@ -248,6 +265,8 @@ def preferences_plugins():
|
||||
# }}}
|
||||
|
||||
# Library Closed Plugins # {{{
|
||||
|
||||
|
||||
def available_library_closed_plugins():
|
||||
customization = config['plugin_customization']
|
||||
for plugin in _initialized_plugins:
|
||||
@ -256,6 +275,7 @@ def available_library_closed_plugins():
|
||||
plugin.site_customization = customization.get(plugin.name, '')
|
||||
yield plugin
|
||||
|
||||
|
||||
def has_library_closed_plugins():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, LibraryClosedPlugin):
|
||||
@ -266,6 +286,7 @@ def has_library_closed_plugins():
|
||||
|
||||
# Store Plugins # {{{
|
||||
|
||||
|
||||
def store_plugins():
|
||||
customization = config['plugin_customization']
|
||||
for plugin in _initialized_plugins:
|
||||
@ -273,17 +294,20 @@ def store_plugins():
|
||||
plugin.site_customization = customization.get(plugin.name, '')
|
||||
yield plugin
|
||||
|
||||
|
||||
def available_store_plugins():
|
||||
for plugin in store_plugins():
|
||||
if not is_disabled(plugin):
|
||||
yield plugin
|
||||
|
||||
|
||||
def stores():
|
||||
stores = set([])
|
||||
for plugin in store_plugins():
|
||||
stores.add(plugin.name)
|
||||
return stores
|
||||
|
||||
|
||||
def available_stores():
|
||||
stores = set([])
|
||||
for plugin in available_store_plugins():
|
||||
@ -295,6 +319,8 @@ def available_stores():
|
||||
# Metadata read/write {{{
|
||||
_metadata_readers = {}
|
||||
_metadata_writers = {}
|
||||
|
||||
|
||||
def reread_metadata_plugins():
|
||||
global _metadata_readers
|
||||
global _metadata_writers
|
||||
@ -308,6 +334,7 @@ def reread_metadata_plugins():
|
||||
for ft in plugin.file_types:
|
||||
_metadata_writers[ft].append(plugin)
|
||||
|
||||
|
||||
def metadata_readers():
|
||||
ans = set([])
|
||||
for plugins in _metadata_readers.values():
|
||||
@ -315,6 +342,7 @@ def metadata_readers():
|
||||
ans.add(plugin)
|
||||
return ans
|
||||
|
||||
|
||||
def metadata_writers():
|
||||
ans = set([])
|
||||
for plugins in _metadata_writers.values():
|
||||
@ -322,6 +350,7 @@ def metadata_writers():
|
||||
ans.add(plugin)
|
||||
return ans
|
||||
|
||||
|
||||
class QuickMetadata(object):
|
||||
|
||||
def __init__(self):
|
||||
@ -335,6 +364,7 @@ class QuickMetadata(object):
|
||||
|
||||
quick_metadata = QuickMetadata()
|
||||
|
||||
|
||||
class ApplyNullMetadata(object):
|
||||
|
||||
def __init__(self):
|
||||
@ -348,6 +378,7 @@ class ApplyNullMetadata(object):
|
||||
|
||||
apply_null_metadata = ApplyNullMetadata()
|
||||
|
||||
|
||||
class ForceIdentifiers(object):
|
||||
|
||||
def __init__(self):
|
||||
@ -361,6 +392,7 @@ class ForceIdentifiers(object):
|
||||
|
||||
force_identifiers = ForceIdentifiers()
|
||||
|
||||
|
||||
def get_file_type_metadata(stream, ftype):
|
||||
mi = MetaInformation(None, None)
|
||||
|
||||
@ -380,6 +412,7 @@ def get_file_type_metadata(stream, ftype):
|
||||
continue
|
||||
return mi
|
||||
|
||||
|
||||
def set_file_type_metadata(stream, mi, ftype, report_error=None):
|
||||
ftype = ftype.lower().strip()
|
||||
if ftype in _metadata_writers:
|
||||
@ -401,6 +434,7 @@ def set_file_type_metadata(stream, mi, ftype, report_error=None):
|
||||
else:
|
||||
report_error(mi, ftype, traceback.format_exc())
|
||||
|
||||
|
||||
def can_set_metadata(ftype):
|
||||
ftype = ftype.lower().strip()
|
||||
for plugin in _metadata_writers.get(ftype, ()):
|
||||
@ -412,6 +446,7 @@ def can_set_metadata(ftype):
|
||||
|
||||
# Add/remove plugins {{{
|
||||
|
||||
|
||||
def add_plugin(path_to_zip_file):
|
||||
make_config_dir()
|
||||
plugin = load_plugin(path_to_zip_file)
|
||||
@ -429,6 +464,7 @@ def add_plugin(path_to_zip_file):
|
||||
initialize_plugins()
|
||||
return plugin
|
||||
|
||||
|
||||
def remove_plugin(plugin_or_name):
|
||||
name = getattr(plugin_or_name, 'name', plugin_or_name)
|
||||
plugins = config['plugins']
|
||||
@ -453,11 +489,13 @@ def remove_plugin(plugin_or_name):
|
||||
|
||||
# Input/Output format plugins {{{
|
||||
|
||||
|
||||
def input_format_plugins():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, InputFormatPlugin):
|
||||
yield plugin
|
||||
|
||||
|
||||
def plugin_for_input_format(fmt):
|
||||
customization = config['plugin_customization']
|
||||
for plugin in input_format_plugins():
|
||||
@ -465,6 +503,7 @@ def plugin_for_input_format(fmt):
|
||||
plugin.site_customization = customization.get(plugin.name, None)
|
||||
return plugin
|
||||
|
||||
|
||||
def all_input_formats():
|
||||
formats = set()
|
||||
for plugin in input_format_plugins():
|
||||
@ -472,6 +511,7 @@ def all_input_formats():
|
||||
formats.add(format)
|
||||
return formats
|
||||
|
||||
|
||||
def available_input_formats():
|
||||
formats = set()
|
||||
for plugin in input_format_plugins():
|
||||
@ -487,6 +527,7 @@ def output_format_plugins():
|
||||
if isinstance(plugin, OutputFormatPlugin):
|
||||
yield plugin
|
||||
|
||||
|
||||
def plugin_for_output_format(fmt):
|
||||
customization = config['plugin_customization']
|
||||
for plugin in output_format_plugins():
|
||||
@ -494,6 +535,7 @@ def plugin_for_output_format(fmt):
|
||||
plugin.site_customization = customization.get(plugin.name, None)
|
||||
return plugin
|
||||
|
||||
|
||||
def available_output_formats():
|
||||
formats = set([])
|
||||
for plugin in output_format_plugins():
|
||||
@ -505,11 +547,13 @@ def available_output_formats():
|
||||
|
||||
# Catalog plugins {{{
|
||||
|
||||
|
||||
def catalog_plugins():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, CatalogPlugin):
|
||||
yield plugin
|
||||
|
||||
|
||||
def available_catalog_formats():
|
||||
formats = set([])
|
||||
for plugin in catalog_plugins():
|
||||
@ -518,6 +562,7 @@ def available_catalog_formats():
|
||||
formats.add(format)
|
||||
return formats
|
||||
|
||||
|
||||
def plugin_for_catalog_format(fmt):
|
||||
for plugin in catalog_plugins():
|
||||
if fmt.lower() in plugin.file_types:
|
||||
@ -526,6 +571,8 @@ def plugin_for_catalog_format(fmt):
|
||||
# }}}
|
||||
|
||||
# Device plugins {{{
|
||||
|
||||
|
||||
def device_plugins(include_disabled=False):
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, DevicePlugin):
|
||||
@ -536,6 +583,7 @@ def device_plugins(include_disabled=False):
|
||||
plugin.do_delayed_plugin_initialization()
|
||||
yield plugin
|
||||
|
||||
|
||||
def disabled_device_plugins():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, DevicePlugin):
|
||||
@ -545,6 +593,8 @@ def disabled_device_plugins():
|
||||
# }}}
|
||||
|
||||
# Metadata sources2 {{{
|
||||
|
||||
|
||||
def metadata_plugins(capabilities):
|
||||
capabilities = frozenset(capabilities)
|
||||
for plugin in all_metadata_plugins():
|
||||
@ -552,6 +602,7 @@ def metadata_plugins(capabilities):
|
||||
not is_disabled(plugin):
|
||||
yield plugin
|
||||
|
||||
|
||||
def all_metadata_plugins():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, Source):
|
||||
@ -559,6 +610,8 @@ def all_metadata_plugins():
|
||||
# }}}
|
||||
|
||||
# Viewer plugins {{{
|
||||
|
||||
|
||||
def all_viewer_plugins():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, ViewerPlugin):
|
||||
@ -566,6 +619,8 @@ def all_viewer_plugins():
|
||||
# }}}
|
||||
|
||||
# Editor plugins {{{
|
||||
|
||||
|
||||
def all_edit_book_tool_plugins():
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, EditBookToolPlugin):
|
||||
@ -576,6 +631,7 @@ def all_edit_book_tool_plugins():
|
||||
|
||||
_initialized_plugins = []
|
||||
|
||||
|
||||
def initialize_plugin(plugin, path_to_zip_file):
|
||||
try:
|
||||
p = plugin(path_to_zip_file)
|
||||
@ -587,10 +643,12 @@ def initialize_plugin(plugin, path_to_zip_file):
|
||||
raise InvalidPlugin((_('Initialization of plugin %s failed with traceback:')
|
||||
%tb) + '\n'+tb)
|
||||
|
||||
|
||||
def has_external_plugins():
|
||||
'True if there are updateable (zip file based) plugins'
|
||||
return bool(config['plugins'])
|
||||
|
||||
|
||||
def initialize_plugins(perf=False):
|
||||
global _initialized_plugins
|
||||
_initialized_plugins = []
|
||||
@ -638,6 +696,7 @@ def initialize_plugins(perf=False):
|
||||
|
||||
initialize_plugins()
|
||||
|
||||
|
||||
def initialized_plugins():
|
||||
for plugin in _initialized_plugins:
|
||||
yield plugin
|
||||
@ -646,6 +705,7 @@ def initialized_plugins():
|
||||
|
||||
# CLI {{{
|
||||
|
||||
|
||||
def build_plugin(path):
|
||||
from calibre import prints
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
@ -663,6 +723,7 @@ def build_plugin(path):
|
||||
os.remove(t.name)
|
||||
prints(u'Plugin updated:', plugin.name, plugin.version)
|
||||
|
||||
|
||||
def option_parser():
|
||||
parser = OptionParser(usage=_('''\
|
||||
%prog options
|
||||
@ -687,6 +748,7 @@ def option_parser():
|
||||
help=_('Disable the named plugin'))
|
||||
return parser
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
if len(args) < 2:
|
||||
|
@ -20,6 +20,7 @@ from calibre.customize import (Plugin, numeric_version, platform,
|
||||
# python 2.x that prevents importing from zip files in locations whose paths
|
||||
# have non ASCII characters
|
||||
|
||||
|
||||
def get_resources(zfp, name_or_list_of_names):
|
||||
'''
|
||||
Load resources from the plugin zip file
|
||||
@ -48,6 +49,7 @@ def get_resources(zfp, name_or_list_of_names):
|
||||
|
||||
return ans
|
||||
|
||||
|
||||
def get_icons(zfp, name_or_list_of_names):
|
||||
'''
|
||||
Load icons from the plugin zip file
|
||||
@ -83,6 +85,7 @@ def get_icons(zfp, name_or_list_of_names):
|
||||
|
||||
_translations_cache = {}
|
||||
|
||||
|
||||
def load_translations(namespace, zfp):
|
||||
null = object()
|
||||
trans = _translations_cache.get(zfp, null)
|
||||
@ -109,6 +112,7 @@ def load_translations(namespace, zfp):
|
||||
namespace['_'] = trans.ugettext
|
||||
namespace['ngettext'] = trans.ungettext
|
||||
|
||||
|
||||
class PluginLoader(object):
|
||||
|
||||
def __init__(self):
|
||||
|
@ -9,6 +9,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
SPOOL_SIZE = 30*1024*1024
|
||||
|
||||
|
||||
def _get_next_series_num_for_list(series_indices, unwrap=True):
|
||||
from calibre.utils.config_base import tweaks
|
||||
from math import ceil, floor
|
||||
@ -39,6 +40,7 @@ def _get_next_series_num_for_list(series_indices, unwrap=True):
|
||||
return float(tweaks['series_index_auto_increment'])
|
||||
return 1.0
|
||||
|
||||
|
||||
def _get_series_values(val):
|
||||
import re
|
||||
series_index_pat = re.compile(r'(.*)\s+\[([.0-9]+)\]$')
|
||||
@ -54,6 +56,7 @@ def _get_series_values(val):
|
||||
pass
|
||||
return (val, None)
|
||||
|
||||
|
||||
def get_data_as_dict(self, prefix=None, authors_as_string=False, ids=None, convert_to_local_tz=True):
|
||||
'''
|
||||
Return all metadata stored in the database as a dict. Includes paths to
|
||||
|
@ -12,20 +12,25 @@ from future_builtins import map
|
||||
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
|
||||
|
||||
def splitext(path):
|
||||
key, ext = os.path.splitext(path)
|
||||
return key, ext[1:].lower()
|
||||
|
||||
|
||||
def formats_ok(formats):
|
||||
return len(formats) > 0
|
||||
|
||||
|
||||
def path_ok(path):
|
||||
return not os.path.isdir(path) and os.access(path, os.R_OK)
|
||||
|
||||
|
||||
def compile_glob(pat):
|
||||
import fnmatch
|
||||
return re.compile(fnmatch.translate(pat), flags=re.I)
|
||||
|
||||
|
||||
def compile_rule(rule):
|
||||
mt = rule['match_type']
|
||||
if 'with' in mt:
|
||||
@ -45,6 +50,7 @@ def compile_rule(rule):
|
||||
ans = lambda filename: not func(filename)
|
||||
return ans, rule['action'] == 'add'
|
||||
|
||||
|
||||
def filter_filename(compiled_rules, filename):
|
||||
for q, action in compiled_rules:
|
||||
if q(filename):
|
||||
@ -52,6 +58,7 @@ def filter_filename(compiled_rules, filename):
|
||||
|
||||
_metadata_extensions = None
|
||||
|
||||
|
||||
def metadata_extensions():
|
||||
# Set of all known book extensions + OPF (the OPF is used to read metadata,
|
||||
# but not actually added)
|
||||
@ -60,6 +67,7 @@ def metadata_extensions():
|
||||
_metadata_extensions = frozenset(map(unicode, BOOK_EXTENSIONS)) | {'opf'}
|
||||
return _metadata_extensions
|
||||
|
||||
|
||||
def listdir(root, sort_by_mtime=False):
|
||||
items = (os.path.join(root, x) for x in os.listdir(root))
|
||||
if sort_by_mtime:
|
||||
@ -74,12 +82,14 @@ def listdir(root, sort_by_mtime=False):
|
||||
if path_ok(path):
|
||||
yield path
|
||||
|
||||
|
||||
def allow_path(path, ext, compiled_rules):
|
||||
ans = filter_filename(compiled_rules, os.path.basename(path))
|
||||
if ans is None:
|
||||
ans = ext in metadata_extensions()
|
||||
return ans
|
||||
|
||||
|
||||
def find_books_in_directory(dirpath, single_book_per_directory, compiled_rules=(), listdir_impl=listdir):
|
||||
dirpath = os.path.abspath(dirpath)
|
||||
if single_book_per_directory:
|
||||
@ -101,6 +111,7 @@ def find_books_in_directory(dirpath, single_book_per_directory, compiled_rules=(
|
||||
if formats_ok(formats):
|
||||
yield list(formats.itervalues())
|
||||
|
||||
|
||||
def import_book_directory_multiple(db, dirpath, callback=None,
|
||||
added_ids=None, compiled_rules=()):
|
||||
from calibre.ebooks.metadata.meta import metadata_from_formats
|
||||
@ -121,6 +132,7 @@ def import_book_directory_multiple(db, dirpath, callback=None,
|
||||
break
|
||||
return duplicates
|
||||
|
||||
|
||||
def import_book_directory(db, dirpath, callback=None, added_ids=None, compiled_rules=()):
|
||||
from calibre.ebooks.metadata.meta import metadata_from_formats
|
||||
dirpath = os.path.abspath(dirpath)
|
||||
@ -140,6 +152,7 @@ def import_book_directory(db, dirpath, callback=None, added_ids=None, compiled_r
|
||||
if callable(callback):
|
||||
callback(mi.title)
|
||||
|
||||
|
||||
def recursive_import(db, root, single_book_per_directory=True,
|
||||
callback=None, added_ids=None, compiled_rules=()):
|
||||
root = os.path.abspath(root)
|
||||
@ -156,6 +169,7 @@ def recursive_import(db, root, single_book_per_directory=True,
|
||||
break
|
||||
return duplicates
|
||||
|
||||
|
||||
def add_catalog(cache, path, title, dbapi=None):
|
||||
from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
@ -189,6 +203,7 @@ def add_catalog(cache, path, title, dbapi=None):
|
||||
|
||||
return db_id, new_book_added
|
||||
|
||||
|
||||
def add_news(cache, path, arg, dbapi=None):
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
from calibre.utils.date import utcnow
|
||||
|
@ -65,6 +65,7 @@ class DynamicFilter(object): # {{{
|
||||
self.ids = frozenset(ids)
|
||||
# }}}
|
||||
|
||||
|
||||
class DBPrefs(dict): # {{{
|
||||
|
||||
'Store preferences as key:value pairs in the db'
|
||||
@ -160,6 +161,8 @@ class DBPrefs(dict): # {{{
|
||||
# }}}
|
||||
|
||||
# Extra collators {{{
|
||||
|
||||
|
||||
def pynocase(one, two, encoding='utf-8'):
|
||||
if isbytestring(one):
|
||||
try:
|
||||
@ -173,11 +176,13 @@ def pynocase(one, two, encoding='utf-8'):
|
||||
pass
|
||||
return cmp(one.lower(), two.lower())
|
||||
|
||||
|
||||
def _author_to_author_sort(x):
|
||||
if not x:
|
||||
return ''
|
||||
return author_to_author_sort(x.replace('|', ','))
|
||||
|
||||
|
||||
def icu_collator(s1, s2):
|
||||
return cmp(sort_key(force_unicode(s1, 'utf-8')),
|
||||
sort_key(force_unicode(s2, 'utf-8')))
|
||||
@ -185,6 +190,8 @@ def icu_collator(s1, s2):
|
||||
# }}}
|
||||
|
||||
# Unused aggregators {{{
|
||||
|
||||
|
||||
def Concatenate(sep=','):
|
||||
'''String concatenation aggregator for sqlite'''
|
||||
|
||||
@ -199,6 +206,7 @@ def Concatenate(sep=','):
|
||||
|
||||
return ([], step, finalize)
|
||||
|
||||
|
||||
def SortedConcatenate(sep=','):
|
||||
'''String concatenation aggregator for sqlite, sorted by supplied index'''
|
||||
|
||||
@ -213,6 +221,7 @@ def SortedConcatenate(sep=','):
|
||||
|
||||
return ({}, step, finalize)
|
||||
|
||||
|
||||
def IdentifiersConcat():
|
||||
'''String concatenation aggregator for the identifiers map'''
|
||||
|
||||
@ -224,6 +233,7 @@ def IdentifiersConcat():
|
||||
|
||||
return ([], step, finalize)
|
||||
|
||||
|
||||
def AumSortedConcatenate():
|
||||
'''String concatenation aggregator for the author sort map'''
|
||||
|
||||
@ -244,6 +254,7 @@ def AumSortedConcatenate():
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class Connection(apsw.Connection): # {{{
|
||||
|
||||
BUSY_TIMEOUT = 10000 # milliseconds
|
||||
@ -304,6 +315,7 @@ class Connection(apsw.Connection): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class DB(object):
|
||||
|
||||
PATH_LIMIT = 40 if iswindows else 100
|
||||
@ -1626,6 +1638,7 @@ class DB(object):
|
||||
|
||||
def get_custom_book_data(self, name, book_ids, default=None):
|
||||
book_ids = frozenset(book_ids)
|
||||
|
||||
def safe_load(val):
|
||||
try:
|
||||
return json.loads(val, object_hook=from_json)
|
||||
|
@ -13,9 +13,11 @@ from threading import Thread, Event
|
||||
from calibre import prints
|
||||
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||
|
||||
|
||||
class Abort(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MetadataBackup(Thread):
|
||||
'''
|
||||
Continuously backup changed metadata into OPF files
|
||||
|
@ -35,20 +35,24 @@ from calibre.utils.config import prefs, tweaks
|
||||
from calibre.utils.date import now as nowf, utcnow, UNDEFINED_DATE
|
||||
from calibre.utils.icu import sort_key
|
||||
|
||||
|
||||
def api(f):
|
||||
f.is_cache_api = True
|
||||
return f
|
||||
|
||||
|
||||
def read_api(f):
|
||||
f = api(f)
|
||||
f.is_read_api = True
|
||||
return f
|
||||
|
||||
|
||||
def write_api(f):
|
||||
f = api(f)
|
||||
f.is_read_api = False
|
||||
return f
|
||||
|
||||
|
||||
def wrap_simple(lock, func):
|
||||
@wraps(func)
|
||||
def call_func_with_lock(*args, **kwargs):
|
||||
@ -62,6 +66,7 @@ def wrap_simple(lock, func):
|
||||
return func(*args, **kwargs)
|
||||
return call_func_with_lock
|
||||
|
||||
|
||||
def run_import_plugins(path_or_stream, fmt):
|
||||
fmt = fmt.lower()
|
||||
if hasattr(path_or_stream, 'seek'):
|
||||
@ -74,6 +79,7 @@ def run_import_plugins(path_or_stream, fmt):
|
||||
path = path_or_stream
|
||||
return run_plugins_on_import(path, fmt)
|
||||
|
||||
|
||||
def _add_newbook_tag(mi):
|
||||
tags = prefs['new_book_tags']
|
||||
if tags:
|
||||
@ -86,6 +92,7 @@ def _add_newbook_tag(mi):
|
||||
|
||||
dynamic_category_preferences = frozenset({'grouped_search_make_user_categories', 'grouped_search_terms', 'user_categories'})
|
||||
|
||||
|
||||
class Cache(object):
|
||||
|
||||
'''
|
||||
@ -804,6 +811,7 @@ class Cache(object):
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
except:
|
||||
return ()
|
||||
|
||||
def verify(fmt):
|
||||
try:
|
||||
name = self.fields['formats'].format_fname(book_id, fmt)
|
||||
@ -918,6 +926,7 @@ class Cache(object):
|
||||
return virtual_fields[fm.get(field, field)].sort_keys_for_books(get_metadata, lang_map)
|
||||
if is_series:
|
||||
idx_func = self.fields[idx].sort_keys_for_books(get_metadata, lang_map)
|
||||
|
||||
def skf(book_id):
|
||||
return (func(book_id), idx_func(book_id))
|
||||
return skf
|
||||
@ -2160,6 +2169,7 @@ class Cache(object):
|
||||
if progress is not None:
|
||||
progress(_('Completed'), total, total)
|
||||
|
||||
|
||||
def import_library(library_key, importer, library_path, progress=None, abort=None):
|
||||
from calibre.db.backend import DB
|
||||
metadata = importer.metadata[library_key]
|
||||
|
@ -17,6 +17,7 @@ from calibre.utils.icu import sort_key, collation_order
|
||||
|
||||
CATEGORY_SORTS = ('name', 'popularity', 'rating') # This has to be a tuple not a set
|
||||
|
||||
|
||||
class Tag(object):
|
||||
|
||||
__slots__ = ('name', 'original_name', 'id', 'count', 'state', 'is_hierarchical',
|
||||
@ -51,6 +52,7 @@ class Tag(object):
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
def find_categories(field_metadata):
|
||||
for category, cat in field_metadata.iteritems():
|
||||
if (cat['is_category'] and cat['kind'] not in {'user', 'search'}):
|
||||
@ -59,6 +61,7 @@ def find_categories(field_metadata):
|
||||
cat['display'].get('make_category', False)):
|
||||
yield (category, cat['is_multiple'].get('cache_to_list', None), True)
|
||||
|
||||
|
||||
def create_tag_class(category, fm):
|
||||
cat = fm[category]
|
||||
dt = cat['datatype']
|
||||
@ -77,6 +80,7 @@ def create_tag_class(category, fm):
|
||||
return partial(Tag, use_sort_as_name=use_sort_as_name,
|
||||
is_editable=is_editable, category=category)
|
||||
|
||||
|
||||
def clean_user_categories(dbcache):
|
||||
user_cats = dbcache.pref('user_categories', {})
|
||||
new_cats = {}
|
||||
@ -108,6 +112,7 @@ category_sort_keys[True]['name'] = \
|
||||
category_sort_keys[False]['name'] = \
|
||||
lambda x:sort_key(x.sort or x.name)
|
||||
|
||||
|
||||
def get_categories(dbcache, sort='name', book_ids=None, first_letter_sort=False):
|
||||
if sort not in CATEGORY_SORTS:
|
||||
raise ValueError('sort ' + sort + ' not a valid value')
|
||||
|
@ -14,6 +14,7 @@ from calibre.ptempfile import remove_dir
|
||||
from calibre.utils.filenames import remove_dir_if_empty
|
||||
from calibre.utils.recycle_bin import delete_tree, delete_file
|
||||
|
||||
|
||||
class DeleteService(Thread):
|
||||
|
||||
''' Provide a blocking file delete implementation with support for the
|
||||
@ -136,6 +137,8 @@ class DeleteService(Thread):
|
||||
shutil.rmtree(tdir)
|
||||
|
||||
__ds = None
|
||||
|
||||
|
||||
def delete_service():
|
||||
global __ds
|
||||
if __ds is None:
|
||||
@ -143,12 +146,14 @@ def delete_service():
|
||||
__ds.start()
|
||||
return __ds
|
||||
|
||||
|
||||
def shutdown(timeout=20):
|
||||
global __ds
|
||||
if __ds is not None:
|
||||
__ds.shutdown(timeout)
|
||||
__ds = None
|
||||
|
||||
|
||||
def has_jobs():
|
||||
global __ds
|
||||
if __ds is not None:
|
||||
|
@ -21,17 +21,20 @@ from calibre.utils.icu import sort_key
|
||||
from calibre.utils.date import UNDEFINED_DATE, clean_date_for_sort, parse_date
|
||||
from calibre.utils.localization import calibre_langcode_to_name
|
||||
|
||||
|
||||
def bool_sort_key(bools_are_tristate):
|
||||
return (lambda x:{True: 1, False: 2, None: 3}.get(x, 3)) if bools_are_tristate else lambda x:{True: 1, False: 2, None: 2}.get(x, 2)
|
||||
|
||||
IDENTITY = lambda x: x
|
||||
|
||||
|
||||
class InvalidLinkTable(Exception):
|
||||
|
||||
def __init__(self, name):
|
||||
Exception.__init__(self, name)
|
||||
self.field_name = name
|
||||
|
||||
|
||||
class Field(object):
|
||||
|
||||
is_many = False
|
||||
@ -166,6 +169,7 @@ class Field(object):
|
||||
ans.append(c)
|
||||
return ans
|
||||
|
||||
|
||||
class OneToOneField(Field):
|
||||
|
||||
def for_book(self, book_id, default_value=None):
|
||||
@ -193,6 +197,7 @@ class OneToOneField(Field):
|
||||
for book_id in candidates:
|
||||
yield cbm.get(book_id, default_value), {book_id}
|
||||
|
||||
|
||||
class CompositeField(OneToOneField):
|
||||
|
||||
is_composite = True
|
||||
@ -339,6 +344,7 @@ class CompositeField(OneToOneField):
|
||||
ans.add(book_id)
|
||||
return ans
|
||||
|
||||
|
||||
class OnDeviceField(OneToOneField):
|
||||
|
||||
def __init__(self, name, table, bools_are_tristate):
|
||||
@ -403,6 +409,7 @@ class OnDeviceField(OneToOneField):
|
||||
for val, book_ids in val_map.iteritems():
|
||||
yield val, book_ids
|
||||
|
||||
|
||||
class LazySortMap(object):
|
||||
|
||||
__slots__ = ('default_sort_key', 'sort_key_func', 'id_map', 'cache')
|
||||
@ -469,6 +476,7 @@ class ManyToOneField(Field):
|
||||
except KeyError:
|
||||
raise InvalidLinkTable(self.name)
|
||||
|
||||
|
||||
class ManyToManyField(Field):
|
||||
|
||||
is_many = True
|
||||
@ -534,6 +542,7 @@ class ManyToManyField(Field):
|
||||
except KeyError:
|
||||
raise InvalidLinkTable(self.name)
|
||||
|
||||
|
||||
class IdentifiersField(ManyToManyField):
|
||||
|
||||
def for_book(self, book_id, default_value=None):
|
||||
@ -569,6 +578,7 @@ class IdentifiersField(ManyToManyField):
|
||||
ans.append(c)
|
||||
return ans
|
||||
|
||||
|
||||
class AuthorsField(ManyToManyField):
|
||||
|
||||
def author_data(self, author_id):
|
||||
@ -588,6 +598,7 @@ class AuthorsField(ManyToManyField):
|
||||
return ' & '.join(self.table.asort_map[k] for k in
|
||||
self.table.book_col_map[book_id])
|
||||
|
||||
|
||||
class FormatsField(ManyToManyField):
|
||||
|
||||
def for_book(self, book_id, default_value=None):
|
||||
@ -618,6 +629,7 @@ class FormatsField(ManyToManyField):
|
||||
ans.append(c)
|
||||
return ans
|
||||
|
||||
|
||||
class LazySeriesSortMap(object):
|
||||
|
||||
__slots__ = ('default_sort_key', 'sort_key_func', 'id_map', 'cache')
|
||||
@ -638,12 +650,14 @@ class LazySeriesSortMap(object):
|
||||
val = self.cache[(item_id, lang)] = self.default_sort_key
|
||||
return val
|
||||
|
||||
|
||||
class SeriesField(ManyToOneField):
|
||||
|
||||
def sort_keys_for_books(self, get_metadata, lang_map):
|
||||
sso = tweaks['title_series_sorting']
|
||||
ssk = self._sort_key
|
||||
ts = title_sort
|
||||
|
||||
def sk(val, lang):
|
||||
return ssk(ts(val, order=sso, lang=lang))
|
||||
sk_map = LazySeriesSortMap(self._default_sort_key, sk, self.table.id_map)
|
||||
@ -718,6 +732,7 @@ class TagsField(ManyToManyField):
|
||||
ans.append(c)
|
||||
return ans
|
||||
|
||||
|
||||
def create_field(name, table, bools_are_tristate):
|
||||
cls = {
|
||||
ONE_ONE: OneToOneField,
|
||||
|
@ -23,6 +23,7 @@ Speeds up calibre startup with large libraries/libraries on a network share,
|
||||
with a composite custom column.
|
||||
'''
|
||||
|
||||
|
||||
def resolved(f):
|
||||
@wraps(f)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
@ -32,6 +33,7 @@ def resolved(f):
|
||||
return f(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
class MutableBase(object):
|
||||
|
||||
@resolved
|
||||
@ -87,6 +89,7 @@ class FormatMetadata(MutableBase, MutableMapping):
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class FormatsList(MutableBase, MutableSequence):
|
||||
|
||||
def __init__(self, formats, format_metadata):
|
||||
@ -106,6 +109,7 @@ class FormatsList(MutableBase, MutableSequence):
|
||||
ga = object.__getattribute__
|
||||
sa = object.__setattr__
|
||||
|
||||
|
||||
def simple_getter(field, default_value=None):
|
||||
def func(dbref, book_id, cache):
|
||||
try:
|
||||
@ -116,6 +120,7 @@ def simple_getter(field, default_value=None):
|
||||
return ret
|
||||
return func
|
||||
|
||||
|
||||
def pp_getter(field, postprocess, default_value=None):
|
||||
def func(dbref, book_id, cache):
|
||||
try:
|
||||
@ -126,6 +131,7 @@ def pp_getter(field, postprocess, default_value=None):
|
||||
return ret
|
||||
return func
|
||||
|
||||
|
||||
def adata_getter(field):
|
||||
def func(dbref, book_id, cache):
|
||||
try:
|
||||
@ -140,6 +146,7 @@ def adata_getter(field):
|
||||
return {adata[i]['name']:adata[i][k] for i in author_ids}
|
||||
return func
|
||||
|
||||
|
||||
def dt_getter(field):
|
||||
def func(dbref, book_id, cache):
|
||||
try:
|
||||
@ -150,6 +157,7 @@ def dt_getter(field):
|
||||
return ret
|
||||
return func
|
||||
|
||||
|
||||
def item_getter(field, default_value=None, key=0):
|
||||
def func(dbref, book_id, cache):
|
||||
try:
|
||||
@ -163,6 +171,7 @@ def item_getter(field, default_value=None, key=0):
|
||||
return default_value
|
||||
return func
|
||||
|
||||
|
||||
def fmt_getter(field):
|
||||
def func(dbref, book_id, cache):
|
||||
try:
|
||||
@ -179,6 +188,7 @@ def fmt_getter(field):
|
||||
return format_metadata
|
||||
return func
|
||||
|
||||
|
||||
def approx_fmts_getter(dbref, book_id, cache):
|
||||
try:
|
||||
return cache['formats']
|
||||
@ -187,6 +197,7 @@ def approx_fmts_getter(dbref, book_id, cache):
|
||||
cache['formats'] = ret = list(db.field_for('formats', book_id))
|
||||
return ret
|
||||
|
||||
|
||||
def series_index_getter(field='series'):
|
||||
def func(dbref, book_id, cache):
|
||||
try:
|
||||
@ -202,6 +213,7 @@ def series_index_getter(field='series'):
|
||||
return ret
|
||||
return func
|
||||
|
||||
|
||||
def has_cover_getter(dbref, book_id, cache):
|
||||
try:
|
||||
return cache['has_cover']
|
||||
@ -211,6 +223,8 @@ def has_cover_getter(dbref, book_id, cache):
|
||||
return ret
|
||||
|
||||
fmt_custom = lambda x:list(x) if isinstance(x, tuple) else x
|
||||
|
||||
|
||||
def custom_getter(field, dbref, book_id, cache):
|
||||
try:
|
||||
return cache[field]
|
||||
@ -219,6 +233,7 @@ def custom_getter(field, dbref, book_id, cache):
|
||||
cache[field] = ret = fmt_custom(db.field_for(field, book_id))
|
||||
return ret
|
||||
|
||||
|
||||
def composite_getter(mi, field, dbref, book_id, cache, formatter, template_cache):
|
||||
try:
|
||||
return cache[field]
|
||||
@ -239,6 +254,7 @@ def composite_getter(mi, field, dbref, book_id, cache, formatter, template_cache
|
||||
return 'ERROR WHILE EVALUATING: %s' % field
|
||||
return ret
|
||||
|
||||
|
||||
def virtual_libraries_getter(dbref, book_id, cache):
|
||||
try:
|
||||
return cache['virtual_libraries']
|
||||
@ -248,6 +264,7 @@ def virtual_libraries_getter(dbref, book_id, cache):
|
||||
ret = cache['virtual_libraries'] = ', '.join(vls)
|
||||
return ret
|
||||
|
||||
|
||||
def user_categories_getter(proxy_metadata):
|
||||
cache = ga(proxy_metadata, '_cache')
|
||||
try:
|
||||
@ -293,6 +310,7 @@ for field in ('formats', 'format_metadata'):
|
||||
getters[field] = fmt_getter(field)
|
||||
# }}}
|
||||
|
||||
|
||||
class ProxyMetadata(Metadata):
|
||||
|
||||
def __init__(self, db, book_id, formatter=None):
|
||||
|
@ -24,6 +24,7 @@ from calibre.db.write import clean_identifier, get_series_values
|
||||
from calibre.utils.date import utcnow
|
||||
from calibre.utils.search_query_parser import set_saved_searches
|
||||
|
||||
|
||||
def cleanup_tags(tags):
|
||||
tags = [x.strip().replace(',', ';') for x in tags if x.strip()]
|
||||
tags = [x.decode(preferred_encoding, 'replace')
|
||||
@ -36,6 +37,7 @@ def cleanup_tags(tags):
|
||||
ans.append(tag)
|
||||
return ans
|
||||
|
||||
|
||||
def create_backend(
|
||||
library_path, default_prefs=None, read_only=False,
|
||||
progress_callback=lambda x, y:True, restore_all_prefs=False,
|
||||
@ -45,6 +47,7 @@ def create_backend(
|
||||
progress_callback=progress_callback,
|
||||
load_user_formatter_functions=load_user_formatter_functions)
|
||||
|
||||
|
||||
class LibraryDatabase(object):
|
||||
|
||||
''' Emulate the old LibraryDatabase2 interface '''
|
||||
@ -748,6 +751,7 @@ for prop in ('author_sort', 'authors', 'comment', 'comments', 'publisher', 'max_
|
||||
def getter(prop):
|
||||
fm = {'comment':'comments', 'metadata_last_modified':
|
||||
'last_modified', 'title_sort':'sort', 'max_size':'size'}.get(prop, prop)
|
||||
|
||||
def func(self, index, index_is_id=False):
|
||||
return self.get_property(index, index_is_id=index_is_id, loc=self.FIELD_MAP[fm])
|
||||
return func
|
||||
@ -793,6 +797,7 @@ for field in (
|
||||
if has_case_change:
|
||||
field = field[1:]
|
||||
acc = field == 'series'
|
||||
|
||||
def func(self, book_id, val, notify=True, commit=True, allow_case_change=acc):
|
||||
ret = self.new_api.set_field(field, {book_id:val}, allow_case_change=allow_case_change)
|
||||
if notify:
|
||||
@ -804,6 +809,7 @@ for field in (
|
||||
else:
|
||||
null_field = field in {'title', 'sort', 'uuid'}
|
||||
retval = (True if field == 'sort' else None)
|
||||
|
||||
def func(self, book_id, val, notify=True, commit=True):
|
||||
if not val and null_field:
|
||||
return (False if field == 'sort' else None)
|
||||
@ -857,6 +863,7 @@ LibraryDatabase.get_author_id = MT(
|
||||
for field in ('tags', 'series', 'publishers', 'ratings', 'languages'):
|
||||
def getter(field):
|
||||
fname = field[:-1] if field in {'publishers', 'ratings'} else field
|
||||
|
||||
def func(self):
|
||||
return [[tid, tag] for tid, tag in self.new_api.get_id_map(fname).iteritems()]
|
||||
return func
|
||||
@ -865,6 +872,7 @@ for field in ('tags', 'series', 'publishers', 'ratings', 'languages'):
|
||||
for field in ('author', 'tag', 'series'):
|
||||
def getter(field):
|
||||
field = field if field == 'series' else (field+'s')
|
||||
|
||||
def func(self, item_id):
|
||||
return self.new_api.get_item_name(field, item_id)
|
||||
return func
|
||||
@ -873,6 +881,7 @@ for field in ('author', 'tag', 'series'):
|
||||
for field in ('publisher', 'series', 'tag'):
|
||||
def getter(field):
|
||||
fname = 'tags' if field == 'tag' else field
|
||||
|
||||
def func(self, item_id):
|
||||
self.new_api.remove_items(fname, (item_id,))
|
||||
return func
|
||||
@ -888,6 +897,7 @@ for func in (
|
||||
def getter(func):
|
||||
if func.startswith('!'):
|
||||
func = func[1:]
|
||||
|
||||
def meth(self, include_composites=True):
|
||||
return getattr(self.field_metadata, func)(include_composites=include_composites)
|
||||
elif func == 'search_term_to_field_key':
|
||||
|
@ -11,6 +11,7 @@ import traceback, sys
|
||||
from threading import Lock, Condition, current_thread
|
||||
from calibre.utils.config_base import tweaks
|
||||
|
||||
|
||||
class LockingError(RuntimeError):
|
||||
|
||||
is_locking_error = True
|
||||
@ -19,9 +20,11 @@ class LockingError(RuntimeError):
|
||||
RuntimeError.__init__(self, msg)
|
||||
self.locking_debug_msg = extra
|
||||
|
||||
|
||||
class DowngradeLockError(LockingError):
|
||||
pass
|
||||
|
||||
|
||||
def create_locks():
|
||||
'''
|
||||
Return a pair of locks: (read_lock, write_lock)
|
||||
@ -48,6 +51,7 @@ def create_locks():
|
||||
wrapper = DebugRWLockWrapper if tweaks.get('newdb_debug_locking', False) else RWLockWrapper
|
||||
return wrapper(l), wrapper(l, is_shared=False)
|
||||
|
||||
|
||||
class SHLock(object): # {{{
|
||||
'''
|
||||
Shareable lock class. Used to implement the Multiple readers-single writer
|
||||
@ -207,6 +211,7 @@ class SHLock(object): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class RWLockWrapper(object):
|
||||
|
||||
def __init__(self, shlock, is_shared=True):
|
||||
@ -225,6 +230,7 @@ class RWLockWrapper(object):
|
||||
def owns_lock(self):
|
||||
return self._shlock.owns_lock()
|
||||
|
||||
|
||||
class DebugRWLockWrapper(RWLockWrapper):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -249,6 +255,7 @@ class DebugRWLockWrapper(RWLockWrapper):
|
||||
__enter__ = acquire
|
||||
__exit__ = release
|
||||
|
||||
|
||||
class SafeReadLock(object):
|
||||
|
||||
def __init__(self, read_lock):
|
||||
|
@ -22,6 +22,7 @@ NON_EBOOK_EXTENSIONS = frozenset([
|
||||
'opf', 'swp', 'swo'
|
||||
])
|
||||
|
||||
|
||||
class Restorer(Cache):
|
||||
|
||||
def __init__(self, library_path, default_prefs=None, restore_all_prefs=False, progress_callback=lambda x, y:True):
|
||||
@ -35,6 +36,7 @@ class Restorer(Cache):
|
||||
def no_op(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class Restore(Thread):
|
||||
|
||||
def __init__(self, library_path, progress_callback=None):
|
||||
|
@ -12,6 +12,7 @@ import os
|
||||
from calibre import prints
|
||||
from calibre.utils.date import isoformat, DEFAULT_DATE
|
||||
|
||||
|
||||
class SchemaUpgrade(object):
|
||||
|
||||
def __init__(self, db, library_path, field_metadata):
|
||||
@ -421,6 +422,7 @@ class SchemaUpgrade(object):
|
||||
'Cache has_cover'
|
||||
self.db.execute('ALTER TABLE books ADD COLUMN has_cover BOOL DEFAULT 0')
|
||||
data = self.db.get('SELECT id,path FROM books', all=True)
|
||||
|
||||
def has_cover(path):
|
||||
if path:
|
||||
path = os.path.join(self.library_path, path.replace('/', os.sep),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user