Merge from trunk

This commit is contained in:
Charles Haley 2011-02-27 16:40:40 +00:00
commit 9d16e223e2
11 changed files with 552 additions and 363 deletions

View File

@ -19,6 +19,10 @@
# new recipes: # new recipes:
# - title: # - title:
# - title: "Launch of a new website that catalogues DRM free books. http://drmfree.calibre-ebook.com"
# description: "A growing catalogue of DRM free books. Books that you actually own after buying instead of renting."
# type: major
- version: 0.7.47 - version: 0.7.47
date: 2011-02-25 date: 2011-02-25
@ -88,8 +92,8 @@
- title: "Various Romanian news sources" - title: "Various Romanian news sources"
author: Silviu Coatara author: Silviu Coatara
- title: "Osnews.pl and SwiatKindle" - title: "Osnews.pl and SwiatCzytnikow"
author: Mori author: Tomasz Dlugosz
- title: "Roger Ebert Journal" - title: "Roger Ebert Journal"
author: Shane Erstad author: Shane Erstad

View File

@ -0,0 +1,56 @@
__license__ = 'GPL v3'
__author__ = 'Todd Chapman'
__copyright__ = 'Todd Chapman'
__version__ = 'v0.1'
__date__ = '26 February 2011'
'''
http://www.buffalonews.com/RSS/
'''
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1298680852(BasicNewsRecipe):
title = u'Buffalo News'
__author__ = 'ChappyOnIce'
language = 'en'
oldest_article = 2
max_articles_per_feed = 20
encoding = 'utf-8'
remove_javascript = True
keep_only_tags = [
dict(name='div', attrs={'class':['main-content-left']})
]
remove_tags = [
dict(name='div', attrs={'id':['commentCount']}),
dict(name='div', attrs={'class':['story-list-links']})
]
remove_tags_after = dict(name='div', attrs={'class':['body storyContent']})
conversion_options = {
'base_font_size' : 14,
}
feeds = [(u'City of Buffalo', u'http://www.buffalonews.com/city/communities/buffalo/?widget=rssfeed&view=feed&contentId=77944'),
(u'Southern Erie County', u'http://www.buffalonews.com/city/communities/southern-erie/?widget=rssfeed&view=feed&contentId=77944'),
(u'Eastern Erie County', u'http://www.buffalonews.com/city/communities/eastern-erie/?widget=rssfeed&view=feed&contentId=77944'),
(u'Southern Tier', u'http://www.buffalonews.com/city/communities/southern-tier/?widget=rssfeed&view=feed&contentId=77944'),
(u'Niagara County', u'http://www.buffalonews.com/city/communities/niagara-county/?widget=rssfeed&view=feed&contentId=77944'),
(u'Business', u'http://www.buffalonews.com/business/?widget=rssfeed&view=feed&contentId=77944'),
(u'MoneySmart', u'http://www.buffalonews.com/business/moneysmart/?widget=rssfeed&view=feed&contentId=77944'),
(u'Bills & NFL', u'http://www.buffalonews.com/sports/bills-nfl/?widget=rssfeed&view=feed&contentId=77944'),
(u'Sabres & NHL', u'http://www.buffalonews.com/sports/sabres-nhl/?widget=rssfeed&view=feed&contentId=77944'),
(u'Bob DiCesare', u'http://www.buffalonews.com/sports/columns/bob-dicesare/?widget=rssfeed&view=feed&contentId=77944'),
(u'Bucky Gleason', u'http://www.buffalonews.com/sports/columns/bucky-gleason/?widget=rssfeed&view=feed&contentId=77944'),
(u'Mark Gaughan', u'http://www.buffalonews.com/sports/bills-nfl/inside-the-nfl/?widget=rssfeed&view=feed&contentId=77944'),
(u'Mike Harrington', u'http://www.buffalonews.com/sports/columns/mike-harrington/?widget=rssfeed&view=feed&contentId=77944'),
(u'Jerry Sullivan', u'http://www.buffalonews.com/sports/columns/jerry-sullivan/?widget=rssfeed&view=feed&contentId=77944'),
(u'Other Sports Columns', u'http://www.buffalonews.com/sports/columns/other-sports-columns/?widget=rssfeed&view=feed&contentId=77944'),
(u'Life', u'http://www.buffalonews.com/life/?widget=rssfeed&view=feed&contentId=77944'),
(u'Bruce Andriatch', u'http://www.buffalonews.com/city/columns/bruce-andriatch/?widget=rssfeed&view=feed&contentId=77944'),
(u'Donn Esmonde', u'http://www.buffalonews.com/city/columns/donn-esmonde/?widget=rssfeed&view=feed&contentId=77944'),
(u'Rod Watson', u'http://www.buffalonews.com/city/columns/rod-watson/?widget=rssfeed&view=feed&contentId=77944'),
(u'Entertainment', u'http://www.buffalonews.com/entertainment/?widget=rssfeed&view=feed&contentId=77944'),
(u'Off Main Street', u'http://www.buffalonews.com/city/columns/off-main-street/?widget=rssfeed&view=feed&contentId=77944'),
(u'Editorials', u'http://www.buffalonews.com/editorial-page/buffalo-news-editorials/?widget=rssfeed&view=feed&contentId=77944')
]

