Merge from trunk

This commit is contained in:
Charles Haley 2011-05-20 08:52:38 +01:00
commit f1651fe966
22 changed files with 803 additions and 346 deletions

View File

@ -31,3 +31,4 @@ nbproject/
.pydevproject .pydevproject
.settings/ .settings/
*.DS_Store *.DS_Store
calibre_plugins/

View File

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = u'2011, Silviu Cotoar\u0103'
'''
dilemaveche.ro
'''
from calibre.web.feeds.news import BasicNewsRecipe
class DilemaVeche(BasicNewsRecipe):
title = u'Dilema Veche'
__author__ = u'Silviu Cotoar\u0103'
description = u'Sunt vechi, domnule!'
publisher = u'Dilema Veche'
oldest_article = 50
language = 'ro'
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
category = 'Ziare'
encoding = 'utf-8'
cover_url = 'http://www.dilemaveche.ro/sites/all/themes/dilema/theme/dilema_two/layouter/dilema_two_homepage/logo.png'
conversion_options = {
'comments' : description
,'tags' : category
,'language' : language
,'publisher' : publisher
}
keep_only_tags = [
dict(name='h1', attrs={'class':'art_title'})
, dict(name='h1', attrs={'class':'art_title online'})
, dict(name='div', attrs={'class':'item'})
, dict(name='div', attrs={'class':'art_content'})
]
remove_tags = [
dict(name='div', attrs={'class':['article_details']})
, dict(name='div', attrs={'class':['controale']})
, dict(name='div', attrs={'class':['art_related_left']})
]
remove_tags_after = [
dict(name='div', attrs={'class':['article_details']})
]
feeds = [
(u'Feeds', u'http://www.dilemaveche.ro/rss.xml')
]
def preprocess_html(self, soup):
return self.adeify_images(soup)

View File

