diff --git a/Changelog.yaml b/Changelog.yaml
index b8b8f2b480..b297823841 100644
--- a/Changelog.yaml
+++ b/Changelog.yaml
@@ -92,8 +92,8 @@
- title: "Various Romanian news sources"
author: Silviu Coatara
- - title: "Osnews.pl and SwiatKindle"
- author: Mori
+ - title: "Osnews.pl and SwiatCzytnikow"
+ author: Tomasz Dlugosz
- title: "Roger Ebert Journal"
author: Shane Erstad
diff --git a/src/calibre/gui2/actions/fetch_news.py b/src/calibre/gui2/actions/fetch_news.py
index 5c2a5e9663..fe51012e31 100644
--- a/src/calibre/gui2/actions/fetch_news.py
+++ b/src/calibre/gui2/actions/fetch_news.py
@@ -58,6 +58,20 @@ class FetchNewsAction(InterfaceAction):
self.scheduler.recipe_download_failed(arg)
return self.gui.job_exception(job)
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()
sync = self.gui.news_to_be_synced
sync.add(id)
diff --git a/src/calibre/gui2/dialogs/scheduler.py b/src/calibre/gui2/dialogs/scheduler.py
index b6a3bed3eb..48d0d67255 100644
--- a/src/calibre/gui2/dialogs/scheduler.py
+++ b/src/calibre/gui2/dialogs/scheduler.py
@@ -10,11 +10,9 @@ Scheduler for automated recipe downloads
from datetime import timedelta
from PyQt4.Qt import QDialog, SIGNAL, Qt, QTime, QObject, QMenu, \
- QAction, QIcon, QMutex, QTimer, pyqtSignal, QWidget, QHBoxLayout, \
- QLabel
+ QAction, QIcon, QMutex, QTimer, pyqtSignal
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.web.feeds.recipes.model import RecipeModel
from calibre.ptempfile import PersistentTemporaryFile
@@ -28,18 +26,12 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.setupUi(self)
self.recipe_model = recipe_model
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.recipe_box.layout().insertWidget(0, self._cont)
+ self.search.setMinimumContentsLength(15)
self.search.search.connect(self.recipe_model.search)
self.recipe_model.searched.connect(self.search.search_done,
type=Qt.QueuedConnection)
@@ -153,9 +145,12 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.recipe_model.un_schedule_recipe(urn)
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 = [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
def initialize_detail_box(self, urn):
@@ -215,9 +210,16 @@ class SchedulerDialog(QDialog, Ui_Dialog):
if d < timedelta(days=366):
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.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):
@@ -299,7 +301,7 @@ class Scheduler(QObject):
un = pw = None
if account_info is not None:
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)
pt = PersistentTemporaryFile('_builtin.recipe')
pt.write(script)
@@ -312,6 +314,7 @@ class Scheduler(QObject):
'recipe':pt.name,
'title':recipe.get('title',''),
'urn':urn,
+ 'keep_issues':keep_issues
}
self.download_queue.add(urn)
self.start_recipe_fetch.emit(arg)
diff --git a/src/calibre/gui2/dialogs/scheduler.ui b/src/calibre/gui2/dialogs/scheduler.ui
index 8e6ab37162..26953bbe16 100644
--- a/src/calibre/gui2/dialogs/scheduler.ui
+++ b/src/calibre/gui2/dialogs/scheduler.ui
@@ -14,358 +14,403 @@
Schedule news download
-
+
:/images/scheduler.png:/images/scheduler.png
-
- -
-
-
- Recipes
+
+
-
+
+
+ &Search:
+
+
+ search
-
-
-
-
-
- false
-
-
-
- 16
- 16
-
-
-
- true
-
-
- true
-
-
-
- -
-
-
- Download all scheduled recipes at once
-
-
- Download &all scheduled
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
- QFrame::NoFrame
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 469
+ 504
+
+
+
+
+ 0
-
- true
-
-
-
-
- 0
- 0
- 375
- 502
-
-
-
-
+
-
+
+
0
-
-
-
-
-
- 0
- 100
-
-
-
- 0
-
-
-
- &Schedule
-
-
+
+
+ &Schedule
+
+
+
-
+
+
+ blurb
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ &Schedule for download:
+
+
+
+ -
+
-
-
+
- blurb
-
-
- Qt::RichText
-
-
- true
-
-
- true
+ Every
-
-
+
+
-
+
+ day
+
+
+ -
+
+ Monday
+
+
+ -
+
+ Tuesday
+
+
+ -
+
+ Wednesday
+
+
+ -
+
+ Thursday
+
+
+ -
+
+ Friday
+
+
+ -
+
+ Saturday
+
+
+ -
+
+ Sunday
+
+
+
+
+ -
+
- &Schedule for download:
+ at
-
-
-
-
-
-
- Every
-
-
-
- -
-
-
-
-
- day
-
-
- -
-
- Monday
-
-
- -
-
- Tuesday
-
-
- -
-
- Wednesday
-
-
- -
-
- Thursday
-
-
- -
-
- Friday
-
-
- -
-
- Saturday
-
-
- -
-
- Sunday
-
-
-
-
- -
-
-
- at
-
-
-
- -
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
+
-
-
-
-
-
-
- Every
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Interval at which to download this recipe. A value of zero means that the recipe will be downloaded every hour.
-
-
- days
-
-
- 1
-
-
- 0.000000000000000
-
-
- 365.100000000000023
-
-
- 1.000000000000000
-
-
- 1.000000000000000
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- &Account
-
-
-
-
-
-
- -
-
-
- &Username:
-
-
- username
-
-
-
- -
-
-
- &Password:
-
-
- password
-
-
-
- -
-
-
- QLineEdit::Password
-
-
-
- -
-
-
- &Show password
-
-
-
-
-
-
- -
-
-
- For the scheduling to work, you must leave calibre running.
-
-
- true
-
-
-
-
-
-
-
- &Advanced
-
-
- -
-
-
- Add &title as tag
-
-
-
- -
-
-
- &Extra tags:
-
-
- custom_tags
-
-
-
- -
-
-
- -
-
+
- Qt::Vertical
+ Qt::Horizontal
- 20
- 40
+ 40
+ 20
-
-
-
- -
-
-
- &Download now
-
-
-
-
-
+
+ -
+
+
-
+
+
+ Every
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Interval at which to download this recipe. A value of zero means that the recipe will be downloaded every hour.
+
+
+ days
+
+
+ 1
+
+
+ 0.000000000000000
+
+
+ 365.100000000000023
+
+
+ 1.000000000000000
+
+
+ 1.000000000000000
+
+
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ &Account
+
+
+
-
+
+
+ -
+
+
+ &Username:
+
+
+ username
+
+
+
+ -
+
+
+ &Password:
+
+
+ password
+
+
+
+ -
+
+
+ QLineEdit::Password
+
+
+
+ -
+
+
+ &Show password
+
+
+
+
+
+
+ -
+
+
+ For the scheduling to work, you must leave calibre running.
+
+
+ true
+
+
+
+
+
+
+
+ &Advanced
+
+
+ -
+
+
+ Add &title as tag
+
+
+
+ -
+
+
+ &Extra tags:
+
+
+ custom_tags
+
+
+
+ -
+
+
+ Maximum number of copies (issues) of this recipe to keep. Set to 0 to keep all (disable).
+
+
+ &Keep at most:
+
+
+ keep_issues
+
+
+
+ -
+
+
+ <p>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.
+<p>Note that this feature only works if you have the option to add the title as tag checked, above.
+<p>Also, the setting for deleting periodicals older than a number of days, below, takes priority over this setting.
+
+
+ all issues
+
+
+ issues
+
+
+ 100000
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ &Download now
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ false
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
-
+
+
+ &Delete downloaded news older than:
+
+
+ old_news
+
+
+
+ -
+
+
+ <p>Delete downloaded news older than the specified number of days. Set to zero to disable.
+<p>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.
+
+
+ never delete
+
+
+ days
+
+
+ 1000
+
- -
+
-
Qt::Horizontal
@@ -375,24 +420,35 @@
- -
-
+
-
+
- Delete downloaded news older than the specified number of days. Set to zero to disable.
+ Download all scheduled news sources at once
-
- days
+
+ Download &all scheduled
-
- Delete downloaded news older than
+
+
+ -
+
+
+
-
- 1000
+
+ Qt::AlignCenter
+
+
+ SearchBox2
+ QComboBox
+ calibre/gui2/search_box.h
+
+
@@ -436,12 +492,12 @@
setEnabled(bool)
- 456
- 173
+ 458
+ 155
- 537
- 176
+ 573
+ 158
@@ -452,12 +508,12 @@
setEnabled(bool)
- 456
- 173
+ 458
+ 155
- 647
- 176
+ 684
+ 157
@@ -468,12 +524,28 @@
setEnabled(bool)
- 456
- 239
+ 458
+ 212
- 495
- 218
+ 752
+ 215
+
+
+
+
+ add_title_tag
+ toggled(bool)
+ keep_issues
+ setEnabled(bool)
+
+
+ 508
+ 42
+
+
+ 577
+ 108
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 4be2ba4340..bf3e9c8a14 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -1501,13 +1501,20 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
############# End get_categories
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()
now = nowf()
tindex = self.FIELD_MAP['timestamp']
gindex = self.FIELD_MAP['tags']
for r in self.data._data:
if r is not None:
- if (now - r[tindex]) > delta:
+ if delta is None or (now - r[tindex]) > delta:
tags = r[gindex]
if tags and tag in [x.strip() for x in
tags.lower().split(',')]:
diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py
index 5dd360213b..cd5a220dc3 100644
--- a/src/calibre/web/feeds/recipes/collection.py
+++ b/src/calibre/web/feeds/recipes/collection.py
@@ -201,12 +201,14 @@ class SchedulerConfig(object):
self.root.append(sr)
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:
for x in list(self.iter_customization()):
if x.get('id') == urn:
self.root.remove(x)
cs = E.recipe_customization({
+ 'keep_issues' : keep_issues,
'id' : urn,
'add_title_tag' : 'yes' if add_title_tag else 'no',
'custom_tags' : ','.join(custom_tags),
@@ -317,16 +319,18 @@ class SchedulerConfig(object):
return x.get('username', ''), x.get('password', '')
def get_customize_info(self, urn):
+ keep_issues = 0
add_title_tag = True
custom_tags = []
with self.lock:
for x in self.iter_customization():
if x.get('id', False) == urn:
+ keep_issues = x.get('keep_issues', '0')
add_title_tag = x.get('add_title_tag', 'yes') == 'yes'
custom_tags = [i.strip() for i in x.get('custom_tags',
'').split(',')]
break
- return add_title_tag, custom_tags
+ return add_title_tag, custom_tags, keep_issues
def get_schedule_info(self, urn):
with self.lock:
diff --git a/src/calibre/web/feeds/recipes/model.py b/src/calibre/web/feeds/recipes/model.py
index 559a5c08dd..553fdcc3c3 100644
--- a/src/calibre/web/feeds/recipes/model.py
+++ b/src/calibre/web/feeds/recipes/model.py
@@ -196,6 +196,7 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
lang_map = {}
self.all_urns = set([])
self.showing_count = 0
+ self.builtin_count = 0
for x in self.custom_recipe_collection:
urn = x.get('id')
self.all_urns.add(urn)
@@ -211,6 +212,7 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
lang_map[lang] = factory(NewsCategory, new_root, lang)
factory(NewsItem, lang_map[lang], urn, x.get('title'))
self.showing_count += 1
+ self.builtin_count += 1
for x in self.scheduler_config.iter_recipes():
urn = x.get('id')
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),
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,
- custom_tags)
+ custom_tags, keep_issues)
def get_to_be_downloaded_recipes(self):
ans = self.scheduler_config.get_to_be_downloaded_recipes()