View File

@ -0,0 +1,27 @@
__license__ = 'GPL v3'
__copyright__ = '2011-2011, Federico Escalada <fedeescalada at gmail.com>'
from calibre.web.feeds.news import BasicNewsRecipe
class Dotpod(BasicNewsRecipe):
__author__ = 'Federico Escalada'
description = 'Tecnologia y Comunicacion Audiovisual'
encoding = 'utf-8'
language = 'es'
max_articles_per_feed = 100
no_stylesheets = True
oldest_article = 7
publication_type = 'blog'
title = 'Dotpod'
authors = 'Federico Picone'
conversion_options = {
'authors' : authors
,'comments' : description
,'language' : language
}
feeds = [('Dotpod', 'http://www.dotpod.com.ar/feed/')]
remove_tags = [dict(name='div', attrs={'class':'feedflare'})]

View File

@ -58,6 +58,20 @@ class FetchNewsAction(InterfaceAction):
self.scheduler.recipe_download_failed(arg) self.scheduler.recipe_download_failed(arg)
return self.gui.job_exception(job) return self.gui.job_exception(job)
id = self.gui.library_view.model().add_news(pt.name, arg) id = self.gui.library_view.model().add_news(pt.name, arg)
# Arg may contain a "keep_issues" variable. If it is non-zero,
# delete all but newest x issues.
try:
keep_issues = int(arg['keep_issues'])
except:
keep_issues = 0
if keep_issues > 0:
ids_with_tag = list(sorted(self.gui.library_view.model().
db.tags_older_than(arg['title'], None), reverse=True))
ids_to_delete = ids_with_tag[keep_issues:]
if ids_to_delete:
self.gui.library_view.model().delete_books_by_id(ids_to_delete)
self.gui.library_view.model().reset() self.gui.library_view.model().reset()
sync = self.gui.news_to_be_synced sync = self.gui.news_to_be_synced
sync.add(id) sync.add(id)

View File