@ -6,13 +6,13 @@ __copyright__ = 'Copyright 2010 Starson17'
www.gocomics.com www.gocomics.com
''' '''
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
import mechanize import mechanize, re
class GoComics(BasicNewsRecipe): class GoComics(BasicNewsRecipe):
title = 'GoComics' title = 'GoComics'
__author__ = 'Starson17' __author__ = 'Starson17'
__version__ = '1.03' __version__ = '1.05'
__date__ = '09 October 2010' __date__ = '19 may 2011'
description = u'200+ Comics - Customize for more days/comics: Defaults to 7 days, 25 comics - 20 general, 5 editorial.' description = u'200+ Comics - Customize for more days/comics: Defaults to 7 days, 25 comics - 20 general, 5 editorial.'
category = 'news, comics' category = 'news, comics'
language = 'en' language = 'en'
@ -20,6 +20,7 @@ class GoComics(BasicNewsRecipe):
no_stylesheets = True no_stylesheets = True
remove_javascript = True remove_javascript = True
cover_url = 'http://paulbuckley14059.files.wordpress.com/2008/06/calvin-and-hobbes.jpg' cover_url = 'http://paulbuckley14059.files.wordpress.com/2008/06/calvin-and-hobbes.jpg'
remove_attributes = ['style']
####### USER PREFERENCES - COMICS, IMAGE SIZE AND NUMBER OF COMICS TO RETRIEVE ######## ####### USER PREFERENCES - COMICS, IMAGE SIZE AND NUMBER OF COMICS TO RETRIEVE ########
# num_comics_to_get - I've tried up to 99 on Calvin&Hobbes # num_comics_to_get - I've tried up to 99 on Calvin&Hobbes
@ -40,6 +41,8 @@ class GoComics(BasicNewsRecipe):
remove_tags = [dict(name='a', attrs={'class':['beginning','prev','cal','next','newest']}), remove_tags = [dict(name='a', attrs={'class':['beginning','prev','cal','next','newest']}),
dict(name='div', attrs={'class':['tag-wrapper']}), dict(name='div', attrs={'class':['tag-wrapper']}),
dict(name='a', attrs={'href':re.compile(r'.*mutable_[0-9]+', re.IGNORECASE)}),
dict(name='img', attrs={'src':re.compile(r'.*mutable_[0-9]+', re.IGNORECASE)}),
dict(name='ul', attrs={'class':['share-nav','feature-nav']}), dict(name='ul', attrs={'class':['share-nav','feature-nav']}),
] ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 B

BIN
recipes/icons/natgeo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

View File

@ -3,8 +3,9 @@ from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1295262156(BasicNewsRecipe): class AdvancedUserRecipe1295262156(BasicNewsRecipe):
title = u'kath.net' title = u'kath.net'
__author__ = 'Bobus' __author__ = 'Bobus'
description = u'Katholische Nachrichten'
oldest_article = 7 oldest_article = 7
language = 'en' language = 'de'
max_articles_per_feed = 100 max_articles_per_feed = 100
feeds = [(u'kath.net', u'http://www.kath.net/2005/xml/index.xml')] feeds = [(u'kath.net', u'http://www.kath.net/2005/xml/index.xml')]

71
recipes/natgeo.recipe Normal file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2011, gagsays <gagsays at gmail dot com>'
'''
nationalgeographic.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class NatGeo(BasicNewsRecipe):
title = u'National Geographic'
description = 'Daily news articles from The National Geographic'
language = 'en'
oldest_article = 20
max_articles_per_feed = 25
encoding = 'utf8'
publisher = 'nationalgeographic.com'
category = 'science, nat geo'
__author__ = 'gagsays'
masthead_url = 'http://s.ngeo.com/wpf/sites/themes/global/i/presentation/ng_logo_small.png'
description = 'Inspiring people to care about the planet since 1888'
timefmt = ' [%a, %d %b, %Y]'
no_stylesheets = True
use_embedded_content = False
extra_css = '''
body {color: #000000;font-size: medium;}
h1 {color: #222222; font-size: large; font-weight:lighter; text-decoration:none; text-align: center;font-family:Georgia,Times New Roman,Times,serif;}
h2 {color: #454545; font-size: small; font-weight:lighter; text-decoration:none; text-align: justify; font-style:italic;font-family :Georgia,Times New Roman,Times,serif;}
h3 {color: #555555; font-size: small; font-style:italic; margin-top: 10px;}
img{margin-bottom: 0.25em;display:block;margin-left: auto;margin-right: auto;}
a:link,a,.a,href {text-decoration: none;color: #000000;}
.caption{color: #000000;font-size: xx-small;text-align: justify;font-weight:normal;}
.credit{color: #555555;font-size: xx-small;text-align: left;font-weight:lighter;}
p.author,p.publication{color: #000000;font-size: xx-small;text-align: left;display:inline;}
p.publication_time{color: #000000;font-size: xx-small;text-align: right;text-decoration: underline;}
p {margin-bottom: 0;}
p + p {text-indent: 1.5em;margin-top: 0;}
.hidden{display:none;}
#page_head{text-transform:uppercase;}
'''
def parse_feeds (self):
feeds = BasicNewsRecipe.parse_feeds(self)
for feed in feeds:
for article in feed.articles[:]:
if 'Presented' in article.title or 'Pictures' in article.title:
feed.articles.remove(article)
return feeds
def preprocess_html(self, soup):
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup
remove_tags_before = dict(id='page_head')
keep_only_tags = [
dict(name='div',attrs={'id':['page_head','content_mainA']})
]
remove_tags_after = [
dict(name='div',attrs={'class':['article_text','promo_collection']})
]
remove_tags = [
dict(name='div', attrs={'class':['aside','primary full_width']})
,dict(name='div', attrs={'id':['header_search','navigation_mainB_wrap']})
]
feeds = [
(u'Daily News', u'http://feeds.nationalgeographic.com/ng/News/News_Main')
]

View File

@ -350,3 +350,11 @@ send_news_to_device_location = "main"
# work on all operating systems) # work on all operating systems)
server_listen_on = '0.0.0.0' server_listen_on = '0.0.0.0'
#: Unified toolbar on OS X
# If you enable this option and restart calibre, the toolbar will be 'unified'
# with the titlebar as is normal for OS X applications. However, doing this has
# various bugs, for instance the minimum width of the toolbar becomes twice
# what it should be and it causes other random bugs on some systems, so turn it
# on at your own risk!
unified_title_toolbar_on_osx = False

View File

@ -1164,6 +1164,12 @@ class StoreFoylesUKStore(StoreBase):
description = _('Foyles of London, online.') description = _('Foyles of London, online.')
actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore' actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore'
class StoreGandalfStore(StoreBase):
name = 'Gandalf'
author = 'Tomasz Długosz'
description = _('Zaczarowany świat książek')
actual_plugin = 'calibre.gui2.store.gandalf_plugin:GandalfStore'
class StoreGoogleBooksStore(StoreBase): class StoreGoogleBooksStore(StoreBase):
name = 'Google Books' name = 'Google Books'
description = _('Google Books') description = _('Google Books')
@ -1191,6 +1197,7 @@ class StoreMobileReadStore(StoreBase):
class StoreNextoStore(StoreBase): class StoreNextoStore(StoreBase):
name = 'Nexto' name = 'Nexto'
author = 'Tomasz Długosz'
description = _('Audiobooki mp3, ebooki, prasa - księgarnia internetowa.') description = _('Audiobooki mp3, ebooki, prasa - księgarnia internetowa.')
actual_plugin = 'calibre.gui2.store.nexto_plugin:NextoStore' actual_plugin = 'calibre.gui2.store.nexto_plugin:NextoStore'
@ -1199,6 +1206,16 @@ class StoreOpenLibraryStore(StoreBase):
description = _('One web page for every book.') description = _('One web page for every book.')
actual_plugin = 'calibre.gui2.store.open_library_plugin:OpenLibraryStore' actual_plugin = 'calibre.gui2.store.open_library_plugin:OpenLibraryStore'
class StoreOReillyStore(StoreBase):
name = 'OReilly'
description = _('DRM-Free tech ebooks.')
actual_plugin = 'calibre.gui2.store.oreilly_plugin:OReillyStore'
class StorePragmaticBookshelfStore(StoreBase):
name = 'Pragmatic Bookshelf'
description = _('The Pragmatic Bookshelf')
actual_plugin = 'calibre.gui2.store.pragmatic_bookshelf_plugin:PragmaticBookshelfStore'
class StoreSmashwordsStore(StoreBase): class StoreSmashwordsStore(StoreBase):
name = 'Smashwords' name = 'Smashwords'
description = _('Your ebook. Your way.') description = _('Your ebook. Your way.')
@ -1219,14 +1236,35 @@ class StoreWizardsTowerBooksStore(StoreBase):
description = 'Wizard\'s Tower Press.' description = 'Wizard\'s Tower Press.'
actual_plugin = 'calibre.gui2.store.wizards_tower_books_plugin:WizardsTowerBooksStore' actual_plugin = 'calibre.gui2.store.wizards_tower_books_plugin:WizardsTowerBooksStore'
plugins += [StoreArchiveOrgStore, StoreAmazonKindleStore, StoreAmazonDEKindleStore, plugins += [
StoreAmazonUKKindleStore, StoreBaenWebScriptionStore, StoreBNStore, StoreArchiveOrgStore,
StoreBeamEBooksDEStore, StoreBeWriteStore, StoreAmazonKindleStore,
StoreDieselEbooksStore, StoreEbookscomStore, StoreEPubBuyDEStore, StoreAmazonDEKindleStore,
StoreEHarlequinStore, StoreFeedbooksStore, StoreAmazonUKKindleStore,
StoreFoylesUKStore, StoreGoogleBooksStore, StoreGutenbergStore, StoreBaenWebScriptionStore,
StoreKoboStore, StoreManyBooksStore, StoreBNStore,
StoreMobileReadStore, StoreNextoStore, StoreOpenLibraryStore, StoreSmashwordsStore, StoreBeamEBooksDEStore,
StoreWaterstonesUKStore, StoreWeightlessBooksStore, StoreWizardsTowerBooksStore] StoreBeWriteStore,
StoreDieselEbooksStore,
StoreEbookscomStore,
StoreEPubBuyDEStore,
StoreEHarlequinStore,
StoreFeedbooksStore,
StoreFoylesUKStore,
StoreGandalfStore,
StoreGoogleBooksStore,
StoreGutenbergStore,
StoreKoboStore,
StoreManyBooksStore,
StoreMobileReadStore,
StoreNextoStore,
StoreOpenLibraryStore,
StoreOReillyStore,
StorePragmaticBookshelfStore,
StoreSmashwordsStore,
StoreWaterstonesUKStore,
StoreWeightlessBooksStore,
StoreWizardsTowerBooksStore
]
# }}} # }}}

View File

@ -941,7 +941,7 @@ class ITUNES(DriverBase):
# declared in use_plugboard_ext and a device name of ITUNES # declared in use_plugboard_ext and a device name of ITUNES
if DEBUG: if DEBUG:
self.log.info("ITUNES.set_plugboard()") self.log.info("ITUNES.set_plugboard()")
#self.log.info(' using plugboard %s' % plugboards) #self.log.info(' plugboard: %s' % plugboards)
self.plugboards = plugboards self.plugboards = plugboards
self.plugboard_func = pb_func self.plugboard_func = pb_func
@ -1052,7 +1052,6 @@ class ITUNES(DriverBase):
'title': metadata[i].title, 'title': metadata[i].title,
'uuid': metadata[i].uuid } 'uuid': metadata[i].uuid }
# Report progress # Report progress
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress((i+1)/file_count, _('%d of %d') % (i+1, file_count)) self.report_progress((i+1)/file_count, _('%d of %d') % (i+1, file_count))
@ -2744,7 +2743,7 @@ class ITUNES(DriverBase):
# Update metadata from plugboard # Update metadata from plugboard
# If self.plugboard is None (no transforms), original metadata is returned intact # If self.plugboard is None (no transforms), original metadata is returned intact
metadata_x = self._xform_metadata_via_plugboard(metadata, this_book.format) metadata_x = self._xform_metadata_via_plugboard(metadata, this_book.format)
self.log("metadata.title_sort: %s metadata_x.title_sort: %s" % (metadata.title_sort, metadata_x.title_sort))
if isosx: if isosx:
if lb_added: if lb_added:
lb_added.name.set(metadata_x.title) lb_added.name.set(metadata_x.title)
@ -2754,8 +2753,7 @@ class ITUNES(DriverBase):
lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
lb_added.enabled.set(True) lb_added.enabled.set(True)
lb_added.sort_artist.set(icu_title(metadata_x.author_sort)) lb_added.sort_artist.set(icu_title(metadata_x.author_sort))
lb_added.sort_name.set(metadata.title_sort) lb_added.sort_name.set(metadata_x.title_sort)
if db_added: if db_added:
db_added.name.set(metadata_x.title) db_added.name.set(metadata_x.title)
@ -2765,7 +2763,7 @@ class ITUNES(DriverBase):
db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
db_added.enabled.set(True) db_added.enabled.set(True)
db_added.sort_artist.set(icu_title(metadata_x.author_sort)) db_added.sort_artist.set(icu_title(metadata_x.author_sort))
db_added.sort_name.set(metadata.title_sort) db_added.sort_name.set(metadata_x.title_sort)
if metadata_x.comments: if metadata_x.comments:
if lb_added: if lb_added:
@ -2785,6 +2783,7 @@ class ITUNES(DriverBase):
# Set genre from series if available, else first alpha tag # Set genre from series if available, else first alpha tag
# Otherwise iTunes grabs the first dc:subject from the opf metadata # Otherwise iTunes grabs the first dc:subject from the opf metadata
# If title_sort applied in plugboard, that overrides using series/index as title_sort
if metadata_x.series and self.settings().extra_customization[self.USE_SERIES_AS_CATEGORY]: if metadata_x.series and self.settings().extra_customization[self.USE_SERIES_AS_CATEGORY]:
if DEBUG: if DEBUG:
self.log.info(" ITUNES._update_iTunes_metadata()") self.log.info(" ITUNES._update_iTunes_metadata()")
@ -2796,7 +2795,9 @@ class ITUNES(DriverBase):
fraction = index-integer fraction = index-integer
series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0')) series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
if lb_added: if lb_added:
lb_added.sort_name.set("%s %s" % (self.title_sorter(metadata_x.series), series_index)) # If no title_sort plugboard tweak, create sort_name from series/index
if metadata.title_sort == metadata_x.title_sort:
lb_added.sort_name.set("%s %s" % (self.title_sorter(metadata_x.series), series_index))
lb_added.episode_ID.set(metadata_x.series) lb_added.episode_ID.set(metadata_x.series)
lb_added.episode_number.set(metadata_x.series_index) lb_added.episode_number.set(metadata_x.series_index)
@ -2810,7 +2811,9 @@ class ITUNES(DriverBase):
break break
if db_added: if db_added:
db_added.sort_name.set("%s %s" % (self.title_sorter(metadata_x.series), series_index)) # If no title_sort plugboard tweak, create sort_name from series/index
if metadata.title_sort == metadata_x.title_sort:
db_added.sort_name.set("%s %s" % (self.title_sorter(metadata_x.series), series_index))
db_added.episode_ID.set(metadata_x.series) db_added.episode_ID.set(metadata_x.series)
db_added.episode_number.set(metadata_x.series_index) db_added.episode_number.set(metadata_x.series_index)
@ -2845,7 +2848,7 @@ class ITUNES(DriverBase):
lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
lb_added.Enabled = True lb_added.Enabled = True
lb_added.SortArtist = icu_title(metadata_x.author_sort) lb_added.SortArtist = icu_title(metadata_x.author_sort)
lb_added.SortName = metadata.title_sort lb_added.SortName = metadata_x.title_sort
if db_added: if db_added:
db_added.Name = metadata_x.title db_added.Name = metadata_x.title
@ -2855,7 +2858,7 @@ class ITUNES(DriverBase):
db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
db_added.Enabled = True db_added.Enabled = True
db_added.SortArtist = icu_title(metadata_x.author_sort) db_added.SortArtist = icu_title(metadata_x.author_sort)
db_added.SortName = metadata.title_sort db_added.SortName = metadata_x.title_sort
if metadata_x.comments: if metadata_x.comments:
if lb_added: if lb_added:
@ -2888,7 +2891,9 @@ class ITUNES(DriverBase):
fraction = index-integer fraction = index-integer
series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0')) series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
if lb_added: if lb_added:
lb_added.SortName = "%s %s" % (self.title_sorter(metadata_x.series), series_index) # If no title_sort plugboard tweak, create sort_name from series/index
if metadata.title_sort == metadata_x.title_sort:
lb_added.SortName = "%s %s" % (self.title_sorter(metadata_x.series), series_index)
lb_added.EpisodeID = metadata_x.series lb_added.EpisodeID = metadata_x.series
try: try:
@ -2914,7 +2919,9 @@ class ITUNES(DriverBase):
break break
if db_added: if db_added:
db_added.SortName = "%s %s" % (self.title_sorter(metadata_x.series), series_index) # If no title_sort plugboard tweak, create sort_name from series/index
if metadata.title_sort == metadata_x.title_sort:
db_added.SortName = "%s %s" % (self.title_sorter(metadata_x.series), series_index)
db_added.EpisodeID = metadata_x.series db_added.EpisodeID = metadata_x.series
try: try:
@ -2975,6 +2982,9 @@ class ITUNES(DriverBase):
newmi.publisher if book.publisher != newmi.publisher else '')) newmi.publisher if book.publisher != newmi.publisher else ''))
self.log.info(" tags: %s %s" % (book.tags, ">>> %s" % self.log.info(" tags: %s %s" % (book.tags, ">>> %s" %
newmi.tags if book.tags != newmi.tags else '')) newmi.tags if book.tags != newmi.tags else ''))
else:
self.log(" matching plugboard not found")
else: else:
newmi = book newmi = book
return newmi return newmi

View File

@ -9,11 +9,12 @@ from functools import partial
from PyQt4.Qt import QMenu, QObject, QTimer from PyQt4.Qt import QMenu, QObject, QTimer
from calibre.gui2 import error_dialog from calibre.gui2 import error_dialog, question_dialog
from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog
from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.dialogs.confirm_delete_location import confirm_location from calibre.gui2.dialogs.confirm_delete_location import confirm_location
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
from calibre.utils.recycle_bin import can_recycle
single_shot = partial(QTimer.singleShot, 10) single_shot = partial(QTimer.singleShot, 10)
@ -24,6 +25,15 @@ class MultiDeleter(QObject):
QObject.__init__(self, gui) QObject.__init__(self, gui)
self.model = gui.library_view.model() self.model = gui.library_view.model()
self.ids = ids self.ids = ids
self.permanent = False
if can_recycle and len(ids) > 100:
if question_dialog(gui, _('Are you sure?'), '<p>'+
_('You are trying to delete %d books. '
'Sending so many files to the Recycle'
' Bin <b>can be slow</b>. Should calibre skip the'
' Recycle Bin? If you click Yes the files'
' will be <b>permanently deleted</b>.')%len(ids)):
self.permanent = True
self.gui = gui self.gui = gui
self.failures = [] self.failures = []
self.deleted_ids = [] self.deleted_ids = []
@ -44,7 +54,8 @@ class MultiDeleter(QObject):
title_ = self.model.db.title(id_, index_is_id=True) title_ = self.model.db.title(id_, index_is_id=True)
if title_: if title_:
title = title_ title = title_
self.model.db.delete_book(id_, notify=False, commit=False) self.model.db.delete_book(id_, notify=False, commit=False,
permanent=self.permanent)
self.deleted_ids.append(id_) self.deleted_ids.append(id_)
except: except:
import traceback import traceback

View File

@ -246,6 +246,8 @@ class BarsManager(QObject):
self.main_bars = tuple(bars[:2]) self.main_bars = tuple(bars[:2])
self.child_bars = tuple(bars[2:]) self.child_bars = tuple(bars[2:])
self.menu_bar = MenuBar(self.location_manager, self.parent())
self.parent().setMenuBar(self.menu_bar)
self.apply_settings() self.apply_settings()
self.init_bars() self.init_bars()
@ -295,11 +297,9 @@ class BarsManager(QObject):
if child_bar.added_actions: if child_bar.added_actions:
child_bar.setVisible(True) child_bar.setVisible(True)
self.menu_bar = MenuBar(self.location_manager, self.parent())
self.menu_bar.init_bar(self.bar_actions[4 if showing_device else 3]) self.menu_bar.init_bar(self.bar_actions[4 if showing_device else 3])
self.menu_bar.update_lm_actions() self.menu_bar.update_lm_actions()
self.menu_bar.setVisible(bool(self.menu_bar.added_actions)) self.menu_bar.setVisible(bool(self.menu_bar.added_actions))
self.parent().setMenuBar(self.menu_bar)
def apply_settings(self): def apply_settings(self):
sz = gprefs['toolbar_icon_size'] sz = gprefs['toolbar_icon_size']

View File

@ -17,6 +17,7 @@ from calibre.gui2.search_box import SearchBox2, SavedSearchBox
from calibre.gui2.throbber import ThrobbingButton from calibre.gui2.throbber import ThrobbingButton
from calibre.gui2.bars import BarsManager from calibre.gui2.bars import BarsManager
from calibre.gui2.widgets import ComboBoxWithHelp from calibre.gui2.widgets import ComboBoxWithHelp
from calibre.utils.config_base import tweaks
from calibre import human_readable from calibre import human_readable
class LocationManager(QObject): # {{{ class LocationManager(QObject): # {{{
@ -264,7 +265,10 @@ class MainWindowMixin(object): # {{{
for bar in self.bars_manager.child_bars: for bar in self.bars_manager.child_bars:
self.addToolBar(Qt.BottomToolBarArea, bar) self.addToolBar(Qt.BottomToolBarArea, bar)
self.bars_manager.update_bars() self.bars_manager.update_bars()
self.setUnifiedTitleAndToolBarOnMac(True) # This is disabled because it introduces various toolbar related bugs
# The width of the toolbar becomes the sum of both toolbars
if tweaks['unified_title_toolbar_on_osx']:
self.setUnifiedTitleAndToolBarOnMac(True)
l = self.centralwidget.layout() l = self.centralwidget.layout()
l.addWidget(self.search_bar) l.addWidget(self.search_bar)

View File

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
__license__ = 'GPL 3'
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
__docformat__ = 'restructuredtext en'
import re
import urllib
from contextlib import closing
from lxml import html
from PyQt4.Qt import QUrl
from calibre import browser, url_slash_cleaner
from calibre.gui2 import open_url
from calibre.gui2.store import StorePlugin
from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class GandalfStore(BasicStoreConfig, StorePlugin):
def open(self, parent=None, detail_item=None, external=False):
url = 'http://www.gandalf.com.pl/ebooks/'
if external or self.config.get('open_external', False):
open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url)))
else:
d = WebStoreDialog(self.gui, url, parent, detail_item)
d.setWindowTitle(self.name)
d.set_tags(self.config.get('tags', ''))
d.exec_()
def search(self, query, max_results=10, timeout=60):
url = 'http://www.gandalf.com.pl/s/'
values={
'search': query.encode('iso8859_2'),
'dzialx':'11'
}
br = browser()
counter = max_results
with closing(br.open(url, data=urllib.urlencode(values), timeout=timeout)) as f:
doc = html.fromstring(f.read())
for data in doc.xpath('//div[@class="box"]'):
if counter <= 0:
break
id = ''.join(data.xpath('.//div[@class="info"]/h3/a/@href'))
if not id:
continue
cover_url = ''.join(data.xpath('.//img/@src'))
title = ''.join(data.xpath('.//div[@class="info"]/h3/a/@title'))
formats = title.split()
formats = formats[-1]
author = ''.join(data.xpath('.//div[@class="info"]/h4/text() | .//div[@class="info"]/h4/span/text()'))
price = ''.join(data.xpath('.//h3[@class="promocja"]/text()'))
price = re.sub('PLN', '', price)
price = re.sub('\.', ',', price)
counter -= 1
s = SearchResult()
s.cover_url = cover_url
s.title = title.strip()
s.author = author.strip()
s.price = price
s.detail_item = id.strip()
s.drm = SearchResult.DRM_UNKNOWN
s.formats = formats.upper().strip()
yield s

View File

@ -63,6 +63,7 @@ class NextoStore(BasicStoreConfig, StorePlugin):
cover_url = ''.join(data.xpath('.//img[@class="cover"]/@src')) cover_url = ''.join(data.xpath('.//img[@class="cover"]/@src'))
title = ''.join(data.xpath('.//a[@class="title"]/text()')) title = ''.join(data.xpath('.//a[@class="title"]/text()'))
title = re.sub(r' - ebook$', '', title)
formats = ', '.join(data.xpath('.//ul[@class="formats_available"]/li//b/text()')) formats = ', '.join(data.xpath('.//ul[@class="formats_available"]/li//b/text()'))
DrmFree = re.search(r'bez.DRM', formats) DrmFree = re.search(r'bez.DRM', formats)
formats = re.sub(r'\(.+\)', '', formats) formats = re.sub(r'\(.+\)', '', formats)

View File

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
import re
import urllib
from contextlib import closing
from lxml import html
from PyQt4.Qt import QUrl
from calibre import browser, url_slash_cleaner
from calibre.gui2 import open_url
from calibre.gui2.store import StorePlugin
from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class OReillyStore(BasicStoreConfig, StorePlugin):
def open(self, parent=None, detail_item=None, external=False):
url = 'http://oreilly.com/ebooks/'
if detail_item:
detail_item = 'https://epoch.oreilly.com/shop/cart.orm?prod=%s.EBOOK&p=CALIBRE' % detail_item
if external or self.config.get('open_external', False):
open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url)))
else:
d = WebStoreDialog(self.gui, url, parent, detail_item)
d.setWindowTitle(self.name)
d.set_tags(self.config.get('tags', ''))
d.exec_()
def search(self, query, max_results=10, timeout=60):
url = 'http://search.oreilly.com/?t1=Books&t2=Format&t3=Ebook&q=' + urllib.quote_plus(query)
br = browser()
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
for data in doc.xpath('//div[@id="results"]/div[@class="result"]'):
if counter <= 0:
break
full_id = ''.join(data.xpath('.//div[@class="title"]/a/@href'))
mo = re.search('\d+', full_id)
if not mo:
continue
id = mo.group()
cover_url = ''.join(data.xpath('.//div[@class="bigCover"]//img/@src'))
title = ''.join(data.xpath('.//div[@class="title"]/a/text()'))
author = ''.join(data.xpath('.//div[@class="author"]/text()'))
author = author.split('By ')[-1].strip()
# Get the detail here because we need to get the ebook id for the detail_item.
with closing(br.open(full_id, timeout=timeout)) as nf:
idoc = html.fromstring(nf.read())
price = ''.join(idoc.xpath('(//span[@class="price"])[1]/span//text()'))
formats = ', '.join(idoc.xpath('//div[@class="ebook_formats"]//a/text()'))
eid = ''.join(idoc.xpath('(//a[@class="product_buy_link" and contains(@href, ".EBOOK")])[1]/@href')).strip()
mo = re.search('\d+', eid)
if mo:
id = mo.group()
counter -= 1
s = SearchResult()
s.cover_url = cover_url.strip()
s.title = title.strip()
s.author = author.strip()
s.detail_item = id.strip()
s.price = price.strip()
s.drm = SearchResult.DRM_UNLOCKED
s.formats = formats.upper()
yield s

View File

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
import urllib
from contextlib import closing
from lxml import html
from PyQt4.Qt import QUrl
from calibre import browser, url_slash_cleaner
from calibre.gui2 import open_url
from calibre.gui2.store import StorePlugin
from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class PragmaticBookshelfStore(BasicStoreConfig, StorePlugin):
def open(self, parent=None, detail_item=None, external=False):
url = 'http://pragprog.com/'
if external or self.config.get('open_external', False):
open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url)))
else:
d = WebStoreDialog(self.gui, url, parent, detail_item)
d.setWindowTitle(self.name)
d.set_tags(self.config.get('tags', ''))
d.exec_()
def search(self, query, max_results=10, timeout=60):
'''
OPDS based search.
We really should get the catelog from http://pragprog.com/catalog.opds
and look for the application/opensearchdescription+xml entry.
Then get the opensearch description to get the search url and
format. However, we are going to be lazy and hard code it.
'''
url = 'http://pragprog.com/catalog/search?q=' + urllib.quote_plus(query)
br = browser()
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
# Use html instead of etree as html allows us
# to ignore the namespace easily.
doc = html.fromstring(f.read())
for data in doc.xpath('//entry'):
if counter <= 0:
break
id = ''.join(data.xpath('.//link[@rel="http://opds-spec.org/acquisition/buy"]/@href'))
if not id:
continue
price = ''.join(data.xpath('.//price/@currencycode')).strip()
price += ' '
price += ''.join(data.xpath('.//price/text()')).strip()
if not price.strip():
continue
cover_url = ''.join(data.xpath('.//link[@rel="http://opds-spec.org/cover"]/@href'))
title = ''.join(data.xpath('.//title/text()'))
author = ''.join(data.xpath('.//author//text()'))
counter -= 1
s = SearchResult()
s.cover_url = cover_url
s.title = title.strip()
s.author = author.strip()
s.price = price.strip()
s.detail_item = id.strip()
s.drm = SearchResult.DRM_UNLOCKED
s.formats = 'EPUB, PDF, MOBI'
yield s

View File

@ -190,7 +190,7 @@ class SearchDialog(QDialog, Ui_Dialog):
else: else:
self.resize_columns() self.resize_columns()
self.open_external.setChecked(self.config.get('open_external', False)) self.open_external.setChecked(self.config.get('open_external', True))
store_check = self.config.get('store_checked', None) store_check = self.config.get('store_checked', None)
if store_check: if store_check:

View File

@ -1145,7 +1145,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.notify('metadata', [id]) self.notify('metadata', [id])
return True return True
def delete_book(self, id, notify=True, commit=True): def delete_book(self, id, notify=True, commit=True, permanent=False):
''' '''
Removes book from the result cache and the underlying database. Removes book from the result cache and the underlying database.
If you set commit to False, you must call clean() manually afterwards If you set commit to False, you must call clean() manually afterwards
@ -1155,10 +1155,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
except: except:
path = None path = None
if path and os.path.exists(path): if path and os.path.exists(path):
self.rmtree(path) self.rmtree(path, permanent=permanent)
parent = os.path.dirname(path) parent = os.path.dirname(path)
if len(os.listdir(parent)) == 0: if len(os.listdir(parent)) == 0:
self.rmtree(parent) self.rmtree(parent, permanent=permanent)
self.conn.execute('DELETE FROM books WHERE id=?', (id,)) self.conn.execute('DELETE FROM books WHERE id=?', (id,))
if commit: if commit:
self.conn.commit() self.conn.commit()

View File

@ -67,7 +67,7 @@ def find_plugboard(device_name, format, plugboards):
cpb = pb[device_name] cpb = pb[device_name]
elif plugboard_any_device_value in pb: elif plugboard_any_device_value in pb:
cpb = pb[plugboard_any_device_value] cpb = pb[plugboard_any_device_value]
if True or DEBUG: if DEBUG:
prints('Device using plugboard', format, device_name, cpb) prints('Device using plugboard', format, device_name, cpb)
return cpb return cpb

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@ elif isosx:
path = path.decode(filesystem_encoding) path = path.decode(filesystem_encoding)
u.send2trash(path) u.send2trash(path)
can_recycle = callable(recycle)
def delete_file(path): def delete_file(path):
if callable(recycle): if callable(recycle):