@ -10,11 +10,9 @@ Scheduler for automated recipe downloads
from datetime import timedelta from datetime import timedelta
from PyQt4.Qt import QDialog, SIGNAL, Qt, QTime, QObject, QMenu, \ from PyQt4.Qt import QDialog, SIGNAL, Qt, QTime, QObject, QMenu, \
QAction, QIcon, QMutex, QTimer, pyqtSignal, QWidget, QHBoxLayout, \ QAction, QIcon, QMutex, QTimer, pyqtSignal
QLabel
from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog
from calibre.gui2.search_box import SearchBox2
from calibre.gui2 import config as gconf, error_dialog from calibre.gui2 import config as gconf, error_dialog
from calibre.web.feeds.recipes.model import RecipeModel from calibre.web.feeds.recipes.model import RecipeModel
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
@ -28,18 +26,12 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.setupUi(self) self.setupUi(self)
self.recipe_model = recipe_model self.recipe_model = recipe_model
self.recipe_model.do_refresh() self.recipe_model.do_refresh()
self.count_label.setText(
_('%s news sources') %
self.recipe_model.showing_count)
self._cont = QWidget(self)
self._cont.l = QHBoxLayout()
self._cont.setLayout(self._cont.l)
self._cont.la = QLabel(_('&Search:'))
self._cont.l.addWidget(self._cont.la, 1)
self.search = SearchBox2(self)
self._cont.l.addWidget(self.search, 100)
self._cont.la.setBuddy(self.search)
self.search.setMinimumContentsLength(25)
self.search.initialize('scheduler_search_history') self.search.initialize('scheduler_search_history')
self.recipe_box.layout().insertWidget(0, self._cont) self.search.setMinimumContentsLength(15)
self.search.search.connect(self.recipe_model.search) self.search.search.connect(self.recipe_model.search)
self.recipe_model.searched.connect(self.search.search_done, self.recipe_model.searched.connect(self.search.search_done,
type=Qt.QueuedConnection) type=Qt.QueuedConnection)
@ -153,9 +145,12 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.recipe_model.un_schedule_recipe(urn) self.recipe_model.un_schedule_recipe(urn)
add_title_tag = self.add_title_tag.isChecked() add_title_tag = self.add_title_tag.isChecked()
keep_issues = u'0'
if self.keep_issues.isEnabled():
keep_issues = unicode(self.keep_issues.value())
custom_tags = unicode(self.custom_tags.text()).strip() custom_tags = unicode(self.custom_tags.text()).strip()
custom_tags = [x.strip() for x in custom_tags.split(',')] custom_tags = [x.strip() for x in custom_tags.split(',')]
self.recipe_model.customize_recipe(urn, add_title_tag, custom_tags) self.recipe_model.customize_recipe(urn, add_title_tag, custom_tags, keep_issues)
return True return True
def initialize_detail_box(self, urn): def initialize_detail_box(self, urn):
@ -215,9 +210,16 @@ class SchedulerDialog(QDialog, Ui_Dialog):
if d < timedelta(days=366): if d < timedelta(days=366):
self.last_downloaded.setText(_('Last downloaded')+': '+tm) self.last_downloaded.setText(_('Last downloaded')+': '+tm)
add_title_tag, custom_tags = customize_info add_title_tag, custom_tags, keep_issues = customize_info
self.add_title_tag.setChecked(add_title_tag) self.add_title_tag.setChecked(add_title_tag)
self.custom_tags.setText(u', '.join(custom_tags)) self.custom_tags.setText(u', '.join(custom_tags))
try:
keep_issues = int(keep_issues)
except:
keep_issues = 0
self.keep_issues.setValue(keep_issues)
self.keep_issues.setEnabled(self.add_title_tag.isChecked())
class Scheduler(QObject): class Scheduler(QObject):
@ -299,7 +301,7 @@ class Scheduler(QObject):
un = pw = None un = pw = None
if account_info is not None: if account_info is not None:
un, pw = account_info un, pw = account_info
add_title_tag, custom_tags = customize_info add_title_tag, custom_tags, keep_issues = customize_info
script = self.recipe_model.get_recipe(urn) script = self.recipe_model.get_recipe(urn)
pt = PersistentTemporaryFile('_builtin.recipe') pt = PersistentTemporaryFile('_builtin.recipe')
pt.write(script) pt.write(script)
@ -312,6 +314,7 @@ class Scheduler(QObject):
'recipe':pt.name, 'recipe':pt.name,
'title':recipe.get('title',''), 'title':recipe.get('title',''),
'urn':urn, 'urn':urn,
'keep_issues':keep_issues
} }
self.download_queue.add(urn) self.download_queue.add(urn)
self.start_recipe_fetch.emit(arg) self.start_recipe_fetch.emit(arg)

View File

@ -14,358 +14,403 @@
<string>Schedule news download</string> <string>Schedule news download</string>
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/scheduler.png</normaloff>:/images/scheduler.png</iconset> <normaloff>:/images/scheduler.png</normaloff>:/images/scheduler.png</iconset>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout" columnstretch="0,1,2">
<item row="0" column="0" rowspan="3"> <item row="0" column="0">
<widget class="QGroupBox" name="recipe_box"> <widget class="QLabel" name="label_8">
<property name="title"> <property name="text">
<string>Recipes</string> <string>&amp;Search:</string>
</property>
<property name="buddy">
<cstring>search</cstring>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="recipes">
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="download_all_button">
<property name="toolTip">
<string>Download all scheduled recipes at once</string>
</property>
<property name="text">
<string>Download &amp;all scheduled</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rnumber">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_3"> <widget class="SearchBox2" name="search"/>
<item> </item>
<widget class="QScrollArea" name="scrollArea"> <item row="0" column="2" rowspan="3">
<property name="frameShape"> <widget class="QScrollArea" name="scrollArea">
<enum>QFrame::NoFrame</enum> <property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>469</width>
<height>504</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="margin">
<number>0</number>
</property> </property>
<property name="widgetResizable"> <item>
<bool>true</bool> <widget class="QTabWidget" name="detail_box">
</property> <property name="currentIndex">
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>375</width>
<height>502</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="margin">
<number>0</number> <number>0</number>
</property> </property>
<item> <widget class="QWidget" name="tab">
<widget class="QTabWidget" name="detail_box"> <attribute name="title">
<property name="sizePolicy"> <string>&amp;Schedule</string>
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> </attribute>
<horstretch>0</horstretch> <layout class="QVBoxLayout" name="verticalLayout_2">
<verstretch>100</verstretch> <item>
</sizepolicy> <widget class="QLabel" name="blurb">
</property> <property name="text">
<property name="currentIndex"> <string>blurb</string>
<number>0</number> </property>
</property> <property name="wordWrap">
<widget class="QWidget" name="tab"> <bool>true</bool>
<attribute name="title"> </property>
<string>&amp;Schedule</string> <property name="openExternalLinks">
</attribute> <bool>true</bool>
<layout class="QVBoxLayout" name="verticalLayout_2"> </property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="schedule">
<property name="text">
<string>&amp;Schedule for download:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QLabel" name="blurb"> <widget class="QRadioButton" name="daily_button">
<property name="text"> <property name="text">
<string>blurb</string> <string>Every </string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="schedule"> <widget class="QComboBox" name="day">
<item>
<property name="text">
<string>day</string>
</property>
</item>
<item>
<property name="text">
<string>Monday</string>
</property>
</item>
<item>
<property name="text">
<string>Tuesday</string>
</property>
</item>
<item>
<property name="text">
<string>Wednesday</string>
</property>
</item>
<item>
<property name="text">
<string>Thursday</string>
</property>
</item>
<item>
<property name="text">
<string>Friday</string>
</property>
</item>
<item>
<property name="text">
<string>Saturday</string>
</property>
</item>
<item>
<property name="text">
<string>Sunday</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>&amp;Schedule for download:</string> <string>at</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <widget class="QTimeEdit" name="time"/>
<item>
<widget class="QRadioButton" name="daily_button">
<property name="text">
<string>Every </string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="day">
<item>
<property name="text">
<string>day</string>
</property>
</item>
<item>
<property name="text">
<string>Monday</string>
</property>
</item>
<item>
<property name="text">
<string>Tuesday</string>
</property>
</item>
<item>
<property name="text">
<string>Wednesday</string>
</property>
</item>
<item>
<property name="text">
<string>Thursday</string>
</property>
</item>
<item>
<property name="text">
<string>Friday</string>
</property>
</item>
<item>
<property name="text">
<string>Saturday</string>
</property>
</item>
<item>
<property name="text">
<string>Sunday</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>at</string>
</property>
</widget>
</item>
<item>
<widget class="QTimeEdit" name="time"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <spacer name="horizontalSpacer">
<item>
<widget class="QRadioButton" name="interval_button">
<property name="text">
<string>Every </string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="interval">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Interval at which to download this recipe. A value of zero means that the recipe will be downloaded every hour.</string>
</property>
<property name="suffix">
<string> days</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>365.100000000000023</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="last_downloaded">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="account">
<property name="title">
<string>&amp;Account</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="QLineEdit" name="username"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Username:</string>
</property>
<property name="buddy">
<cstring>username</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Password:</string>
</property>
<property name="buddy">
<cstring>password</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="show_password">
<property name="text">
<string>&amp;Show password</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>For the scheduling to work, you must leave calibre running.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>&amp;Advanced</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="add_title_tag">
<property name="text">
<string>Add &amp;title as tag</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>&amp;Extra tags:</string>
</property>
<property name="buddy">
<cstring>custom_tags</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="custom_tags"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>40</width>
<height>40</height> <height>20</height>
</size> </size>
</property> </property>
</spacer> </spacer>
</item> </item>
</layout> </layout>
</widget> </item>
</widget> <item>
</item> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QPushButton" name="download_button"> <widget class="QRadioButton" name="interval_button">
<property name="text"> <property name="text">
<string>&amp;Download now</string> <string>Every </string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item>
</widget> <widget class="QDoubleSpinBox" name="interval">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Interval at which to download this recipe. A value of zero means that the recipe will be downloaded every hour.</string>
</property>
<property name="suffix">
<string> days</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>365.100000000000023</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="last_downloaded">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="account">
<property name="title">
<string>&amp;Account</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="QLineEdit" name="username"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Username:</string>
</property>
<property name="buddy">
<cstring>username</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Password:</string>
</property>
<property name="buddy">
<cstring>password</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="show_password">
<property name="text">
<string>&amp;Show password</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>For the scheduling to work, you must leave calibre running.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>&amp;Advanced</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="add_title_tag">
<property name="text">
<string>Add &amp;title as tag</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>&amp;Extra tags:</string>
</property>
<property name="buddy">
<cstring>custom_tags</cstring>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_6">
<property name="toolTip">
<string>Maximum number of copies (issues) of this recipe to keep. Set to 0 to keep all (disable).</string>
</property>
<property name="text">
<string>&amp;Keep at most:</string>
</property>
<property name="buddy">
<cstring>keep_issues</cstring>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QSpinBox" name="keep_issues">
<property name="toolTip">
<string>&lt;p&gt;When set, this option will cause calibre to keep, at most, the specified number of issues of this periodical. Every time a new issue is downloaded, the oldest one is deleted, if the total is larger than this number.
&lt;p&gt;Note that this feature only works if you have the option to add the title as tag checked, above.
&lt;p&gt;Also, the setting for deleting periodicals older than a number of days, below, takes priority over this setting.</string>
</property>
<property name="specialValueText">
<string>all issues</string>
</property>
<property name="suffix">
<string> issues</string>
</property>
<property name="maximum">
<number>100000</number>
</property>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="custom_tags"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QPushButton" name="download_button">
<property name="text">
<string>&amp;Download now</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTreeView" name="recipes">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>&amp;Delete downloaded news older than:</string>
</property>
<property name="buddy">
<cstring>old_news</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="old_news">
<property name="toolTip">
<string>&lt;p&gt;Delete downloaded news older than the specified number of days. Set to zero to disable.
&lt;p&gt;You can also control the maximum number of issues of a specific periodical that are kept by clicking the Advanced tab for that periodical above.</string>
</property>
<property name="specialValueText">
<string>never delete</string>
</property>
<property name="suffix">
<string> days</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item row="2" column="1"> <item row="4" column="2">
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -375,24 +420,35 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="4" column="0" colspan="2">
<widget class="QSpinBox" name="old_news"> <widget class="QPushButton" name="download_all_button">
<property name="toolTip"> <property name="toolTip">
<string>Delete downloaded news older than the specified number of days. Set to zero to disable.</string> <string>Download all scheduled news sources at once</string>
</property> </property>
<property name="suffix"> <property name="text">
<string> days</string> <string>Download &amp;all scheduled</string>
</property> </property>
<property name="prefix"> </widget>
<string>Delete downloaded news older than </string> </item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="count_label">
<property name="text">
<string/>
</property> </property>
<property name="maximum"> <property name="alignment">
<number>1000</number> <set>Qt::AlignCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>SearchBox2</class>
<extends>QComboBox</extends>
<header>calibre/gui2/search_box.h</header>
</customwidget>
</customwidgets>
<resources> <resources>
<include location="../../../../resources/images.qrc"/> <include location="../../../../resources/images.qrc"/>
</resources> </resources>
@ -436,12 +492,12 @@
<slot>setEnabled(bool)</slot> <slot>setEnabled(bool)</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>456</x> <x>458</x>
<y>173</y> <y>155</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>537</x> <x>573</x>
<y>176</y> <y>158</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
@ -452,12 +508,12 @@
<slot>setEnabled(bool)</slot> <slot>setEnabled(bool)</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>456</x> <x>458</x>
<y>173</y> <y>155</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>647</x> <x>684</x>
<y>176</y> <y>157</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
@ -468,12 +524,28 @@
<slot>setEnabled(bool)</slot> <slot>setEnabled(bool)</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>456</x> <x>458</x>
<y>239</y> <y>212</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>495</x> <x>752</x>
<y>218</y> <y>215</y>
</hint>
</hints>
</connection>
<connection>
<sender>add_title_tag</sender>
<signal>toggled(bool)</signal>
<receiver>keep_issues</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>508</x>
<y>42</y>
</hint>
<hint type="destinationlabel">
<x>577</x>
<y>108</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>

View File

@ -376,7 +376,7 @@ class TagsView(QTreeView): # {{{
action='delete_user_category', key=key)) action='delete_user_category', key=key))
self.context_menu.addSeparator() self.context_menu.addSeparator()
# Hide/Show/Restore categories # Hide/Show/Restore categories
# if not key.startswith('@') or key.find('.') < 0: #if not key.startswith('@') or key.find('.') < 0:
self.context_menu.addAction(_('Hide category %s') % category, self.context_menu.addAction(_('Hide category %s') % category,
partial(self.context_menu_handler, action='hide', partial(self.context_menu_handler, action='hide',
category=key)) category=key))

View File

@ -1501,13 +1501,20 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
############# End get_categories ############# End get_categories
def tags_older_than(self, tag, delta): def tags_older_than(self, tag, delta):
'''
Return the ids of all books having the tag ``tag`` that are older than
than the specified time. tag comparison is case insensitive.
:param delta: A timedelta object or None. If None, then all ids with
the tag are returned.
'''
tag = tag.lower().strip() tag = tag.lower().strip()
now = nowf() now = nowf()
tindex = self.FIELD_MAP['timestamp'] tindex = self.FIELD_MAP['timestamp']
gindex = self.FIELD_MAP['tags'] gindex = self.FIELD_MAP['tags']
for r in self.data._data: for r in self.data._data:
if r is not None: if r is not None:
if (now - r[tindex]) > delta: if delta is None or (now - r[tindex]) > delta:
tags = r[gindex] tags = r[gindex]
if tags and tag in [x.strip() for x in if tags and tag in [x.strip() for x in
tags.lower().split(',')]: tags.lower().split(',')]:

View File

@ -81,7 +81,7 @@ Device Integration
What devices does |app| support? What devices does |app| support?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At the moment |app| has full support for the SONY PRS line, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle line, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, SpringDesign Alex, Kobo Reader, various Android phones and the iPhone/iPad. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. At the moment |app| has full support for the SONY PRS line, Barnes & Noble Nook line, Cybook Gen 3/Opus, Amazon Kindle line, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook line, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3 and clones, SpringDesign Alex, Kobo Reader, various Android phones and the iPhone/iPad. In addition, using the :guilabel:`Connect to folder` function you can use it with any ebook reader that exports itself as a USB disk.
How can I help get my device supported in |app|? How can I help get my device supported in |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -201,12 +201,14 @@ class SchedulerConfig(object):
self.root.append(sr) self.root.append(sr)
self.write_scheduler_file() self.write_scheduler_file()
def customize_recipe(self, urn, add_title_tag, custom_tags): # 'keep_issues' argument for recipe-specific number of copies to keep
def customize_recipe(self, urn, add_title_tag, custom_tags, keep_issues):
with self.lock: with self.lock:
for x in list(self.iter_customization()): for x in list(self.iter_customization()):
if x.get('id') == urn: if x.get('id') == urn:
self.root.remove(x) self.root.remove(x)
cs = E.recipe_customization({ cs = E.recipe_customization({
'keep_issues' : keep_issues,
'id' : urn, 'id' : urn,
'add_title_tag' : 'yes' if add_title_tag else 'no', 'add_title_tag' : 'yes' if add_title_tag else 'no',
'custom_tags' : ','.join(custom_tags), 'custom_tags' : ','.join(custom_tags),
@ -317,16 +319,18 @@ class SchedulerConfig(object):
return x.get('username', ''), x.get('password', '') return x.get('username', ''), x.get('password', '')
def get_customize_info(self, urn): def get_customize_info(self, urn):
keep_issues = 0
add_title_tag = True add_title_tag = True
custom_tags = [] custom_tags = []
with self.lock: with self.lock:
for x in self.iter_customization(): for x in self.iter_customization():
if x.get('id', False) == urn: if x.get('id', False) == urn:
keep_issues = x.get('keep_issues', '0')
add_title_tag = x.get('add_title_tag', 'yes') == 'yes' add_title_tag = x.get('add_title_tag', 'yes') == 'yes'
custom_tags = [i.strip() for i in x.get('custom_tags', custom_tags = [i.strip() for i in x.get('custom_tags',
'').split(',')] '').split(',')]
break break
return add_title_tag, custom_tags return add_title_tag, custom_tags, keep_issues
def get_schedule_info(self, urn): def get_schedule_info(self, urn):
with self.lock: with self.lock:

View File

@ -196,6 +196,7 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
lang_map = {} lang_map = {}
self.all_urns = set([]) self.all_urns = set([])
self.showing_count = 0 self.showing_count = 0
self.builtin_count = 0
for x in self.custom_recipe_collection: for x in self.custom_recipe_collection:
urn = x.get('id') urn = x.get('id')
self.all_urns.add(urn) self.all_urns.add(urn)
@ -211,6 +212,7 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
lang_map[lang] = factory(NewsCategory, new_root, lang) lang_map[lang] = factory(NewsCategory, new_root, lang)
factory(NewsItem, lang_map[lang], urn, x.get('title')) factory(NewsItem, lang_map[lang], urn, x.get('title'))
self.showing_count += 1 self.showing_count += 1
self.builtin_count += 1
for x in self.scheduler_config.iter_recipes(): for x in self.scheduler_config.iter_recipes():
urn = x.get('id') urn = x.get('id')
if urn not in self.all_urns: if urn not in self.all_urns:
@ -354,9 +356,9 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
self.scheduler_config.schedule_recipe(self.recipe_from_urn(urn), self.scheduler_config.schedule_recipe(self.recipe_from_urn(urn),
sched_type, schedule) sched_type, schedule)
def customize_recipe(self, urn, add_title_tag, custom_tags): def customize_recipe(self, urn, add_title_tag, custom_tags, keep_issues):
self.scheduler_config.customize_recipe(urn, add_title_tag, self.scheduler_config.customize_recipe(urn, add_title_tag,
custom_tags) custom_tags, keep_issues)
def get_to_be_downloaded_recipes(self): def get_to_be_downloaded_recipes(self):
ans = self.scheduler_config.get_to_be_downloaded_recipes() ans = self.scheduler_config.get_to_be_downloaded_recipes()