mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Create documentation for feeds2lrf in User Manual
This commit is contained in:
parent
9e23a43f15
commit
416f404328
@ -20,7 +20,8 @@ from libprs500.ebooks.lrf.meta import get_metadata
|
||||
from libprs500.ebooks.lrf.parser import LRFDocument
|
||||
from libprs500.ebooks.metadata.opf import OPFCreator
|
||||
|
||||
from libprs500.ebooks.lrf.objects import PageAttr, BlockAttr
|
||||
from libprs500.ebooks.lrf.objects import PageAttr, BlockAttr, TextAttr
|
||||
|
||||
|
||||
class BlockStyle(object):
|
||||
|
||||
@ -74,7 +75,8 @@ class LRFConverter(object):
|
||||
selector = 'body.'+str(obj.id)
|
||||
self.page_css = selector + ' {\n'
|
||||
# TODO: Headers and footers
|
||||
self.page_css += '}\n'
|
||||
self.page_css += '}\n'
|
||||
|
||||
|
||||
def create_block_styles(self):
|
||||
self.block_css = ''
|
||||
@ -82,8 +84,12 @@ class LRFConverter(object):
|
||||
if isinstance(obj, BlockAttr):
|
||||
self.block_css += str(BlockStyle(obj))
|
||||
|
||||
print self.block_css
|
||||
|
||||
def create_text_styles(self):
|
||||
self.text_css = ''
|
||||
for obj in self.lrf.objects.values():
|
||||
if isinstance(obj, TextAttr):
|
||||
self.text_css += str(TextStyle(obj))
|
||||
print self.text_css
|
||||
|
||||
def create_styles(self):
|
||||
self.logger.info('Creating CSS stylesheet...')
|
||||
|
@ -12,10 +12,9 @@
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
from libprs500.ebooks.lrf import entity_to_unicode
|
||||
import struct, array, zlib, cStringIO, collections, re
|
||||
|
||||
from libprs500.ebooks.lrf import LRFParseError
|
||||
from libprs500.ebooks.lrf import LRFParseError, PRS500_PROFILE, entity_to_unicode
|
||||
from libprs500.ebooks.lrf.tags import Tag
|
||||
|
||||
ruby_tags = {
|
||||
@ -245,6 +244,10 @@ class PageAttr(StyleObject, LRFObject):
|
||||
0xF529: ['', "parse_bg_image"],
|
||||
}
|
||||
tag_map.update(LRFObject.tag_map)
|
||||
|
||||
@classmethod
|
||||
def to_css(cls, obj, inline=False):
|
||||
return ''
|
||||
|
||||
|
||||
class Color(object):
|
||||
@ -446,8 +449,42 @@ class BlockAttr(StyleObject, LRFObject):
|
||||
0xF529: ['', 'parse_bg_image'],
|
||||
}
|
||||
tag_map.update(LRFObject.tag_map)
|
||||
|
||||
@classmethod
|
||||
def to_css(cls, obj, inline=False):
|
||||
ans = ''
|
||||
def item(line):
|
||||
ans += '' if inline else '\t'
|
||||
ans += line
|
||||
ans += ' ' if inline else '\n'
|
||||
|
||||
if hasattr(obj, 'sidemargin'):
|
||||
margin = str(obj.sidemargin) + 'px'
|
||||
item('margin-left: %(m)s; margin-right: %(m)s;'%dict(m=margin))
|
||||
if hasattr(obj, 'topskip'):
|
||||
item('margin-top: %dpx;'%obj.topskip)
|
||||
if hasattr(obj, 'footskip'):
|
||||
item('margin-bottom: %dpx;'%obj.footskip)
|
||||
if hasattr(obj, 'framewidth'):
|
||||
item('border: solid %dpx'%obj.framewidth)
|
||||
if hasattr(obj, 'framecolor') and obj.framecolor.a < 255:
|
||||
item('border-color: %s;'%obj.framecolor.to_html())
|
||||
if hasattr(obj, 'bgcolor') and obj.bgcolor.a < 255:
|
||||
item('background-color: %s;'%obj.bgcolor.to_html())
|
||||
|
||||
return ans
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TextAttr(StyleObject, LRFObject):
|
||||
|
||||
FONT_MAP = collections.defaultdict(lambda : 'serif')
|
||||
for key, value in PRS500_PROFILE.default_fonts.items():
|
||||
FONT_MAP[value] = key
|
||||
|
||||
tag_map = {
|
||||
0xF511: ['fontsize', 'w'],
|
||||
0xF512: ['fontwidth', 'w'],
|
||||
@ -472,6 +509,45 @@ class TextAttr(StyleObject, LRFObject):
|
||||
}
|
||||
tag_map.update(ruby_tags)
|
||||
tag_map.update(LRFObject.tag_map)
|
||||
|
||||
@classmethod
|
||||
def to_css(cls, obj, inline=False):
|
||||
ans = ''
|
||||
def item(line):
|
||||
ans += '' if inline else '\t'
|
||||
ans += line
|
||||
ans += ' ' if inline else '\n'
|
||||
|
||||
fs = getattr(obj, 'fontsize', None)
|
||||
if fs is not None:
|
||||
item('font-size: %fpt;'%(int(fs)/10.))
|
||||
fw = getattr(obj, 'fontweight', None)
|
||||
if fw is not None:
|
||||
item('font-weight: %s;'%('bold' if int(fw) >= 700 else 'normal'))
|
||||
fn = getattr(obj, 'fontfacename', None)
|
||||
if fn is not None:
|
||||
fn = cls.FONT_MAP[fn]
|
||||
item('font-family: %s;'%fn)
|
||||
fg = getattr(obj, 'textcolor', None)
|
||||
if fg is not None:
|
||||
fg = fg.to_html()
|
||||
item('color: %s;'%fg)
|
||||
bg = getattr(obj, 'textbgcolor', None)
|
||||
if bg is not None:
|
||||
bg = bg.to_html()
|
||||
item('background-color: %s;'%bg)
|
||||
al = getattr(obj, 'align', None)
|
||||
if al is not None:
|
||||
al = dict(head='left', center='center', foot='right')
|
||||
item('text-align: %s;'%al)
|
||||
lh = getattr(obj, 'linespace', None)
|
||||
if lh is not None:
|
||||
item('text-align: %fpt;'%(int(lh)/10.))
|
||||
pi = getattr(obj, 'parindent', None)
|
||||
if pi is not None:
|
||||
item('text-indent: %fpt;'%(int(pi)/10.))
|
||||
|
||||
return ans
|
||||
|
||||
|
||||
class Block(LRFStream):
|
||||
|
@ -17,7 +17,8 @@ import time
|
||||
from PyQt4.QtCore import SIGNAL
|
||||
from PyQt4.QtGui import QDialog, QMessageBox
|
||||
|
||||
from libprs500.ebooks.lrf.web.profiles import FullContentProfile, create_class
|
||||
from libprs500.web.feeds.recipes import compile_recipe
|
||||
from libprs500.web.feeds.news import AutomaticNewsRecipe
|
||||
from libprs500.gui2.dialogs.user_profiles_ui import Ui_Dialog
|
||||
from libprs500.gui2 import qstring_to_unicode, error_dialog, question_dialog
|
||||
from libprs500.gui2.widgets import PythonHighlighter
|
||||
@ -52,56 +53,56 @@ class UserProfiles(QDialog, Ui_Dialog):
|
||||
if not current:
|
||||
current = previous
|
||||
src = current.user_data[1]
|
||||
if 'class BasicUserProfile' in src:
|
||||
profile = create_class(src)
|
||||
self.populate_options(profile)
|
||||
if 'class BasicUserRecipe' in src:
|
||||
recipe = compile_recipe(src)
|
||||
self.populate_options(recipe)
|
||||
self.stacks.setCurrentIndex(0)
|
||||
self.toggle_mode_button.setText('Switch to Advanced mode')
|
||||
self.toggle_mode_button.setText(_('Switch to Advanced mode'))
|
||||
else:
|
||||
self.source_code.setPlainText(src)
|
||||
self.highlighter = PythonHighlighter(self.source_code.document())
|
||||
self.stacks.setCurrentIndex(1)
|
||||
self.toggle_mode_button.setText('Switch to Basic mode')
|
||||
self.toggle_mode_button.setText(_('Switch to Basic mode'))
|
||||
|
||||
def toggle_mode(self, *args):
|
||||
if self.stacks.currentIndex() == 1:
|
||||
self.stacks.setCurrentIndex(0)
|
||||
self.toggle_mode_button.setText('Switch to Advanced mode')
|
||||
self.toggle_mode_button.setText(_('Switch to Advanced mode'))
|
||||
else:
|
||||
self.stacks.setCurrentIndex(1)
|
||||
self.toggle_mode_button.setText('Switch to Basic mode')
|
||||
self.toggle_mode_button.setText(_('Switch to Basic mode'))
|
||||
if not qstring_to_unicode(self.source_code.toPlainText()).strip():
|
||||
src = self.options_to_profile()[0]
|
||||
self.source_code.setPlainText(src.replace('BasicUserProfile', 'AdvancedUserProfile'))
|
||||
src = self.options_to_profile()[0].replace('AutomaticNewsRecipe', 'BasicNewsRecipe')
|
||||
self.source_code.setPlainText(src.replace('BasicUserRecipe', 'AdvancedUserRecipe'))
|
||||
self.highlighter = PythonHighlighter(self.source_code.document())
|
||||
|
||||
|
||||
def add_feed(self, *args):
|
||||
title = qstring_to_unicode(self.feed_title.text()).strip()
|
||||
if not title:
|
||||
d = error_dialog(self, 'Feed must have a title', 'The feed must have a title')
|
||||
d.exec_()
|
||||
error_dialog(self, _('Feed must have a title'),
|
||||
_('The feed must have a title')).exec_()
|
||||
return
|
||||
url = qstring_to_unicode(self.feed_url.text()).strip()
|
||||
if not url:
|
||||
d = error_dialog(self, 'Feed must have a URL', 'The feed %s must have a URL'%title)
|
||||
d.exec_()
|
||||
error_dialog(self, _('Feed must have a URL'),
|
||||
_('The feed %s must have a URL')%title).exec_()
|
||||
return
|
||||
try:
|
||||
self.added_feeds.add_item(title+' - '+url, (title, url))
|
||||
except ValueError:
|
||||
error_dialog(self, 'Already in list', 'This feed has already been added to the profile').exec_()
|
||||
error_dialog(self, _('Already exists'),
|
||||
_('This feed has already been added to the recipe')).exec_()
|
||||
return
|
||||
self.feed_title.setText('')
|
||||
self.feed_url.setText('')
|
||||
|
||||
def options_to_profile(self):
|
||||
classname = 'BasicUserProfile'+str(int(time.time()))
|
||||
classname = 'BasicUserRecipe'+str(int(time.time()))
|
||||
title = qstring_to_unicode(self.profile_title.text()).strip()
|
||||
if not title:
|
||||
title = classname
|
||||
self.profile_title.setText(title)
|
||||
summary_length = self.summary_length.value()
|
||||
oldest_article = self.oldest_article.value()
|
||||
max_articles = self.max_articles.value()
|
||||
feeds = [i.user_data for i in self.added_feeds.items()]
|
||||
@ -109,20 +110,19 @@ class UserProfiles(QDialog, Ui_Dialog):
|
||||
src = '''\
|
||||
class %(classname)s(%(base_class)s):
|
||||
title = %(title)s
|
||||
summary_length = %(summary_length)d
|
||||
oldest_article = %(oldest_article)d
|
||||
max_articles_per_feed = %(max_articles)d
|
||||
|
||||
feeds = %(feeds)s
|
||||
'''%dict(classname=classname, title=repr(title), summary_length=summary_length,
|
||||
'''%dict(classname=classname, title=repr(title),
|
||||
feeds=repr(feeds), oldest_article=oldest_article,
|
||||
max_articles=max_articles,
|
||||
base_class='DefaultProfile' if self.full_articles.isChecked() else 'FullContentProfile')
|
||||
base_class='AutomaticNewsRecipe')
|
||||
return src, title
|
||||
|
||||
|
||||
def populate_source_code(self):
|
||||
src = self.options_to_profile().replace('BasicUserProfile', 'AdvancedUserProfile')
|
||||
src = self.options_to_profile().replace('BasicUserRecipe', 'AdvancedUserRecipe')
|
||||
self.source_code.setPlainText(src)
|
||||
self.highlighter = PythonHighlighter(self.source_code.document())
|
||||
|
||||
@ -131,26 +131,26 @@ class %(classname)s(%(base_class)s):
|
||||
src, title = self.options_to_profile()
|
||||
|
||||
try:
|
||||
create_class(src)
|
||||
compile_recipe(src)
|
||||
except Exception, err:
|
||||
error_dialog(self, 'Invalid input',
|
||||
'<p>Could not create profile. Error:<br>%s'%str(err)).exec_()
|
||||
error_dialog(self, _('Invalid input'),
|
||||
_('<p>Could not create recipe. Error:<br>%s')%str(err)).exec_()
|
||||
return
|
||||
profile = src
|
||||
else:
|
||||
src = qstring_to_unicode(self.source_code.toPlainText())
|
||||
try:
|
||||
title = create_class(src).title
|
||||
title = compile_recipe(src).title
|
||||
except Exception, err:
|
||||
error_dialog(self, 'Invalid input',
|
||||
'<p>Could not create profile. Error:<br>%s'%str(err)).exec_()
|
||||
error_dialog(self, _('Invalid input'),
|
||||
_('<p>Could not create recipe. Error:<br>%s')%str(err)).exec_()
|
||||
return
|
||||
profile = src.replace('BasicUserProfile', 'AdvancedUserProfile')
|
||||
profile = src.replace('BasicUserRecipe', 'AdvancedUserRecipe')
|
||||
try:
|
||||
self.available_profiles.add_item(title, (title, profile), replace=False)
|
||||
except ValueError:
|
||||
d = question_dialog(self, 'Replace profile?',
|
||||
'A custom profile named %s already exists. Do you want to replace it?'%title)
|
||||
d = question_dialog(self, _('Replace recipe?'),
|
||||
_('A custom recipe named %s already exists. Do you want to replace it?')%title)
|
||||
if d.exec_() == QMessageBox.Yes:
|
||||
self.available_profiles.add_item(title, (title, profile), replace=True)
|
||||
else:
|
||||
@ -160,18 +160,17 @@ class %(classname)s(%(base_class)s):
|
||||
def populate_options(self, profile):
|
||||
self.oldest_article.setValue(profile.oldest_article)
|
||||
self.max_articles.setValue(profile.max_articles_per_feed)
|
||||
self.summary_length.setValue(profile.summary_length)
|
||||
self.profile_title.setText(profile.title)
|
||||
self.added_feeds.clear()
|
||||
for title, url in profile.feeds:
|
||||
feeds = [] if profile.feeds is None else profile.feeds
|
||||
for title, url in feeds:
|
||||
self.added_feeds.add_item(title+' - '+url, (title, url))
|
||||
self.feed_title.setText('')
|
||||
self.feed_url.setText('')
|
||||
self.full_articles.setChecked(isinstance(profile, FullContentProfile))
|
||||
|
||||
|
||||
def clear(self):
|
||||
self.populate_options(FullContentProfile)
|
||||
self.populate_options(AutomaticNewsRecipe)
|
||||
self.source_code.setText('')
|
||||
|
||||
def profiles(self):
|
||||
|
@ -45,7 +45,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>Available user profiles</string>
|
||||
<string>Available user recipes</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
@ -64,7 +64,7 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_profile_button" >
|
||||
<property name="text" >
|
||||
<string>Add/Update &profile</string>
|
||||
<string>Add/Update &recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >:/images/plus.svg</iconset>
|
||||
@ -74,7 +74,7 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="remove_profile_button" >
|
||||
<property name="text" >
|
||||
<string>&Remove profile</string>
|
||||
<string>&Remove recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >:/images/list_remove.svg</iconset>
|
||||
@ -83,7 +83,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="" >
|
||||
<widget class="QWidget" name="layoutWidget" >
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QPushButton" name="toggle_mode_button" >
|
||||
@ -105,7 +105,7 @@
|
||||
<string><html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Create a basic news profile, by adding RSS feeds to it. <br />For most feeds, you will have to use the "Advanced" setting to further customize the fetch process.<br />The Basic tab is useful mainly for feeds that have the full article content embedded within them.</p></body></html></string>
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Create a basic news recipe, by adding RSS feeds to it. <br />For most feeds, you will have to use the "Advanced mode" to further customize the fetch process.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::RichText</enum>
|
||||
@ -120,7 +120,7 @@ p, li { white-space: pre-wrap; }
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>Profile &title:</string>
|
||||
<string>Recipe &title:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>profile_title</cstring>
|
||||
@ -137,32 +137,6 @@ p, li { white-space: pre-wrap; }
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2" >
|
||||
<widget class="QLabel" name="label_3" >
|
||||
<property name="text" >
|
||||
<string>&Summary length:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>summary_length</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2" >
|
||||
<widget class="QSpinBox" name="summary_length" >
|
||||
<property name="suffix" >
|
||||
<string> characters</string>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<number>100000</number>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<number>500</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_6" >
|
||||
<property name="text" >
|
||||
@ -218,22 +192,12 @@ p, li { white-space: pre-wrap; }
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QCheckBox" name="full_articles" >
|
||||
<property name="toolTip" >
|
||||
<string>Try to follow links in the RSS feed to full articles on the web. If you enable this option, you're probably going to end up having to use the advanced mode.</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Try to download &full articles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2" >
|
||||
<property name="title" >
|
||||
<string>Feeds in profile</string>
|
||||
<string>Feeds in recipe</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
@ -246,7 +210,7 @@ p, li { white-space: pre-wrap; }
|
||||
<item>
|
||||
<widget class="QToolButton" name="remove_feed_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Remove feed from profile</string>
|
||||
<string>Remove feed from recipe</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
@ -262,7 +226,7 @@ p, li { white-space: pre-wrap; }
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3" >
|
||||
<property name="title" >
|
||||
<string>Add feed to profile</string>
|
||||
<string>Add feed to recipe</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
@ -294,7 +258,7 @@ p, li { white-space: pre-wrap; }
|
||||
<item row="2" column="0" colspan="2" >
|
||||
<widget class="QPushButton" name="add_feed_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Add feed to profile</string>
|
||||
<string>Add feed to recipe</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>&Add feed</string>
|
||||
@ -314,7 +278,7 @@ p, li { white-space: pre-wrap; }
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8" >
|
||||
<property name="text" >
|
||||
<string>For help with writing advanced news profiles, please visit <a href="https://__appname__.kovidgoyal.net/wiki/UserProfiles">UserProfiles</a></string>
|
||||
<string>For help with writing advanced news recipes, please visit <a href="http://__appname__.kovidgoyal.net/user_manual/news.html">User Recipes</a></string>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
@ -327,7 +291,7 @@ p, li { white-space: pre-wrap; }
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4" >
|
||||
<property name="title" >
|
||||
<string>Profile source code (python)</string>
|
||||
<string>Recipe source code (python)</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
|
@ -24,6 +24,7 @@ from libprs500.ebooks.metadata.meta import set_metadata, metadata_from_formats
|
||||
from libprs500.ebooks.metadata.opf import OPFCreator
|
||||
from libprs500.ebooks.metadata import MetaInformation
|
||||
from libprs500.ebooks import BOOK_EXTENSIONS
|
||||
from libprs500.web.feeds.recipes import migrate_automatic_profile_to_automatic_recipe
|
||||
|
||||
class Concatenate(object):
|
||||
'''String concatenation aggregator for sqlite'''
|
||||
@ -779,6 +780,15 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
||||
conn.execute('pragma user_version=8')
|
||||
conn.commit()
|
||||
|
||||
@staticmethod
|
||||
def upgrade_version8(conn):
|
||||
feeds = conn.execute('SELECT title, script FROM feeds').fetchall()
|
||||
for title, script in feeds:
|
||||
script = migrate_automatic_profile_to_automatic_recipe(script)
|
||||
conn.execute('UPDATE feeds SET script=? WHERE title=?', (script, title))
|
||||
conn.execute('pragma user_version=9')
|
||||
conn.commit()
|
||||
|
||||
def __del__(self):
|
||||
global _lock_file
|
||||
import os
|
||||
@ -808,6 +818,8 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
||||
LibraryDatabase.upgrade_version6(self.conn)
|
||||
if self.user_version == 7: # Upgrade to 8
|
||||
LibraryDatabase.upgrade_version7(self.conn)
|
||||
if self.user_version == 8: # Upgrade to 9
|
||||
LibraryDatabase.upgrade_version8(self.conn)
|
||||
|
||||
def close(self):
|
||||
global _lock_file
|
||||
|
@ -21,7 +21,7 @@ help:
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
|
||||
clean:
|
||||
-rm -rf .build/*
|
||||
-rm -rf .build/* cli
|
||||
|
||||
html:
|
||||
mkdir -p .build/html .build/doctrees
|
||||
|
@ -24,7 +24,7 @@ import custom
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
|
||||
extensions = ['custom']
|
||||
extensions = ['sphinx.ext.autodoc', 'custom']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['.templates']
|
||||
@ -54,9 +54,8 @@ release = __version__
|
||||
today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
unused_docs = ['global']
|
||||
unused_docs = ['global', 'cli/global']
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
@ -105,7 +104,7 @@ html_last_updated_fmt = '%b %d, %Y'
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
html_use_modindex = False
|
||||
# html_use_modindex = True
|
||||
|
||||
# If true, the reST sources are included in the HTML build as _sources/<name>.
|
||||
html_copy_source = False
|
||||
|
@ -15,8 +15,13 @@
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
import shutil, sys, os
|
||||
import shutil, sys, os, inspect, re
|
||||
from sphinx.builder import StandaloneHTMLBuilder, bold
|
||||
from sphinx.util import rpartition
|
||||
from sphinx.ext.autodoc import get_module_charset, prepare_docstring
|
||||
from docutils.statemachine import ViewList
|
||||
from docutils import nodes
|
||||
|
||||
from genshi.template import TextTemplate
|
||||
sys.path.append(os.path.abspath('../../../'))
|
||||
from libprs500.linux import entry_points
|
||||
@ -29,7 +34,7 @@ def substitute(app, doctree):
|
||||
pass
|
||||
|
||||
CLI_INDEX = '''\
|
||||
.. include:: ../global.rst
|
||||
.. include:: global.rst
|
||||
||
|
||||
.. _cli:
|
||||
||
|
||||
@ -62,22 +67,19 @@ You can see usage for undocumented commands by executing them without arguments
|
||||
'''
|
||||
|
||||
CLI_CMD=r'''
|
||||
.. include:: ../global.rst
|
||||
.. include:: global.rst
|
||||
||
|
||||
.. _$cmd:
|
||||
||
|
||||
.. role:: mycmdopt(literal)
|
||||
:class: bold
|
||||
||
|
||||
#def option(opt)
|
||||
`${opt.get_opt_string() + ((', '+', '.join(opt._short_opts)) if opt._short_opts else '')}`:mycmdopt:
|
||||
:option:`${opt.get_opt_string() + ((', '+', '.join(opt._short_opts)) if opt._short_opts else '')}`
|
||||
#end
|
||||
$cmd
|
||||
====================================================================
|
||||
||
|
||||
Usage::
|
||||
.. code-block:: none
|
||||
||
|
||||
$cmdline
|
||||
$cmdline
|
||||
||
|
||||
#for line in usage
|
||||
#choose
|
||||
@ -111,7 +113,8 @@ ${option(opt)}
|
||||
#end
|
||||
'''
|
||||
|
||||
def cli_docs(info):
|
||||
def cli_docs(app):
|
||||
info = app.builder.info
|
||||
info(bold('creating CLI documentation...'))
|
||||
documented_cmds = []
|
||||
undocumented_cmds = []
|
||||
@ -134,7 +137,11 @@ def cli_docs(info):
|
||||
raw = raw.replace('||', '\n')
|
||||
if not os.path.exists('cli'):
|
||||
os.makedirs('cli')
|
||||
open(os.path.join('cli', 'cli-index.rst'), 'wb').write(raw)
|
||||
if not os.path.exists(os.path.join('cli', 'global.rst')):
|
||||
os.link('global.rst', os.path.join('cli', 'global.rst'))
|
||||
if not os.path.exists(os.path.join('cli', 'cli-index.rst')):
|
||||
info(bold('creating cli-index...'))
|
||||
open(os.path.join('cli', 'cli-index.rst'), 'wb').write(raw)
|
||||
|
||||
templ = TextTemplate(CLI_CMD)
|
||||
for cmd, parser in documented_cmds:
|
||||
@ -148,18 +155,68 @@ def cli_docs(info):
|
||||
|
||||
raw = templ.generate(cmd=cmd, cmdline=cmdline, usage=usage, groups=groups).render()
|
||||
raw = raw.replace('||', '\n').replace('<', '<').replace('>', '>')
|
||||
open(os.path.join('cli', cmd+'.rst'), 'wb').write(raw)
|
||||
if not os.path.exists(os.path.join('cli', cmd+'.rst')):
|
||||
info(bold('creating docs for %s...'%cmd))
|
||||
open(os.path.join('cli', cmd+'.rst'), 'wb').write(raw)
|
||||
|
||||
def generate(app):
|
||||
app.builder.info(bold('copying images to the build tree...'))
|
||||
shutil.rmtree('.build/html/images', True)
|
||||
shutil.copytree('images', '.build/html/images')
|
||||
shutil.rmtree('.build/html/images/.svn', True)
|
||||
shutil.rmtree('.build/html/images/.bzr', True)
|
||||
cli_docs(app.builder.info)
|
||||
def auto_member(dirname, arguments, options, content, lineno,
|
||||
content_offset, block_text, state, state_machine):
|
||||
name = arguments[0]
|
||||
env = state.document.settings.env
|
||||
|
||||
mod_cls, obj = rpartition(name, '.')
|
||||
if not mod_cls and hasattr(env, 'autodoc_current_class'):
|
||||
mod_cls = env.autodoc_current_class
|
||||
if not mod_cls:
|
||||
mod_cls = env.currclass
|
||||
mod, cls = rpartition(mod_cls, '.')
|
||||
if not mod and hasattr(env, 'autodoc_current_module'):
|
||||
mod = env.autodoc_current_module
|
||||
if not mod:
|
||||
mod = env.currmodule
|
||||
|
||||
module = __import__(mod, None, None, ['foo'])
|
||||
cls = getattr(module, cls)
|
||||
lines = inspect.getsourcelines(cls)[0]
|
||||
|
||||
comment_lines = []
|
||||
for i, line in enumerate(lines):
|
||||
if re.search(r'%s\s*=\s*\S+'%obj, line) and not line.strip().startswith('#:'):
|
||||
for j in range(i-1, 0, -1):
|
||||
raw = lines[j].strip()
|
||||
if not raw.startswith('#:'):
|
||||
break
|
||||
comment_lines.append(raw[2:])
|
||||
break
|
||||
comment_lines.reverse()
|
||||
docstring = '\n'.join(comment_lines)
|
||||
|
||||
if module is not None and docstring is not None:
|
||||
docstring = docstring.decode(get_module_charset(mod))
|
||||
|
||||
result = ViewList()
|
||||
result.append('.. attribute:: %s.%s'%(cls.__name__, obj), '<autodoc>')
|
||||
result.append('', '<autodoc>')
|
||||
|
||||
docstring = prepare_docstring(docstring)
|
||||
for i, line in enumerate(docstring):
|
||||
result.append(' ' + line, '<docstring of %s>' % name, i)
|
||||
|
||||
result.append('', '')
|
||||
result.append(' **Default**: ``%s``'%repr(getattr(cls, obj, None)), '<default memeber value>')
|
||||
result.append('', '')
|
||||
node = nodes.paragraph()
|
||||
state.nested_parse(result, content_offset, node)
|
||||
|
||||
return node
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_builder(CustomBuilder)
|
||||
app.add_directive('automember', auto_member, 1, (1, 0, 1))
|
||||
app.connect('doctree-read', substitute)
|
||||
app.connect('builder-inited', generate)
|
||||
app.connect('builder-inited', cli_docs)
|
||||
|
||||
|
30
src/libprs500/manual/glossary.rst
Normal file
30
src/libprs500/manual/glossary.rst
Normal file
@ -0,0 +1,30 @@
|
||||
.. include:: global.rst
|
||||
|
||||
Glossary
|
||||
==========
|
||||
|
||||
.. glossary::
|
||||
|
||||
RSS
|
||||
**RSS** *(Really Simple Syndication)* is a web feed format that is used to publish frequently updated content, like news articles, blog posts, etc. It is a format that is particularly suited to being read by computers, and is therefore the preferred way of getting content from the web into an e-book. There are many other feed formats in use on the internet, and |app| understands most of them. In particular, it has good support for the *ATOM* format, which is commonly used for blogs.
|
||||
|
||||
recipe
|
||||
A recipe is a set of instructions that teach |app| how to convert an online news source, like a magazine or a blog into an e-book. A recipe, is essentially `python <http://www.python.org>`_ code. As such, it is capable of converting arbitrarily complex news sources into e-books. At the simplest level, it is just a set of variables such as URLs that give |app| enough information to go out onto the internet and download the news.
|
||||
|
||||
HTML
|
||||
**HTML** *(Hyper Text Mark-Up Language)*, a subset of Standard Generalized Mark-Up Language (SGML) for electronic publishing, the specific standard used for the World Wide Web.
|
||||
|
||||
CSS
|
||||
**CSS** *(Cascading Style Sheets)* a language used to describe how an :term:`HTML` document should be rendered (visual styling).
|
||||
|
||||
API
|
||||
**API** *(Application Programming Interface)* is a source code interface that a library provides to support requests for services to be made of it by computer programs.
|
||||
|
||||
LRF
|
||||
**LRF** The e-book format that is read by the SONY e-book readers.
|
||||
|
||||
URL
|
||||
**URL** *(Uniform Resource Locator)* for example: ``http://example.com``
|
||||
|
||||
regexp
|
||||
**Regular expressions** provide a concise and flexible means for identifying strings of text of interest, such as particular characters, words, or patterns of characters. See http://docs.python.org/lib/re-syntax.html for the syntax of regular expressions used in python.
|
BIN
src/libprs500/manual/images/bbc_advanced.png
Normal file
BIN
src/libprs500/manual/images/bbc_advanced.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
src/libprs500/manual/images/bbc_altered.png
Normal file
BIN
src/libprs500/manual/images/bbc_altered.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
src/libprs500/manual/images/bbc_altered1.png
Normal file
BIN
src/libprs500/manual/images/bbc_altered1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
src/libprs500/manual/images/custom_news.png
Normal file
BIN
src/libprs500/manual/images/custom_news.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
@ -29,6 +29,7 @@ Sections
|
||||
metadata
|
||||
cli/cli-index
|
||||
faq
|
||||
glossary
|
||||
|
||||
Convenience
|
||||
-----------------------
|
||||
|
@ -4,3 +4,207 @@
|
||||
|
||||
Adding your favorite news website
|
||||
==================================
|
||||
|
||||
|app| has a powerful, flexible and easy-to-use framework for downloading news from the internet and converting it into an e-book. In the following, I will show you by means of examples, how to get news from various websites.
|
||||
|
||||
To gain an understanding of how to use the framework, follow the examples in the order listed below:
|
||||
|
||||
.. contents::
|
||||
:depth: 2
|
||||
:local:
|
||||
|
||||
Completely automatic fetching
|
||||
-------------------------------
|
||||
|
||||
If your news source is simple enough, |app| may well be able to fetch it completely automatically, all you need to do is provide the URL. |app| gathers all the information needed to download a news source into a :term:`recipe`. In order to tell |app| about a news source, you have to create a :term:`recipe` for it. Let's see some examples:
|
||||
|
||||
.. _portfolio:
|
||||
|
||||
portfolio.com
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*portfolio.com* is the website for *Condé Nast Portfolio*, a business related magazine. In order to download articles from the magazine and convert them to e-books, we rely on the :term:`RSS` feeds of portfolio.com. A list of such feeds is available at http://www.portfolio.com/rss/.
|
||||
|
||||
Lets pick a couple of feeds that look interesting:
|
||||
|
||||
#. Business Travel: http://feeds.portfolio.com/portfolio/businesstravel
|
||||
#. Tech Observer: http://feeds.portfolio.com/portfolio/thetechobserver
|
||||
|
||||
I got the URLs by clicking the little orange RSS icon next to each feed name. To make |app| download the feeds and convert them into an e-book, you should click the :guilabel:`Fetch news` button and then the :guilabel:`Add a custom news source` menu item. A dialog similar to that shown below should open up.
|
||||
|
||||
.. image:: images/custom_news.png
|
||||
|
||||
First enter ``Portfolio`` into the :guilabel:`Recipe title` field. This will be the title of the e-book that will be created from the articles in the above feeds.
|
||||
|
||||
The next two fields (:guilabel:`Oldest article` and :guilabel:`Max. number of articles`) allow you some control over how many articles should be downloaded from each feed, and they are pretty self explanatory.
|
||||
|
||||
To add the feeds to the recipe, enter the feed title and the feed URL and click the :guilabel:`Add feed` button. Once you have added both feeds, simply click the :guilabel:`Add/update recipe` button and you're done! Close the dialog.
|
||||
|
||||
To test your new :term:`recipe`, click the :guilabel:`Fetch news` button and in the :guilabel:`Custom news sources` sub-menu click :guilabel:`Portfolio`. After a couple of minutes, the newly downloaded Portfolio e-book will appear in the main library view (if you have your reader connected, it will be put onto the reader instead of into the library). Select it and hit the :guilabel:`View` button to read!
|
||||
|
||||
The reason this worked so well, with so little effort is that *portfolio.com* provides *full-content* :term:`RSS` feeds, i.e., the article content is embedded in the feed itself. For most news sources that provide news in this fashion, with *full-content* feeds, you don't need any more effort to convert them to e-books. Now we will look at a news source that does not provide full content feeds. In such feeds, the full article is a webpage and the feed only contains a link to the webpage with a short summary of the article.
|
||||
|
||||
.. _bbc:
|
||||
|
||||
bbc.co.uk
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Lets try the following two feeds from *The BBC*:
|
||||
|
||||
#. News Front Page: http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml
|
||||
#. Science/Nature: http://newsrss.bbc.co.uk/rss/newsonline_world_edition/science/nature/rss.xml
|
||||
|
||||
Follow the procedure outlined in :ref:`portfolio` to create a recipe for *The BBC* (using the feeds above). Looking at the downloaded e-book, we see that |app| has done a creditable job of extracting only the content you care about from each article's webpage. However, the extraction process is not perfect. Sometimes it leaves in undesirable content like menus and navigation aids or it removes content that should have been left alone, like article headings. In order, to have perfect content extraction, we will need to customize the fetch process, as described in the next section.
|
||||
|
||||
Customizing the fetch process
|
||||
--------------------------------
|
||||
|
||||
When you want to perfect the download process, or download content from a particularly complex website, you can avail yourself of all the power and flexibility of the :term:`recipe` framework. In order to do that, in the :guilabel:`Add custom news sources` dialog, simply click the :guilabel:`Switch to Advanced mode` button.
|
||||
|
||||
The easiest and often most productive customization is to use the print version of the online articles. The print version typically has much less cruft and translates much more smoothly to an e-book. Let's try to use the print version of the articles from *The BBC*.
|
||||
|
||||
Using the print version of bbc.co.uk
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The first step is to look at the e-book we downloaded previously from :ref:`bbc`. At the end of each article, in the e-book is a little blurb telling you where the article was downloaded from. Copy and paste that URL into a browser. Now on the article webpage look for a link that points to the "Printable version". Click it to see the print version of the article. It looks much neater! Now compare the two URLs. For me they were:
|
||||
|
||||
Article URL
|
||||
http://news.bbc.co.uk/2/hi/science/nature/7312016.stm
|
||||
|
||||
Print version URL
|
||||
http://newsvote.bbc.co.uk/mpapps/pagetools/print/news.bbc.co.uk/2/hi/science/nature/7312016.stm
|
||||
|
||||
So it looks like to get the print version, we need to prefix every article URL with:
|
||||
|
||||
newsvote.bbc.co.uk/mpapps/pagetools/print/
|
||||
|
||||
Now in the :guilabel:`Advanced Mode` of the Custom news sources dialog, you should see something like (remember to select *The BBC* recipe before switching to advanced mode):
|
||||
|
||||
.. image:: images/bbc_advanced.png
|
||||
|
||||
You can see that the fields from the :guilabel:`Basic mode` have been translated to python code in a straightforward manner. We need to add instructions to this recipe to use the print version of the articles. All that's needed is to add the following two lines:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('http://', 'http://newsvote.bbc.co.uk/mpapps/pagetools/print/')
|
||||
|
||||
This is python, so indentation is important. After you've added the lines, it should look like:
|
||||
|
||||
.. image:: images/bbc_altered.png
|
||||
|
||||
In the above, ``def print_version(self, url)`` defines a *method* that is called by |app| for every article. ``url`` is the URL of the original article. What ``print_version`` does is take that url and replace it with the new URL that points to the print version of the article. To learn about `python <http://www.python.org>`_ see the `tutorial <http://docs.python.org/tut/>`_.
|
||||
|
||||
Now, click the :guilabel:`Add/update recipe` button and your changes will be saved. Re-download the e-book. You should have a much improved e-book. One of the problems with the new version is that the fonts on the print version webpage are too small. This is automatically fixed when converting to an e-book, but even after the fixing process, the font size of the menus and navigation bar to become too large relative to the article text. To fix this, we will do some more customization, in the next section.
|
||||
|
||||
Replacing article styles
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the previous section, we saw that the font size for articles from the print version of *The BBC* was too small. In most websites, *The BBC* included, this font size is set by means of :term:`CSS` stylesheets. We can disable the fetching of such stylesheets by adding the line::
|
||||
|
||||
no_stylesheets = True
|
||||
|
||||
The recipe now looks like:
|
||||
|
||||
.. _bbc1:
|
||||
|
||||
.. image:: images/bbc_altered1.png
|
||||
|
||||
The new version looks pretty good. If you're a perfectionist, you'll want to read the next section, which deals with actually modifying the downloaded content.
|
||||
|
||||
Slicing and dicing
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|app| contains very powerful and flexible abilities when it comes to manipulating downloaded content. To show off a couple of these, let's look at our old friend the :ref:`The BBC <bbc1>` recipe again. Looking at the source code (:term:`HTML`) of a couple of articles (print version), we see that they have a footer that contains no useful information, contained in
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div class="footer">
|
||||
...
|
||||
</div>
|
||||
|
||||
This can be removed by adding::
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':'footer'})]
|
||||
|
||||
to the recipe. Finally, lets replace some of the :term:`CSS` that we disabled earlier, with our own :term:`CSS` that is suitable for conversion to an e-book::
|
||||
|
||||
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
|
||||
|
||||
With these additions, our recipe has become "production quality", indeed it is very close to the actual recipe used by |app| for the *BBC*, shown below:
|
||||
|
||||
.. literalinclude:: ../web/feeds/recipes/bbc.py
|
||||
|
||||
This :term:`recipe` explores only the tip of the iceberg when it comes to the power of |app|. To explore more of the abilities of |app| we'll examine a more complex real life example in the next section.
|
||||
|
||||
Real life example
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A reasonably complex real life example that exposes more of the :term:`API` of ``BasicNewsRecipe`` is the :term:`recipe` for *The New York Times*
|
||||
|
||||
.. literalinclude:: ../web/feeds/recipes/nytimes.py
|
||||
:linenos:
|
||||
|
||||
|
||||
Tips for developing new recipes
|
||||
---------------------------------
|
||||
|
||||
The best way to develop new recipes is to use the command line interface. Create the recipe using your favorite python editor and save it to a file say :file:`myrecipe.py`. You can download content using this recipe with the command:
|
||||
|
||||
:command:`feeds2disk` :option:`--debug` :option:`--test` myrecipe.py
|
||||
|
||||
The :command:`feeds2disk` will download all the webpages and save them to the current directory. The :option:`--debug` makes feeds2disk spit out a lot of information about what it is doing. The :option:`--test` makes it download only a couple of articles from at most two feeds.
|
||||
|
||||
Once the download is complete, you can look at the downloaded :term:`HTML` by opening the file :file:`index.html` in a browser. Once you're satisfied that the download and preprocessing is happening correctly, you can generate an LRF ebook with the command
|
||||
|
||||
:command:`html2lrf` :option:`--use-spine` :option:`--page-break-before` "$" index.html
|
||||
|
||||
If the generated :term:`LRF` looks good, you can finally, run
|
||||
|
||||
:command:`feeds2lrf` myrecipe.py
|
||||
|
||||
to see the final :term:`LRF` format e-book generated from your recipe. If you're satisfied with your recipe, consider attaching it to `the wiki <http://libprs500.kovidgoyal.net/wiki/UserRecipes>`_, so that others can use it as well. If you feel there is enough demand to justify its inclusion into the set of built-in recipes, add a comment to the ticket http://libprs500.kovidgoyal.net/ticket/405
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`feeds2disk`
|
||||
The command line interfce for downloading content from the internet
|
||||
|
||||
:ref:`feeds2lrf`
|
||||
The command line interface for downloading content fro the internet and converting it into a :term:`LRF` e-book.
|
||||
|
||||
:ref:`html2lrf`
|
||||
The command line interface for converting :term:`HTML` into a :term:`LRF` e-book.
|
||||
|
||||
|
||||
Further reading
|
||||
--------------------
|
||||
|
||||
To learn more about writing advanced recipes using some of the facilities, available in ``BasicNewsRecipe`` you should consult the following sources:
|
||||
|
||||
:ref:`API Documentation <news_recipe>`
|
||||
Documentation of the ``BasicNewsRecipe`` class and all its important methods and fields.
|
||||
|
||||
`BasicNewsRecipe <http://libprs500.kovidgoyal.net/browser/trunk/src/libprs500/web/feeds/news.py>`_
|
||||
The source code of ``BasicNewsRecipe``
|
||||
|
||||
`Built-in recipes <http://libprs500.kovidgoyal.net/browser/trunk/src/libprs500/web/feeds/recipes>`_
|
||||
The source code for the built-in recipes that come with |app|
|
||||
|
||||
Migrating old style profiles to recipes
|
||||
----------------------------------------
|
||||
|
||||
In earlier versions of |app| there was a similar, if less powerful, framework for fetching news based on *Profiles*. If you have a profile that you would like to migrate to a recipe, the basic technique is simple, as they are very similar (on the surface). Common changes you have to make include:
|
||||
|
||||
* Replace ``DefaultProfile`` with ``BasicNewsRecipe``
|
||||
|
||||
* Remove ``max_recursions``
|
||||
|
||||
* If the server you're downloading from doesn't like multiple connects, set ``simultaneous_downloads = 1``.
|
||||
|
||||
API documentation
|
||||
--------------------
|
||||
|
||||
.. toctree::
|
||||
|
||||
news_recipe
|
||||
|
133
src/libprs500/manual/news_recipe.rst
Normal file
133
src/libprs500/manual/news_recipe.rst
Normal file
@ -0,0 +1,133 @@
|
||||
.. include:: global.rst
|
||||
|
||||
.. _news_recipe:
|
||||
|
||||
API Documentation for recipes
|
||||
===============================
|
||||
|
||||
.. module:: libprs500.web.feeds.news
|
||||
:synopsis: Defines various abstract base classes that can be subclassed to create powerful news fetching recipes.
|
||||
|
||||
Defines various abstract base classes that can be subclassed to create powerful news fetching recipes. The useful
|
||||
subclasses are:
|
||||
|
||||
.. contents::
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
BasicNewsRecipe
|
||||
-----------------
|
||||
|
||||
.. class:: BasicNewsRecipe
|
||||
|
||||
Abstract base class that contains a number of members and methods to customize the fetching of contents in your recipes. All
|
||||
recipes must inherit from this class or a subclass of it.
|
||||
|
||||
The members and methods are organized as follows:
|
||||
|
||||
.. contents::
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
|
||||
|
||||
Customizing e-book download
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automember:: BasicNewsRecipe.title
|
||||
|
||||
.. automember:: BasicNewsRecipe.__author__
|
||||
|
||||
.. automember:: BasicNewsRecipe.max_articles_per_feed
|
||||
|
||||
.. automember:: BasicNewsRecipe.oldest_article
|
||||
|
||||
.. automember:: BasicNewsRecipe.recursions
|
||||
|
||||
.. automember:: BasicNewsRecipe.delay
|
||||
|
||||
.. automember:: BasicNewsRecipe.simultaneous_downloads
|
||||
|
||||
.. automember:: BasicNewsRecipe.timeout
|
||||
|
||||
.. automember:: BasicNewsRecipe.timefmt
|
||||
|
||||
.. automember:: BasicNewsRecipe.feeds
|
||||
|
||||
.. automember:: BasicNewsRecipe.no_stylesheets
|
||||
|
||||
.. automember:: BasicNewsRecipe.encoding
|
||||
|
||||
.. automethod:: BasicNewsRecipe.get_browser
|
||||
|
||||
.. automethod:: BasicNewsRecipe.get_cover_url
|
||||
|
||||
.. automethod:: BasicNewsRecipe.get_feeds
|
||||
|
||||
.. automethod:: BasicNewsRecipe.parse_index
|
||||
|
||||
|
||||
|
||||
Customizing feed parsing
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automember:: BasicNewsRecipe.summary_length
|
||||
|
||||
.. automember:: BasicNewsRecipe.use_embedded_content
|
||||
|
||||
.. automethod:: BasicNewsRecipe.get_article_url
|
||||
|
||||
.. automethod:: BasicNewsRecipe.print_version
|
||||
|
||||
.. automethod:: BasicNewsRecipe.parse_feeds
|
||||
|
||||
|
||||
Pre/post processing of downloaded HTML
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automember:: BasicNewsRecipe.extra_css
|
||||
|
||||
.. automember:: BasicNewsRecipe.match_regexps
|
||||
|
||||
.. automember:: BasicNewsRecipe.filter_regexps
|
||||
|
||||
.. automember:: BasicNewsRecipe.remove_tags
|
||||
|
||||
.. automember:: BasicNewsRecipe.remove_tags_after
|
||||
|
||||
.. automember:: BasicNewsRecipe.remove_tags_before
|
||||
|
||||
.. automember:: BasicNewsRecipe.keep_only_tags
|
||||
|
||||
.. automember:: BasicNewsRecipe.preprocess_regexps
|
||||
|
||||
.. automethod:: BasicNewsRecipe.preprocess_html
|
||||
|
||||
.. automethod:: BasicNewsRecipe.postprocess_html
|
||||
|
||||
|
||||
|
||||
Convenience methods
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automethod:: BasicNewsRecipe.cleanup
|
||||
|
||||
.. automethod:: BasicNewsRecipe.index_to_soup
|
||||
|
||||
.. automethod:: BasicNewsRecipe.sort_index_by
|
||||
|
||||
.. automethod:: BasicNewsRecipe.tag_to_string
|
||||
|
||||
|
||||
CustomIndexRecipe
|
||||
---------------------
|
||||
|
||||
.. class:: CustomIndexRecipe
|
||||
|
||||
This class is useful for getting content from websites that don't follow the "multiple articles in several feeds" content model. For example, it is used in the built-in recipe for fetching the `Daily Dilbert` comic strip.
|
||||
|
||||
.. literalinclude:: ../web/feeds/recipes/dilbert.py
|
||||
|
||||
.. automethod:: CustomIndexRecipe.custom_index
|
||||
|
||||
|
970
src/libprs500/path.py
Normal file
970
src/libprs500/path.py
Normal file
@ -0,0 +1,970 @@
|
||||
""" path.py - An object representing a path to a file or directory.
|
||||
|
||||
Example:
|
||||
|
||||
from path import path
|
||||
d = path('/home/guido/bin')
|
||||
for f in d.files('*.py'):
|
||||
f.chmod(0755)
|
||||
|
||||
This module requires Python 2.2 or later.
|
||||
|
||||
|
||||
URL: http://www.jorendorff.com/articles/python/path
|
||||
Author: Jason Orendorff <jason.orendorff\x40gmail\x2ecom> (and others - see the url!)
|
||||
Date: 9 Mar 2007
|
||||
"""
|
||||
|
||||
|
||||
# TODO
|
||||
# - Tree-walking functions don't avoid symlink loops. Matt Harrison
|
||||
# sent me a patch for this.
|
||||
# - Bug in write_text(). It doesn't support Universal newline mode.
|
||||
# - Better error message in listdir() when self isn't a
|
||||
# directory. (On Windows, the error message really sucks.)
|
||||
# - Make sure everything has a good docstring.
|
||||
# - Add methods for regex find and replace.
|
||||
# - guess_content_type() method?
|
||||
# - Perhaps support arguments to touch().
|
||||
|
||||
from __future__ import generators
|
||||
|
||||
import sys, warnings, os, fnmatch, glob, shutil, codecs, md5
|
||||
|
||||
__version__ = '2.2'
|
||||
__all__ = ['path']
|
||||
|
||||
# Platform-specific support for path.owner
|
||||
if os.name == 'nt':
|
||||
try:
|
||||
import win32security
|
||||
except ImportError:
|
||||
win32security = None
|
||||
else:
|
||||
try:
|
||||
import pwd
|
||||
except ImportError:
|
||||
pwd = None
|
||||
|
||||
# Pre-2.3 support. Are unicode filenames supported?
|
||||
_base = str
|
||||
_getcwd = os.getcwd
|
||||
try:
|
||||
if os.path.supports_unicode_filenames:
|
||||
_base = unicode
|
||||
_getcwd = os.getcwdu
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Pre-2.3 workaround for booleans
|
||||
try:
|
||||
True, False
|
||||
except NameError:
|
||||
True, False = 1, 0
|
||||
|
||||
# Pre-2.3 workaround for basestring.
|
||||
try:
|
||||
basestring
|
||||
except NameError:
|
||||
basestring = (str, unicode)
|
||||
|
||||
# Universal newline support
|
||||
_textmode = 'r'
|
||||
if hasattr(file, 'newlines'):
|
||||
_textmode = 'U'
|
||||
|
||||
|
||||
class TreeWalkWarning(Warning):
|
||||
pass
|
||||
|
||||
class path(_base):
|
||||
""" Represents a filesystem path.
|
||||
|
||||
For documentation on individual methods, consult their
|
||||
counterparts in os.path.
|
||||
"""
|
||||
|
||||
# --- Special Python methods.
|
||||
|
||||
def __repr__(self):
|
||||
return 'path(%s)' % _base.__repr__(self)
|
||||
|
||||
# Adding a path and a string yields a path.
|
||||
def __add__(self, more):
|
||||
try:
|
||||
resultStr = _base.__add__(self, more)
|
||||
except TypeError: #Python bug
|
||||
resultStr = NotImplemented
|
||||
if resultStr is NotImplemented:
|
||||
return resultStr
|
||||
return self.__class__(resultStr)
|
||||
|
||||
def __radd__(self, other):
|
||||
if isinstance(other, basestring):
|
||||
return self.__class__(other.__add__(self))
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
# The / operator joins paths.
|
||||
def __div__(self, rel):
|
||||
""" fp.__div__(rel) == fp / rel == fp.joinpath(rel)
|
||||
|
||||
Join two path components, adding a separator character if
|
||||
needed.
|
||||
"""
|
||||
return self.__class__(os.path.join(self, rel))
|
||||
|
||||
# Make the / operator work even when true division is enabled.
|
||||
__truediv__ = __div__
|
||||
|
||||
def getcwd(cls):
|
||||
""" Return the current working directory as a path object. """
|
||||
return cls(_getcwd())
|
||||
getcwd = classmethod(getcwd)
|
||||
|
||||
|
||||
# --- Operations on path strings.
|
||||
|
||||
isabs = os.path.isabs
|
||||
def abspath(self): return self.__class__(os.path.abspath(self))
|
||||
def normcase(self): return self.__class__(os.path.normcase(self))
|
||||
def normpath(self): return self.__class__(os.path.normpath(self))
|
||||
def realpath(self): return self.__class__(os.path.realpath(self))
|
||||
def expanduser(self): return self.__class__(os.path.expanduser(self))
|
||||
def expandvars(self): return self.__class__(os.path.expandvars(self))
|
||||
def dirname(self): return self.__class__(os.path.dirname(self))
|
||||
basename = os.path.basename
|
||||
|
||||
def expand(self):
|
||||
""" Clean up a filename by calling expandvars(),
|
||||
expanduser(), and normpath() on it.
|
||||
|
||||
This is commonly everything needed to clean up a filename
|
||||
read from a configuration file, for example.
|
||||
"""
|
||||
return self.expandvars().expanduser().normpath()
|
||||
|
||||
def _get_namebase(self):
|
||||
base, ext = os.path.splitext(self.name)
|
||||
return base
|
||||
|
||||
def _get_ext(self):
|
||||
f, ext = os.path.splitext(_base(self))
|
||||
return ext
|
||||
|
||||
def _get_drive(self):
|
||||
drive, r = os.path.splitdrive(self)
|
||||
return self.__class__(drive)
|
||||
|
||||
parent = property(
|
||||
dirname, None, None,
|
||||
""" This path's parent directory, as a new path object.
|
||||
|
||||
For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib')
|
||||
""")
|
||||
|
||||
name = property(
|
||||
basename, None, None,
|
||||
""" The name of this file or directory without the full path.
|
||||
|
||||
For example, path('/usr/local/lib/libpython.so').name == 'libpython.so'
|
||||
""")
|
||||
|
||||
namebase = property(
|
||||
_get_namebase, None, None,
|
||||
""" The same as path.name, but with one file extension stripped off.
|
||||
|
||||
For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz',
|
||||
but path('/home/guido/python.tar.gz').namebase == 'python.tar'
|
||||
""")
|
||||
|
||||
ext = property(
|
||||
_get_ext, None, None,
|
||||
""" The file extension, for example '.py'. """)
|
||||
|
||||
drive = property(
|
||||
_get_drive, None, None,
|
||||
""" The drive specifier, for example 'C:'.
|
||||
This is always empty on systems that don't use drive specifiers.
|
||||
""")
|
||||
|
||||
def splitpath(self):
|
||||
""" p.splitpath() -> Return (p.parent, p.name). """
|
||||
parent, child = os.path.split(self)
|
||||
return self.__class__(parent), child
|
||||
|
||||
def splitdrive(self):
|
||||
""" p.splitdrive() -> Return (p.drive, <the rest of p>).
|
||||
|
||||
Split the drive specifier from this path. If there is
|
||||
no drive specifier, p.drive is empty, so the return value
|
||||
is simply (path(''), p). This is always the case on Unix.
|
||||
"""
|
||||
drive, rel = os.path.splitdrive(self)
|
||||
return self.__class__(drive), rel
|
||||
|
||||
def splitext(self):
|
||||
""" p.splitext() -> Return (p.stripext(), p.ext).
|
||||
|
||||
Split the filename extension from this path and return
|
||||
the two parts. Either part may be empty.
|
||||
|
||||
The extension is everything from '.' to the end of the
|
||||
last path segment. This has the property that if
|
||||
(a, b) == p.splitext(), then a + b == p.
|
||||
"""
|
||||
filename, ext = os.path.splitext(self)
|
||||
return self.__class__(filename), ext
|
||||
|
||||
def stripext(self):
|
||||
""" p.stripext() -> Remove one file extension from the path.
|
||||
|
||||
For example, path('/home/guido/python.tar.gz').stripext()
|
||||
returns path('/home/guido/python.tar').
|
||||
"""
|
||||
return self.splitext()[0]
|
||||
|
||||
if hasattr(os.path, 'splitunc'):
|
||||
def splitunc(self):
|
||||
unc, rest = os.path.splitunc(self)
|
||||
return self.__class__(unc), rest
|
||||
|
||||
def _get_uncshare(self):
|
||||
unc, r = os.path.splitunc(self)
|
||||
return self.__class__(unc)
|
||||
|
||||
uncshare = property(
|
||||
_get_uncshare, None, None,
|
||||
""" The UNC mount point for this path.
|
||||
This is empty for paths on local drives. """)
|
||||
|
||||
def joinpath(self, *args):
|
||||
""" Join two or more path components, adding a separator
|
||||
character (os.sep) if needed. Returns a new path
|
||||
object.
|
||||
"""
|
||||
return self.__class__(os.path.join(self, *args))
|
||||
|
||||
def splitall(self):
|
||||
r""" Return a list of the path components in this path.
|
||||
|
||||
The first item in the list will be a path. Its value will be
|
||||
either os.curdir, os.pardir, empty, or the root directory of
|
||||
this path (for example, '/' or 'C:\\'). The other items in
|
||||
the list will be strings.
|
||||
|
||||
path.path.joinpath(*result) will yield the original path.
|
||||
"""
|
||||
parts = []
|
||||
loc = self
|
||||
while loc != os.curdir and loc != os.pardir:
|
||||
prev = loc
|
||||
loc, child = prev.splitpath()
|
||||
if loc == prev:
|
||||
break
|
||||
parts.append(child)
|
||||
parts.append(loc)
|
||||
parts.reverse()
|
||||
return parts
|
||||
|
||||
def relpath(self):
|
||||
""" Return this path as a relative path,
|
||||
based from the current working directory.
|
||||
"""
|
||||
cwd = self.__class__(os.getcwd())
|
||||
return cwd.relpathto(self)
|
||||
|
||||
def relpathto(self, dest):
|
||||
""" Return a relative path from self to dest.
|
||||
|
||||
If there is no relative path from self to dest, for example if
|
||||
they reside on different drives in Windows, then this returns
|
||||
dest.abspath().
|
||||
"""
|
||||
origin = self.abspath()
|
||||
dest = self.__class__(dest).abspath()
|
||||
|
||||
orig_list = origin.normcase().splitall()
|
||||
# Don't normcase dest! We want to preserve the case.
|
||||
dest_list = dest.splitall()
|
||||
|
||||
if orig_list[0] != os.path.normcase(dest_list[0]):
|
||||
# Can't get here from there.
|
||||
return dest
|
||||
|
||||
# Find the location where the two paths start to differ.
|
||||
i = 0
|
||||
for start_seg, dest_seg in zip(orig_list, dest_list):
|
||||
if start_seg != os.path.normcase(dest_seg):
|
||||
break
|
||||
i += 1
|
||||
|
||||
# Now i is the point where the two paths diverge.
|
||||
# Need a certain number of "os.pardir"s to work up
|
||||
# from the origin to the point of divergence.
|
||||
segments = [os.pardir] * (len(orig_list) - i)
|
||||
# Need to add the diverging part of dest_list.
|
||||
segments += dest_list[i:]
|
||||
if len(segments) == 0:
|
||||
# If they happen to be identical, use os.curdir.
|
||||
relpath = os.curdir
|
||||
else:
|
||||
relpath = os.path.join(*segments)
|
||||
return self.__class__(relpath)
|
||||
|
||||
# --- Listing, searching, walking, and matching
|
||||
|
||||
def listdir(self, pattern=None):
|
||||
""" D.listdir() -> List of items in this directory.
|
||||
|
||||
Use D.files() or D.dirs() instead if you want a listing
|
||||
of just files or just subdirectories.
|
||||
|
||||
The elements of the list are path objects.
|
||||
|
||||
With the optional 'pattern' argument, this only lists
|
||||
items whose names match the given pattern.
|
||||
"""
|
||||
names = os.listdir(self)
|
||||
if pattern is not None:
|
||||
names = fnmatch.filter(names, pattern)
|
||||
return [self / child for child in names]
|
||||
|
||||
def dirs(self, pattern=None):
|
||||
""" D.dirs() -> List of this directory's subdirectories.
|
||||
|
||||
The elements of the list are path objects.
|
||||
This does not walk recursively into subdirectories
|
||||
(but see path.walkdirs).
|
||||
|
||||
With the optional 'pattern' argument, this only lists
|
||||
directories whose names match the given pattern. For
|
||||
example, d.dirs('build-*').
|
||||
"""
|
||||
return [p for p in self.listdir(pattern) if p.isdir()]
|
||||
|
||||
def files(self, pattern=None):
|
||||
""" D.files() -> List of the files in this directory.
|
||||
|
||||
The elements of the list are path objects.
|
||||
This does not walk into subdirectories (see path.walkfiles).
|
||||
|
||||
With the optional 'pattern' argument, this only lists files
|
||||
whose names match the given pattern. For example,
|
||||
d.files('*.pyc').
|
||||
"""
|
||||
|
||||
return [p for p in self.listdir(pattern) if p.isfile()]
|
||||
|
||||
def walk(self, pattern=None, errors='strict'):
|
||||
""" D.walk() -> iterator over files and subdirs, recursively.
|
||||
|
||||
The iterator yields path objects naming each child item of
|
||||
this directory and its descendants. This requires that
|
||||
D.isdir().
|
||||
|
||||
This performs a depth-first traversal of the directory tree.
|
||||
Each directory is returned just before all its children.
|
||||
|
||||
The errors= keyword argument controls behavior when an
|
||||
error occurs. The default is 'strict', which causes an
|
||||
exception. The other allowed values are 'warn', which
|
||||
reports the error via warnings.warn(), and 'ignore'.
|
||||
"""
|
||||
if errors not in ('strict', 'warn', 'ignore'):
|
||||
raise ValueError("invalid errors parameter")
|
||||
|
||||
try:
|
||||
childList = self.listdir()
|
||||
except Exception:
|
||||
if errors == 'ignore':
|
||||
return
|
||||
elif errors == 'warn':
|
||||
warnings.warn(
|
||||
"Unable to list directory '%s': %s"
|
||||
% (self, sys.exc_info()[1]),
|
||||
TreeWalkWarning)
|
||||
return
|
||||
else:
|
||||
raise
|
||||
|
||||
for child in childList:
|
||||
if pattern is None or child.fnmatch(pattern):
|
||||
yield child
|
||||
try:
|
||||
isdir = child.isdir()
|
||||
except Exception:
|
||||
if errors == 'ignore':
|
||||
isdir = False
|
||||
elif errors == 'warn':
|
||||
warnings.warn(
|
||||
"Unable to access '%s': %s"
|
||||
% (child, sys.exc_info()[1]),
|
||||
TreeWalkWarning)
|
||||
isdir = False
|
||||
else:
|
||||
raise
|
||||
|
||||
if isdir:
|
||||
for item in child.walk(pattern, errors):
|
||||
yield item
|
||||
|
||||
def walkdirs(self, pattern=None, errors='strict'):
|
||||
""" D.walkdirs() -> iterator over subdirs, recursively.
|
||||
|
||||
With the optional 'pattern' argument, this yields only
|
||||
directories whose names match the given pattern. For
|
||||
example, mydir.walkdirs('*test') yields only directories
|
||||
with names ending in 'test'.
|
||||
|
||||
The errors= keyword argument controls behavior when an
|
||||
error occurs. The default is 'strict', which causes an
|
||||
exception. The other allowed values are 'warn', which
|
||||
reports the error via warnings.warn(), and 'ignore'.
|
||||
"""
|
||||
if errors not in ('strict', 'warn', 'ignore'):
|
||||
raise ValueError("invalid errors parameter")
|
||||
|
||||
try:
|
||||
dirs = self.dirs()
|
||||
except Exception:
|
||||
if errors == 'ignore':
|
||||
return
|
||||
elif errors == 'warn':
|
||||
warnings.warn(
|
||||
"Unable to list directory '%s': %s"
|
||||
% (self, sys.exc_info()[1]),
|
||||
TreeWalkWarning)
|
||||
return
|
||||
else:
|
||||
raise
|
||||
|
||||
for child in dirs:
|
||||
if pattern is None or child.fnmatch(pattern):
|
||||
yield child
|
||||
for subsubdir in child.walkdirs(pattern, errors):
|
||||
yield subsubdir
|
||||
|
||||
def walkfiles(self, pattern=None, errors='strict'):
|
||||
""" D.walkfiles() -> iterator over files in D, recursively.
|
||||
|
||||
The optional argument, pattern, limits the results to files
|
||||
with names that match the pattern. For example,
|
||||
mydir.walkfiles('*.tmp') yields only files with the .tmp
|
||||
extension.
|
||||
"""
|
||||
if errors not in ('strict', 'warn', 'ignore'):
|
||||
raise ValueError("invalid errors parameter")
|
||||
|
||||
try:
|
||||
childList = self.listdir()
|
||||
except Exception:
|
||||
if errors == 'ignore':
|
||||
return
|
||||
elif errors == 'warn':
|
||||
warnings.warn(
|
||||
"Unable to list directory '%s': %s"
|
||||
% (self, sys.exc_info()[1]),
|
||||
TreeWalkWarning)
|
||||
return
|
||||
else:
|
||||
raise
|
||||
|
||||
for child in childList:
|
||||
try:
|
||||
isfile = child.isfile()
|
||||
isdir = not isfile and child.isdir()
|
||||
except:
|
||||
if errors == 'ignore':
|
||||
continue
|
||||
elif errors == 'warn':
|
||||
warnings.warn(
|
||||
"Unable to access '%s': %s"
|
||||
% (self, sys.exc_info()[1]),
|
||||
TreeWalkWarning)
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
|
||||
if isfile:
|
||||
if pattern is None or child.fnmatch(pattern):
|
||||
yield child
|
||||
elif isdir:
|
||||
for f in child.walkfiles(pattern, errors):
|
||||
yield f
|
||||
|
||||
def fnmatch(self, pattern):
|
||||
""" Return True if self.name matches the given pattern.
|
||||
|
||||
pattern - A filename pattern with wildcards,
|
||||
for example '*.py'.
|
||||
"""
|
||||
return fnmatch.fnmatch(self.name, pattern)
|
||||
|
||||
def glob(self, pattern):
|
||||
""" Return a list of path objects that match the pattern.
|
||||
|
||||
pattern - a path relative to this directory, with wildcards.
|
||||
|
||||
For example, path('/users').glob('*/bin/*') returns a list
|
||||
of all the files users have in their bin directories.
|
||||
"""
|
||||
cls = self.__class__
|
||||
return [cls(s) for s in glob.glob(_base(self / pattern))]
|
||||
|
||||
|
||||
# --- Reading or writing an entire file at once.
|
||||
|
||||
def open(self, mode='r'):
|
||||
""" Open this file. Return a file object. """
|
||||
return file(self, mode)
|
||||
|
||||
def bytes(self):
|
||||
""" Open this file, read all bytes, return them as a string. """
|
||||
f = self.open('rb')
|
||||
try:
|
||||
return f.read()
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def write_bytes(self, bytes, append=False):
|
||||
""" Open this file and write the given bytes to it.
|
||||
|
||||
Default behavior is to overwrite any existing file.
|
||||
Call p.write_bytes(bytes, append=True) to append instead.
|
||||
"""
|
||||
if append:
|
||||
mode = 'ab'
|
||||
else:
|
||||
mode = 'wb'
|
||||
f = self.open(mode)
|
||||
try:
|
||||
f.write(bytes)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def text(self, encoding=None, errors='strict'):
|
||||
r""" Open this file, read it in, return the content as a string.
|
||||
|
||||
This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
|
||||
are automatically translated to '\n'.
|
||||
|
||||
Optional arguments:
|
||||
|
||||
encoding - The Unicode encoding (or character set) of
|
||||
the file. If present, the content of the file is
|
||||
decoded and returned as a unicode object; otherwise
|
||||
it is returned as an 8-bit str.
|
||||
errors - How to handle Unicode errors; see help(str.decode)
|
||||
for the options. Default is 'strict'.
|
||||
"""
|
||||
if encoding is None:
|
||||
# 8-bit
|
||||
f = self.open(_textmode)
|
||||
try:
|
||||
return f.read()
|
||||
finally:
|
||||
f.close()
|
||||
else:
|
||||
# Unicode
|
||||
f = codecs.open(self, 'r', encoding, errors)
|
||||
# (Note - Can't use 'U' mode here, since codecs.open
|
||||
# doesn't support 'U' mode, even in Python 2.3.)
|
||||
try:
|
||||
t = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
return (t.replace(u'\r\n', u'\n')
|
||||
.replace(u'\r\x85', u'\n')
|
||||
.replace(u'\r', u'\n')
|
||||
.replace(u'\x85', u'\n')
|
||||
.replace(u'\u2028', u'\n'))
|
||||
|
||||
def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
|
||||
r""" Write the given text to this file.
|
||||
|
||||
The default behavior is to overwrite any existing file;
|
||||
to append instead, use the 'append=True' keyword argument.
|
||||
|
||||
There are two differences between path.write_text() and
|
||||
path.write_bytes(): newline handling and Unicode handling.
|
||||
See below.
|
||||
|
||||
Parameters:
|
||||
|
||||
- text - str/unicode - The text to be written.
|
||||
|
||||
- encoding - str - The Unicode encoding that will be used.
|
||||
This is ignored if 'text' isn't a Unicode string.
|
||||
|
||||
- errors - str - How to handle Unicode encoding errors.
|
||||
Default is 'strict'. See help(unicode.encode) for the
|
||||
options. This is ignored if 'text' isn't a Unicode
|
||||
string.
|
||||
|
||||
- linesep - keyword argument - str/unicode - The sequence of
|
||||
characters to be used to mark end-of-line. The default is
|
||||
os.linesep. You can also specify None; this means to
|
||||
leave all newlines as they are in 'text'.
|
||||
|
||||
- append - keyword argument - bool - Specifies what to do if
|
||||
the file already exists (True: append to the end of it;
|
||||
False: overwrite it.) The default is False.
|
||||
|
||||
|
||||
--- Newline handling.
|
||||
|
||||
write_text() converts all standard end-of-line sequences
|
||||
('\n', '\r', and '\r\n') to your platform's default end-of-line
|
||||
sequence (see os.linesep; on Windows, for example, the
|
||||
end-of-line marker is '\r\n').
|
||||
|
||||
If you don't like your platform's default, you can override it
|
||||
using the 'linesep=' keyword argument. If you specifically want
|
||||
write_text() to preserve the newlines as-is, use 'linesep=None'.
|
||||
|
||||
This applies to Unicode text the same as to 8-bit text, except
|
||||
there are three additional standard Unicode end-of-line sequences:
|
||||
u'\x85', u'\r\x85', and u'\u2028'.
|
||||
|
||||
(This is slightly different from when you open a file for
|
||||
writing with fopen(filename, "w") in C or file(filename, 'w')
|
||||
in Python.)
|
||||
|
||||
|
||||
--- Unicode
|
||||
|
||||
If 'text' isn't Unicode, then apart from newline handling, the
|
||||
bytes are written verbatim to the file. The 'encoding' and
|
||||
'errors' arguments are not used and must be omitted.
|
||||
|
||||
If 'text' is Unicode, it is first converted to bytes using the
|
||||
specified 'encoding' (or the default encoding if 'encoding'
|
||||
isn't specified). The 'errors' argument applies only to this
|
||||
conversion.
|
||||
|
||||
"""
|
||||
if isinstance(text, unicode):
|
||||
if linesep is not None:
|
||||
# Convert all standard end-of-line sequences to
|
||||
# ordinary newline characters.
|
||||
text = (text.replace(u'\r\n', u'\n')
|
||||
.replace(u'\r\x85', u'\n')
|
||||
.replace(u'\r', u'\n')
|
||||
.replace(u'\x85', u'\n')
|
||||
.replace(u'\u2028', u'\n'))
|
||||
text = text.replace(u'\n', linesep)
|
||||
if encoding is None:
|
||||
encoding = sys.getdefaultencoding()
|
||||
bytes = text.encode(encoding, errors)
|
||||
else:
|
||||
# It is an error to specify an encoding if 'text' is
|
||||
# an 8-bit string.
|
||||
assert encoding is None
|
||||
|
||||
if linesep is not None:
|
||||
text = (text.replace('\r\n', '\n')
|
||||
.replace('\r', '\n'))
|
||||
bytes = text.replace('\n', linesep)
|
||||
|
||||
self.write_bytes(bytes, append)
|
||||
|
||||
def lines(self, encoding=None, errors='strict', retain=True):
|
||||
r""" Open this file, read all lines, return them in a list.
|
||||
|
||||
Optional arguments:
|
||||
encoding - The Unicode encoding (or character set) of
|
||||
the file. The default is None, meaning the content
|
||||
of the file is read as 8-bit characters and returned
|
||||
as a list of (non-Unicode) str objects.
|
||||
errors - How to handle Unicode errors; see help(str.decode)
|
||||
for the options. Default is 'strict'
|
||||
retain - If true, retain newline characters; but all newline
|
||||
character combinations ('\r', '\n', '\r\n') are
|
||||
translated to '\n'. If false, newline characters are
|
||||
stripped off. Default is True.
|
||||
|
||||
This uses 'U' mode in Python 2.3 and later.
|
||||
"""
|
||||
if encoding is None and retain:
|
||||
f = self.open(_textmode)
|
||||
try:
|
||||
return f.readlines()
|
||||
finally:
|
||||
f.close()
|
||||
else:
|
||||
return self.text(encoding, errors).splitlines(retain)
|
||||
|
||||
def write_lines(self, lines, encoding=None, errors='strict',
|
||||
linesep=os.linesep, append=False):
|
||||
r""" Write the given lines of text to this file.
|
||||
|
||||
By default this overwrites any existing file at this path.
|
||||
|
||||
This puts a platform-specific newline sequence on every line.
|
||||
See 'linesep' below.
|
||||
|
||||
lines - A list of strings.
|
||||
|
||||
encoding - A Unicode encoding to use. This applies only if
|
||||
'lines' contains any Unicode strings.
|
||||
|
||||
errors - How to handle errors in Unicode encoding. This
|
||||
also applies only to Unicode strings.
|
||||
|
||||
linesep - The desired line-ending. This line-ending is
|
||||
applied to every line. If a line already has any
|
||||
standard line ending ('\r', '\n', '\r\n', u'\x85',
|
||||
u'\r\x85', u'\u2028'), that will be stripped off and
|
||||
this will be used instead. The default is os.linesep,
|
||||
which is platform-dependent ('\r\n' on Windows, '\n' on
|
||||
Unix, etc.) Specify None to write the lines as-is,
|
||||
like file.writelines().
|
||||
|
||||
Use the keyword argument append=True to append lines to the
|
||||
file. The default is to overwrite the file. Warning:
|
||||
When you use this with Unicode data, if the encoding of the
|
||||
existing data in the file is different from the encoding
|
||||
you specify with the encoding= parameter, the result is
|
||||
mixed-encoding data, which can really confuse someone trying
|
||||
to read the file later.
|
||||
"""
|
||||
if append:
|
||||
mode = 'ab'
|
||||
else:
|
||||
mode = 'wb'
|
||||
f = self.open(mode)
|
||||
try:
|
||||
for line in lines:
|
||||
isUnicode = isinstance(line, unicode)
|
||||
if linesep is not None:
|
||||
# Strip off any existing line-end and add the
|
||||
# specified linesep string.
|
||||
if isUnicode:
|
||||
if line[-2:] in (u'\r\n', u'\x0d\x85'):
|
||||
line = line[:-2]
|
||||
elif line[-1:] in (u'\r', u'\n',
|
||||
u'\x85', u'\u2028'):
|
||||
line = line[:-1]
|
||||
else:
|
||||
if line[-2:] == '\r\n':
|
||||
line = line[:-2]
|
||||
elif line[-1:] in ('\r', '\n'):
|
||||
line = line[:-1]
|
||||
line += linesep
|
||||
if isUnicode:
|
||||
if encoding is None:
|
||||
encoding = sys.getdefaultencoding()
|
||||
line = line.encode(encoding, errors)
|
||||
f.write(line)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def read_md5(self):
|
||||
""" Calculate the md5 hash for this file.
|
||||
|
||||
This reads through the entire file.
|
||||
"""
|
||||
f = self.open('rb')
|
||||
try:
|
||||
m = md5.new()
|
||||
while True:
|
||||
d = f.read(8192)
|
||||
if not d:
|
||||
break
|
||||
m.update(d)
|
||||
finally:
|
||||
f.close()
|
||||
return m.digest()
|
||||
|
||||
# --- Methods for querying the filesystem.
|
||||
|
||||
exists = os.path.exists
|
||||
isdir = os.path.isdir
|
||||
isfile = os.path.isfile
|
||||
islink = os.path.islink
|
||||
ismount = os.path.ismount
|
||||
|
||||
if hasattr(os.path, 'samefile'):
|
||||
samefile = os.path.samefile
|
||||
|
||||
getatime = os.path.getatime
|
||||
atime = property(
|
||||
getatime, None, None,
|
||||
""" Last access time of the file. """)
|
||||
|
||||
getmtime = os.path.getmtime
|
||||
mtime = property(
|
||||
getmtime, None, None,
|
||||
""" Last-modified time of the file. """)
|
||||
|
||||
if hasattr(os.path, 'getctime'):
|
||||
getctime = os.path.getctime
|
||||
ctime = property(
|
||||
getctime, None, None,
|
||||
""" Creation time of the file. """)
|
||||
|
||||
getsize = os.path.getsize
|
||||
size = property(
|
||||
getsize, None, None,
|
||||
""" Size of the file, in bytes. """)
|
||||
|
||||
if hasattr(os, 'access'):
|
||||
def access(self, mode):
|
||||
""" Return true if current user has access to this path.
|
||||
|
||||
mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
|
||||
"""
|
||||
return os.access(self, mode)
|
||||
|
||||
def stat(self):
|
||||
""" Perform a stat() system call on this path. """
|
||||
return os.stat(self)
|
||||
|
||||
def lstat(self):
|
||||
""" Like path.stat(), but do not follow symbolic links. """
|
||||
return os.lstat(self)
|
||||
|
||||
def get_owner(self):
|
||||
r""" Return the name of the owner of this file or directory.
|
||||
|
||||
This follows symbolic links.
|
||||
|
||||
On Windows, this returns a name of the form ur'DOMAIN\User Name'.
|
||||
On Windows, a group can own a file or directory.
|
||||
"""
|
||||
if os.name == 'nt':
|
||||
if win32security is None:
|
||||
raise Exception("path.owner requires win32all to be installed")
|
||||
desc = win32security.GetFileSecurity(
|
||||
self, win32security.OWNER_SECURITY_INFORMATION)
|
||||
sid = desc.GetSecurityDescriptorOwner()
|
||||
account, domain, typecode = win32security.LookupAccountSid(None, sid)
|
||||
return domain + u'\\' + account
|
||||
else:
|
||||
if pwd is None:
|
||||
raise NotImplementedError("path.owner is not implemented on this platform.")
|
||||
st = self.stat()
|
||||
return pwd.getpwuid(st.st_uid).pw_name
|
||||
|
||||
owner = property(
|
||||
get_owner, None, None,
|
||||
""" Name of the owner of this file or directory. """)
|
||||
|
||||
if hasattr(os, 'statvfs'):
|
||||
def statvfs(self):
|
||||
""" Perform a statvfs() system call on this path. """
|
||||
return os.statvfs(self)
|
||||
|
||||
if hasattr(os, 'pathconf'):
|
||||
def pathconf(self, name):
|
||||
return os.pathconf(self, name)
|
||||
|
||||
|
||||
# --- Modifying operations on files and directories
|
||||
|
||||
def utime(self, times):
|
||||
""" Set the access and modified times of this file. """
|
||||
os.utime(self, times)
|
||||
|
||||
def chmod(self, mode):
|
||||
os.chmod(self, mode)
|
||||
|
||||
if hasattr(os, 'chown'):
|
||||
def chown(self, uid, gid):
|
||||
os.chown(self, uid, gid)
|
||||
|
||||
def rename(self, new):
|
||||
os.rename(self, new)
|
||||
|
||||
def renames(self, new):
|
||||
os.renames(self, new)
|
||||
|
||||
|
||||
# --- Create/delete operations on directories
|
||||
|
||||
def mkdir(self, mode=0777):
|
||||
os.mkdir(self, mode)
|
||||
|
||||
def makedirs(self, mode=0777):
|
||||
os.makedirs(self, mode)
|
||||
|
||||
def rmdir(self):
|
||||
os.rmdir(self)
|
||||
|
||||
def removedirs(self):
|
||||
os.removedirs(self)
|
||||
|
||||
|
||||
# --- Modifying operations on files
|
||||
|
||||
def touch(self):
|
||||
""" Set the access/modified times of this file to the current time.
|
||||
Create the file if it does not exist.
|
||||
"""
|
||||
fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666)
|
||||
os.close(fd)
|
||||
os.utime(self, None)
|
||||
|
||||
def remove(self):
|
||||
os.remove(self)
|
||||
|
||||
def unlink(self):
|
||||
os.unlink(self)
|
||||
|
||||
|
||||
# --- Links
|
||||
|
||||
if hasattr(os, 'link'):
|
||||
def link(self, newpath):
|
||||
""" Create a hard link at 'newpath', pointing to this file. """
|
||||
os.link(self, newpath)
|
||||
|
||||
if hasattr(os, 'symlink'):
|
||||
def symlink(self, newlink):
|
||||
""" Create a symbolic link at 'newlink', pointing here. """
|
||||
os.symlink(self, newlink)
|
||||
|
||||
if hasattr(os, 'readlink'):
|
||||
def readlink(self):
|
||||
""" Return the path to which this symbolic link points.
|
||||
|
||||
The result may be an absolute or a relative path.
|
||||
"""
|
||||
return self.__class__(os.readlink(self))
|
||||
|
||||
def readlinkabs(self):
|
||||
""" Return the path to which this symbolic link points.
|
||||
|
||||
The result is always an absolute path.
|
||||
"""
|
||||
p = self.readlink()
|
||||
if p.isabs():
|
||||
return p
|
||||
else:
|
||||
return (self.parent / p).abspath()
|
||||
|
||||
|
||||
# --- High-level functions from shutil
|
||||
|
||||
copyfile = shutil.copyfile
|
||||
copymode = shutil.copymode
|
||||
copystat = shutil.copystat
|
||||
copy = shutil.copy
|
||||
copy2 = shutil.copy2
|
||||
copytree = shutil.copytree
|
||||
if hasattr(shutil, 'move'):
|
||||
move = shutil.move
|
||||
rmtree = shutil.rmtree
|
||||
|
||||
|
||||
# --- Special stuff from os
|
||||
|
||||
if hasattr(os, 'chroot'):
|
||||
def chroot(self):
|
||||
os.chroot(self)
|
||||
|
||||
if hasattr(os, 'startfile'):
|
||||
def startfile(self):
|
||||
os.startfile(self)
|
||||
|
@ -10,7 +10,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ca\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2008-03-23 16:44+PDT\n"
|
||||
"POT-Creation-Date: 2008-03-24 15:10+PDT\n"
|
||||
"PO-Revision-Date: 2007-11-16 09:07+0100\n"
|
||||
"Last-Translator: libprs500\n"
|
||||
"Language-Team: \n"
|
||||
@ -452,7 +452,7 @@ msgstr "Cerca la nova ubicació de la base de dades"
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:146
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:149
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:153
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:256
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:185
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:186
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:187
|
||||
@ -1214,27 +1214,76 @@ msgstr ""
|
||||
msgid "Add tag to available tags and apply it to current book"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Add custom news source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
msgid "Available user profiles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
msgid "Add/Update &profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Remove profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:60
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:70
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:223
|
||||
msgid "Switch to Advanced mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:65
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:73
|
||||
msgid "Switch to Basic mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:83
|
||||
msgid "Feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:84
|
||||
msgid "The feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:88
|
||||
msgid "Feed must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:89
|
||||
msgid "The feed %s must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:94
|
||||
msgid "Already exists"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:95
|
||||
msgid "This feed has already been added to the recipe"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:136
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:145
|
||||
msgid "Invalid input"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:137
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:146
|
||||
msgid "<p>Could not create recipe. Error:<br>%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:152
|
||||
msgid "Replace recipe?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:153
|
||||
msgid "A custom recipe named %s already exists. Do you want to replace it?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:219
|
||||
msgid "Add custom news source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:220
|
||||
msgid "Available user profiles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:221
|
||||
msgid "Add/Update &profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:222
|
||||
msgid "&Remove profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:224
|
||||
msgid ""
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css"
|
||||
"\">\n"
|
||||
@ -1243,87 +1292,67 @@ msgid ""
|
||||
"font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Create a basic news "
|
||||
"profile, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.<br /"
|
||||
">The Basic tab is useful mainly for feeds that have the full article content "
|
||||
"embedded within them.</p></body></html>"
|
||||
"recipe, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.</p></"
|
||||
"body></html>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:244
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:228
|
||||
msgid "Profile &title:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:245
|
||||
msgid "&Summary length:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:246
|
||||
msgid " characters"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:247
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:229
|
||||
msgid "&Oldest article:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:248
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:230
|
||||
msgid "The oldest article to download"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:249
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:231
|
||||
msgid " days"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:250
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:232
|
||||
msgid "&Max. number of articles per feed:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:251
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:233
|
||||
msgid "Maximum number of articles to download per feed."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:252
|
||||
msgid ""
|
||||
"Try to follow links in the RSS feed to full articles on the web. If you "
|
||||
"enable this option, you're probably going to end up having to use the "
|
||||
"advanced mode."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:253
|
||||
msgid "Try to download &full articles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:254
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:234
|
||||
msgid "Feeds in profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:255
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Remove feed from profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:257
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:260
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
msgid "Add feed to profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:258
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Feed title:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:259
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
msgid "Feed &URL:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:261
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:241
|
||||
msgid "&Add feed"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:262
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:242
|
||||
msgid ""
|
||||
"For help with writing advanced news profiles, please visit <a href=\"https://"
|
||||
"libprs500.kovidgoyal.net/wiki/UserProfiles\">UserProfiles</a>"
|
||||
"For help with writing advanced news recipes, please visit <a href=\"http://"
|
||||
"libprs500.kovidgoyal.net/user_manual/news.html\">User Recipes</a>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:263
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:243
|
||||
msgid "Profile source code (python)"
|
||||
msgstr ""
|
||||
|
||||
@ -1945,7 +1974,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:91
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:95
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:512
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:511
|
||||
msgid "Fetching feeds..."
|
||||
msgstr ""
|
||||
|
||||
@ -1953,79 +1982,79 @@ msgstr ""
|
||||
msgid "Unknown News Source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:413
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:412
|
||||
msgid "Download finished"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:415
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:414
|
||||
msgid "Failed to download the following articles:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:417
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:423
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:416
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:422
|
||||
msgid " from "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:421
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:420
|
||||
msgid "Failed to download parts of the following articles:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:425
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:424
|
||||
msgid "\tFailed links:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:494
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:493
|
||||
msgid "Could not fetch article. Run with --debug to see the reason"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:516
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:515
|
||||
msgid "Got feeds from index page"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:520
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:519
|
||||
msgid "Trying to download cover..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:570
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:569
|
||||
msgid "Starting download [%d thread(s)]..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:584
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:583
|
||||
msgid "Feeds downloaded to %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:593
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:592
|
||||
msgid "Could not download cover: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:598
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:597
|
||||
msgid "Downloading cover from %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:633
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:632
|
||||
msgid "Untitled Article"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:674
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:673
|
||||
msgid ""
|
||||
"\n"
|
||||
"Downloaded article %s from %s\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:680
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:679
|
||||
msgid "Article downloaded: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:686
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:685
|
||||
msgid "Failed to download article: %s from %s\n"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:691
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:690
|
||||
msgid "Article download failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:707
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:706
|
||||
msgid "Fetching feed"
|
||||
msgstr ""
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -5,7 +5,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: libprs500 0.4.17\n"
|
||||
"POT-Creation-Date: 2008-03-23 16:46+PDT\n"
|
||||
"POT-Creation-Date: 2008-03-24 15:10+PDT\n"
|
||||
"PO-Revision-Date: 2008-03-21 11:00+0100\n"
|
||||
"Last-Translator: S. Dorscht <stdoonline@googlemail.com>\n"
|
||||
"Language-Team: de\n"
|
||||
@ -464,7 +464,7 @@ msgstr "Zu einem neuen Ort der Datenbank wechseln"
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:146
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:149
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:153
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:256
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:185
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:186
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:187
|
||||
@ -1263,27 +1263,76 @@ msgstr ""
|
||||
"Etikett zu den verfügbaren Etiketten hinzufügen und dem aktuellen Buch "
|
||||
"zuweisen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Add custom news source"
|
||||
msgstr "Eigene Nachrichtenquelle hinzufügen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
msgid "Available user profiles"
|
||||
msgstr "Verfügbare Benutzerprofile"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
msgid "Add/Update &profile"
|
||||
msgstr "&Profil hinzufügen/aktualisieren"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Remove profile"
|
||||
msgstr "Profil entfe&rnen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:60
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:70
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:223
|
||||
msgid "Switch to Advanced mode"
|
||||
msgstr "In erweiterten Modus umschalten"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:65
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:73
|
||||
msgid "Switch to Basic mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:83
|
||||
msgid "Feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:84
|
||||
msgid "The feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:88
|
||||
msgid "Feed must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:89
|
||||
msgid "The feed %s must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:94
|
||||
msgid "Already exists"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:95
|
||||
msgid "This feed has already been added to the recipe"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:136
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:145
|
||||
msgid "Invalid input"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:137
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:146
|
||||
msgid "<p>Could not create recipe. Error:<br>%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:152
|
||||
msgid "Replace recipe?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:153
|
||||
msgid "A custom recipe named %s already exists. Do you want to replace it?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:219
|
||||
msgid "Add custom news source"
|
||||
msgstr "Eigene Nachrichtenquelle hinzufügen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:220
|
||||
msgid "Available user profiles"
|
||||
msgstr "Verfügbare Benutzerprofile"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:221
|
||||
msgid "Add/Update &profile"
|
||||
msgstr "&Profil hinzufügen/aktualisieren"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:222
|
||||
msgid "&Remove profile"
|
||||
msgstr "Profil entfe&rnen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:224
|
||||
msgid ""
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css"
|
||||
"\">\n"
|
||||
@ -1292,105 +1341,67 @@ msgid ""
|
||||
"font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Create a basic news "
|
||||
"profile, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.<br /"
|
||||
">The Basic tab is useful mainly for feeds that have the full article content "
|
||||
"embedded within them.</p></body></html>"
|
||||
"recipe, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.</p></"
|
||||
"body></html>"
|
||||
msgstr ""
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css"
|
||||
"\">\n"
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:'DejaVu Sans'; font-size:10pt; "
|
||||
"font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Erstellen Sie ein "
|
||||
"Nachrichten-Grundprofil, indem Sie RSS Feeds hinzufügen. <br />Für die "
|
||||
"meisten Feeds müssen Sie die \"Erweitert\" Einstellung verwenden, um den "
|
||||
"Abruf weiter anzupassen.<br />Die Einstellung \"Einfach\" ist für Feeds "
|
||||
"ausreichend, die den vollständigen Nachrichten-Inhalt im Feed schon "
|
||||
"eingebettet haben.</p></body></html>"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:244
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:228
|
||||
msgid "Profile &title:"
|
||||
msgstr "Profil&titel:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:245
|
||||
msgid "&Summary length:"
|
||||
msgstr "Länge der Zu&sammenfassung:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:246
|
||||
msgid " characters"
|
||||
msgstr " Zeichen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:247
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:229
|
||||
msgid "&Oldest article:"
|
||||
msgstr "Ä<ester Artikel:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:248
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:230
|
||||
msgid "The oldest article to download"
|
||||
msgstr "Ältester Artikel, der geladen wird"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:249
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:231
|
||||
msgid " days"
|
||||
msgstr " Tage"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:250
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:232
|
||||
msgid "&Max. number of articles per feed:"
|
||||
msgstr "&Maximale Anzahl der Artikel pro feed:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:251
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:233
|
||||
msgid "Maximum number of articles to download per feed."
|
||||
msgstr "Maximale Anzahl der zu ladenden Artikel pro feed."
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:252
|
||||
msgid ""
|
||||
"Try to follow links in the RSS feed to full articles on the web. If you "
|
||||
"enable this option, you're probably going to end up having to use the "
|
||||
"advanced mode."
|
||||
msgstr ""
|
||||
"Verknüpfungen im RSS Feed bis zu den vollständigen Artikeln im Netz "
|
||||
"verfolgen. Falls Sie diese Option wählen, müssen Sie in den meisten Fällen "
|
||||
"den erweiterten Modus zur Konfiguration benutzen."
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:253
|
||||
msgid "Try to download &full articles"
|
||||
msgstr "Versuche &vollständige Artikel zu laden"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:254
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:234
|
||||
msgid "Feeds in profile"
|
||||
msgstr "Feeds im Profil"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:255
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Remove feed from profile"
|
||||
msgstr "Feeds aus dem Profil entfernen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:257
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:260
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
msgid "Add feed to profile"
|
||||
msgstr "Neuen Feed zum Profil hinzufügen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:258
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Feed title:"
|
||||
msgstr "&Feed Titel:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:259
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
msgid "Feed &URL:"
|
||||
msgstr "Feed &URL:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:261
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:241
|
||||
msgid "&Add feed"
|
||||
msgstr "Feed &anfügen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:262
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:242
|
||||
msgid ""
|
||||
"For help with writing advanced news profiles, please visit <a href=\"https://"
|
||||
"libprs500.kovidgoyal.net/wiki/UserProfiles\">UserProfiles</a>"
|
||||
"For help with writing advanced news recipes, please visit <a href=\"http://"
|
||||
"libprs500.kovidgoyal.net/user_manual/news.html\">User Recipes</a>"
|
||||
msgstr ""
|
||||
"Benötigen Sie Hilfe beim Erstellen von weiteren Nachrichten-Profilen? "
|
||||
"Schauen Sie hier vorbei: <a href=\"https://libprs500.kovidgoyal.net/wiki/"
|
||||
"UserProfiles\">UserProfiles</a>"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:263
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:243
|
||||
msgid "Profile source code (python)"
|
||||
msgstr "Profil-Quellcode (Python)"
|
||||
|
||||
@ -2062,7 +2073,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:91
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:95
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:512
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:511
|
||||
msgid "Fetching feeds..."
|
||||
msgstr "Rufe Feeds ab..."
|
||||
|
||||
@ -2070,62 +2081,62 @@ msgstr "Rufe Feeds ab..."
|
||||
msgid "Unknown News Source"
|
||||
msgstr "Nachrichtenquelle unbekannt"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:413
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:412
|
||||
msgid "Download finished"
|
||||
msgstr "Download beendet"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:415
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:414
|
||||
msgid "Failed to download the following articles:"
|
||||
msgstr "Der Download der folgenden Artikel schlug fehl:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:417
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:423
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:416
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:422
|
||||
msgid " from "
|
||||
msgstr " von"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:421
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:420
|
||||
msgid "Failed to download parts of the following articles:"
|
||||
msgstr "Der Download von Teilen der folgenden Artikel schlug fehl:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:425
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:424
|
||||
msgid "\tFailed links:"
|
||||
msgstr "\tFehlgeschlagene Verknüpfungen:"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:494
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:493
|
||||
msgid "Could not fetch article. Run with --debug to see the reason"
|
||||
msgstr ""
|
||||
"Konnte Artikel nicht abrufen. Der erneute Start mit --debug zeigt mögliche "
|
||||
"Gründe an "
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:516
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:515
|
||||
msgid "Got feeds from index page"
|
||||
msgstr "Feeds der Index Seite erhalten"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:520
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:519
|
||||
msgid "Trying to download cover..."
|
||||
msgstr "Versuche Umschlagbild zu laden..."
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:570
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:569
|
||||
msgid "Starting download [%d thread(s)]..."
|
||||
msgstr "Starte Download von [%d Thread(s)]..."
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:584
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:583
|
||||
msgid "Feeds downloaded to %s"
|
||||
msgstr "Feeds wurden nach %s heruntergeladen"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:593
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:592
|
||||
msgid "Could not download cover: %s"
|
||||
msgstr "Konnte Umschlagbild nicht laden: %s"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:598
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:597
|
||||
msgid "Downloading cover from %s"
|
||||
msgstr "Lade Umschlagbild von %s"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:633
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:632
|
||||
msgid "Untitled Article"
|
||||
msgstr "Artikel ohne Titel"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:674
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:673
|
||||
msgid ""
|
||||
"\n"
|
||||
"Downloaded article %s from %s\n"
|
||||
@ -2135,22 +2146,74 @@ msgstr ""
|
||||
"Artikel %s von %s geladen\n"
|
||||
"%s"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:680
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:679
|
||||
msgid "Article downloaded: %s"
|
||||
msgstr "Artikel geladen: %s"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:686
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:685
|
||||
msgid "Failed to download article: %s from %s\n"
|
||||
msgstr "Laden der Artikel fehlgeschlagen: %s von %s\n"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:691
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:690
|
||||
msgid "Article download failed: %s"
|
||||
msgstr "Laden der Artikel schlug fehl: %s"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:707
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:706
|
||||
msgid "Fetching feed"
|
||||
msgstr "Rufe Feed ab"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/"
|
||||
#~ "css\">\n"
|
||||
#~ "p, li { white-space: pre-wrap; }\n"
|
||||
#~ "</style></head><body style=\" font-family:'DejaVu Sans'; font-size:10pt; "
|
||||
#~ "font-weight:400; font-style:normal;\">\n"
|
||||
#~ "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
#~ "right:0px; -qt-block-indent:0; text-indent:0px;\">Create a basic news "
|
||||
#~ "profile, by adding RSS feeds to it. <br />For most feeds, you will have "
|
||||
#~ "to use the \"Advanced\" setting to further customize the fetch process."
|
||||
#~ "<br />The Basic tab is useful mainly for feeds that have the full article "
|
||||
#~ "content embedded within them.</p></body></html>"
|
||||
#~ msgstr ""
|
||||
#~ "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/"
|
||||
#~ "css\">\n"
|
||||
#~ "p, li { white-space: pre-wrap; }\n"
|
||||
#~ "</style></head><body style=\" font-family:'DejaVu Sans'; font-size:10pt; "
|
||||
#~ "font-weight:400; font-style:normal;\">\n"
|
||||
#~ "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
#~ "right:0px; -qt-block-indent:0; text-indent:0px;\">Erstellen Sie ein "
|
||||
#~ "Nachrichten-Grundprofil, indem Sie RSS Feeds hinzufügen. <br />Für die "
|
||||
#~ "meisten Feeds müssen Sie die \"Erweitert\" Einstellung verwenden, um den "
|
||||
#~ "Abruf weiter anzupassen.<br />Die Einstellung \"Einfach\" ist für Feeds "
|
||||
#~ "ausreichend, die den vollständigen Nachrichten-Inhalt im Feed schon "
|
||||
#~ "eingebettet haben.</p></body></html>"
|
||||
|
||||
#~ msgid "&Summary length:"
|
||||
#~ msgstr "Länge der Zu&sammenfassung:"
|
||||
|
||||
#~ msgid " characters"
|
||||
#~ msgstr " Zeichen"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Try to follow links in the RSS feed to full articles on the web. If you "
|
||||
#~ "enable this option, you're probably going to end up having to use the "
|
||||
#~ "advanced mode."
|
||||
#~ msgstr ""
|
||||
#~ "Verknüpfungen im RSS Feed bis zu den vollständigen Artikeln im Netz "
|
||||
#~ "verfolgen. Falls Sie diese Option wählen, müssen Sie in den meisten "
|
||||
#~ "Fällen den erweiterten Modus zur Konfiguration benutzen."
|
||||
|
||||
#~ msgid "Try to download &full articles"
|
||||
#~ msgstr "Versuche &vollständige Artikel zu laden"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "For help with writing advanced news profiles, please visit <a href="
|
||||
#~ "\"https://libprs500.kovidgoyal.net/wiki/UserProfiles\">UserProfiles</a>"
|
||||
#~ msgstr ""
|
||||
#~ "Benötigen Sie Hilfe beim Erstellen von weiteren Nachrichten-Profilen? "
|
||||
#~ "Schauen Sie hier vorbei: <a href=\"https://libprs500.kovidgoyal.net/wiki/"
|
||||
#~ "UserProfiles\">UserProfiles</a>"
|
||||
|
||||
#~ msgid "Add custom RSS feed"
|
||||
#~ msgstr "Individuellen RSS feed hinzufügen"
|
||||
|
||||
|
@ -10,7 +10,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: es\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2008-03-23 16:44+PDT\n"
|
||||
"POT-Creation-Date: 2008-03-24 15:10+PDT\n"
|
||||
"PO-Revision-Date: 2007-11-16 09:21+0100\n"
|
||||
"Last-Translator: libprs500\n"
|
||||
"Language-Team: Spanish\n"
|
||||
@ -453,7 +453,7 @@ msgstr "Navegar a la nueva ubicación de la base de datos"
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:146
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:149
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:153
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:256
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:185
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:186
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:187
|
||||
@ -1216,27 +1216,76 @@ msgstr ""
|
||||
msgid "Add tag to available tags and apply it to current book"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Add custom news source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
msgid "Available user profiles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
msgid "Add/Update &profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Remove profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:60
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:70
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:223
|
||||
msgid "Switch to Advanced mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:65
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:73
|
||||
msgid "Switch to Basic mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:83
|
||||
msgid "Feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:84
|
||||
msgid "The feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:88
|
||||
msgid "Feed must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:89
|
||||
msgid "The feed %s must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:94
|
||||
msgid "Already exists"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:95
|
||||
msgid "This feed has already been added to the recipe"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:136
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:145
|
||||
msgid "Invalid input"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:137
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:146
|
||||
msgid "<p>Could not create recipe. Error:<br>%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:152
|
||||
msgid "Replace recipe?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:153
|
||||
msgid "A custom recipe named %s already exists. Do you want to replace it?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:219
|
||||
msgid "Add custom news source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:220
|
||||
msgid "Available user profiles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:221
|
||||
msgid "Add/Update &profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:222
|
||||
msgid "&Remove profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:224
|
||||
msgid ""
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css"
|
||||
"\">\n"
|
||||
@ -1245,87 +1294,67 @@ msgid ""
|
||||
"font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Create a basic news "
|
||||
"profile, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.<br /"
|
||||
">The Basic tab is useful mainly for feeds that have the full article content "
|
||||
"embedded within them.</p></body></html>"
|
||||
"recipe, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.</p></"
|
||||
"body></html>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:244
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:228
|
||||
msgid "Profile &title:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:245
|
||||
msgid "&Summary length:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:246
|
||||
msgid " characters"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:247
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:229
|
||||
msgid "&Oldest article:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:248
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:230
|
||||
msgid "The oldest article to download"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:249
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:231
|
||||
msgid " days"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:250
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:232
|
||||
msgid "&Max. number of articles per feed:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:251
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:233
|
||||
msgid "Maximum number of articles to download per feed."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:252
|
||||
msgid ""
|
||||
"Try to follow links in the RSS feed to full articles on the web. If you "
|
||||
"enable this option, you're probably going to end up having to use the "
|
||||
"advanced mode."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:253
|
||||
msgid "Try to download &full articles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:254
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:234
|
||||
msgid "Feeds in profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:255
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Remove feed from profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:257
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:260
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
msgid "Add feed to profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:258
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Feed title:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:259
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
msgid "Feed &URL:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:261
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:241
|
||||
msgid "&Add feed"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:262
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:242
|
||||
msgid ""
|
||||
"For help with writing advanced news profiles, please visit <a href=\"https://"
|
||||
"libprs500.kovidgoyal.net/wiki/UserProfiles\">UserProfiles</a>"
|
||||
"For help with writing advanced news recipes, please visit <a href=\"http://"
|
||||
"libprs500.kovidgoyal.net/user_manual/news.html\">User Recipes</a>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:263
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:243
|
||||
msgid "Profile source code (python)"
|
||||
msgstr ""
|
||||
|
||||
@ -1948,7 +1977,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:91
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:95
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:512
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:511
|
||||
msgid "Fetching feeds..."
|
||||
msgstr ""
|
||||
|
||||
@ -1956,79 +1985,79 @@ msgstr ""
|
||||
msgid "Unknown News Source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:413
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:412
|
||||
msgid "Download finished"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:415
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:414
|
||||
msgid "Failed to download the following articles:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:417
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:423
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:416
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:422
|
||||
msgid " from "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:421
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:420
|
||||
msgid "Failed to download parts of the following articles:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:425
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:424
|
||||
msgid "\tFailed links:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:494
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:493
|
||||
msgid "Could not fetch article. Run with --debug to see the reason"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:516
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:515
|
||||
msgid "Got feeds from index page"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:520
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:519
|
||||
msgid "Trying to download cover..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:570
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:569
|
||||
msgid "Starting download [%d thread(s)]..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:584
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:583
|
||||
msgid "Feeds downloaded to %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:593
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:592
|
||||
msgid "Could not download cover: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:598
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:597
|
||||
msgid "Downloading cover from %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:633
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:632
|
||||
msgid "Untitled Article"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:674
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:673
|
||||
msgid ""
|
||||
"\n"
|
||||
"Downloaded article %s from %s\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:680
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:679
|
||||
msgid "Article downloaded: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:686
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:685
|
||||
msgid "Failed to download article: %s from %s\n"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:691
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:690
|
||||
msgid "Article download failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:707
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:706
|
||||
msgid "Fetching feed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: libprs500 0.4.22\n"
|
||||
"POT-Creation-Date: 2008-03-23 16:44+PDT\n"
|
||||
"POT-Creation-Date: 2008-03-24 15:10+PDT\n"
|
||||
"PO-Revision-Date: 2008-01-20 09:59+0100\n"
|
||||
"Last-Translator: FixB <fix.bornes@free.fr>\n"
|
||||
"Language-Team: fr\n"
|
||||
@ -455,7 +455,7 @@ msgstr "Choisir un nouvel emplacement pour la base de données"
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:146
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:149
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:153
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:256
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:185
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:186
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:187
|
||||
@ -1231,27 +1231,76 @@ msgid "Add tag to available tags and apply it to current book"
|
||||
msgstr ""
|
||||
"Ajoute le mot-clef à la liste des mots-clefs et l'applique au livre en cours"
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Add custom news source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
msgid "Available user profiles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
msgid "Add/Update &profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Remove profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:60
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:70
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:223
|
||||
msgid "Switch to Advanced mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:65
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:73
|
||||
msgid "Switch to Basic mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:83
|
||||
msgid "Feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:84
|
||||
msgid "The feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:88
|
||||
msgid "Feed must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:89
|
||||
msgid "The feed %s must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:94
|
||||
msgid "Already exists"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:95
|
||||
msgid "This feed has already been added to the recipe"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:136
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:145
|
||||
msgid "Invalid input"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:137
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:146
|
||||
msgid "<p>Could not create recipe. Error:<br>%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:152
|
||||
msgid "Replace recipe?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:153
|
||||
msgid "A custom recipe named %s already exists. Do you want to replace it?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:219
|
||||
msgid "Add custom news source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:220
|
||||
msgid "Available user profiles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:221
|
||||
msgid "Add/Update &profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:222
|
||||
msgid "&Remove profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:224
|
||||
msgid ""
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css"
|
||||
"\">\n"
|
||||
@ -1260,87 +1309,67 @@ msgid ""
|
||||
"font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Create a basic news "
|
||||
"profile, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.<br /"
|
||||
">The Basic tab is useful mainly for feeds that have the full article content "
|
||||
"embedded within them.</p></body></html>"
|
||||
"recipe, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.</p></"
|
||||
"body></html>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:244
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:228
|
||||
msgid "Profile &title:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:245
|
||||
msgid "&Summary length:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:246
|
||||
msgid " characters"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:247
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:229
|
||||
msgid "&Oldest article:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:248
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:230
|
||||
msgid "The oldest article to download"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:249
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:231
|
||||
msgid " days"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:250
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:232
|
||||
msgid "&Max. number of articles per feed:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:251
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:233
|
||||
msgid "Maximum number of articles to download per feed."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:252
|
||||
msgid ""
|
||||
"Try to follow links in the RSS feed to full articles on the web. If you "
|
||||
"enable this option, you're probably going to end up having to use the "
|
||||
"advanced mode."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:253
|
||||
msgid "Try to download &full articles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:254
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:234
|
||||
msgid "Feeds in profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:255
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Remove feed from profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:257
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:260
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
msgid "Add feed to profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:258
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Feed title:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:259
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
msgid "Feed &URL:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:261
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:241
|
||||
msgid "&Add feed"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:262
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:242
|
||||
msgid ""
|
||||
"For help with writing advanced news profiles, please visit <a href=\"https://"
|
||||
"libprs500.kovidgoyal.net/wiki/UserProfiles\">UserProfiles</a>"
|
||||
"For help with writing advanced news recipes, please visit <a href=\"http://"
|
||||
"libprs500.kovidgoyal.net/user_manual/news.html\">User Recipes</a>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:263
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:243
|
||||
msgid "Profile source code (python)"
|
||||
msgstr ""
|
||||
|
||||
@ -1971,7 +2000,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:91
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:95
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:512
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:511
|
||||
msgid "Fetching feeds..."
|
||||
msgstr ""
|
||||
|
||||
@ -1979,79 +2008,79 @@ msgstr ""
|
||||
msgid "Unknown News Source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:413
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:412
|
||||
msgid "Download finished"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:415
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:414
|
||||
msgid "Failed to download the following articles:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:417
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:423
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:416
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:422
|
||||
msgid " from "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:421
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:420
|
||||
msgid "Failed to download parts of the following articles:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:425
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:424
|
||||
msgid "\tFailed links:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:494
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:493
|
||||
msgid "Could not fetch article. Run with --debug to see the reason"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:516
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:515
|
||||
msgid "Got feeds from index page"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:520
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:519
|
||||
msgid "Trying to download cover..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:570
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:569
|
||||
msgid "Starting download [%d thread(s)]..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:584
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:583
|
||||
msgid "Feeds downloaded to %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:593
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:592
|
||||
msgid "Could not download cover: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:598
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:597
|
||||
msgid "Downloading cover from %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:633
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:632
|
||||
msgid "Untitled Article"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:674
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:673
|
||||
msgid ""
|
||||
"\n"
|
||||
"Downloaded article %s from %s\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:680
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:679
|
||||
msgid "Article downloaded: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:686
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:685
|
||||
msgid "Failed to download article: %s from %s\n"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:691
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:690
|
||||
msgid "Article download failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:707
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:706
|
||||
msgid "Fetching feed"
|
||||
msgstr ""
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: libprs500 0.4.17\n"
|
||||
"POT-Creation-Date: 2008-03-23 16:44+PDT\n"
|
||||
"POT-Creation-Date: 2008-03-24 15:10+PDT\n"
|
||||
"PO-Revision-Date: 2007-11-08 14:39+PST\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: sl\n"
|
||||
@ -390,7 +390,7 @@ msgstr ""
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:146
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:149
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/tag_editor_ui.py:153
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:256
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:185
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:186
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/lrf_renderer/main_ui.py:187
|
||||
@ -1136,27 +1136,76 @@ msgstr ""
|
||||
msgid "Add tag to available tags and apply it to current book"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Add custom news source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:236
|
||||
msgid "Available user profiles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
msgid "Add/Update &profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Remove profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:60
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:70
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:223
|
||||
msgid "Switch to Advanced mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:65
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:73
|
||||
msgid "Switch to Basic mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:83
|
||||
msgid "Feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:84
|
||||
msgid "The feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:88
|
||||
msgid "Feed must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:89
|
||||
msgid "The feed %s must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:94
|
||||
msgid "Already exists"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:95
|
||||
msgid "This feed has already been added to the recipe"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:136
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:145
|
||||
msgid "Invalid input"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:137
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:146
|
||||
msgid "<p>Could not create recipe. Error:<br>%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:152
|
||||
msgid "Replace recipe?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles.py:153
|
||||
msgid "A custom recipe named %s already exists. Do you want to replace it?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:219
|
||||
msgid "Add custom news source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:220
|
||||
msgid "Available user profiles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:221
|
||||
msgid "Add/Update &profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:222
|
||||
msgid "&Remove profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:224
|
||||
msgid ""
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css"
|
||||
"\">\n"
|
||||
@ -1165,87 +1214,67 @@ msgid ""
|
||||
"font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Create a basic news "
|
||||
"profile, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.<br /"
|
||||
">The Basic tab is useful mainly for feeds that have the full article content "
|
||||
"embedded within them.</p></body></html>"
|
||||
"recipe, by adding RSS feeds to it. <br />For most feeds, you will have to "
|
||||
"use the \"Advanced\" setting to further customize the fetch process.</p></"
|
||||
"body></html>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:244
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:228
|
||||
msgid "Profile &title:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:245
|
||||
msgid "&Summary length:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:246
|
||||
msgid " characters"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:247
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:229
|
||||
msgid "&Oldest article:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:248
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:230
|
||||
msgid "The oldest article to download"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:249
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:231
|
||||
msgid " days"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:250
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:232
|
||||
msgid "&Max. number of articles per feed:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:251
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:233
|
||||
msgid "Maximum number of articles to download per feed."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:252
|
||||
msgid ""
|
||||
"Try to follow links in the RSS feed to full articles on the web. If you "
|
||||
"enable this option, you're probably going to end up having to use the "
|
||||
"advanced mode."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:253
|
||||
msgid "Try to download &full articles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:254
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:234
|
||||
msgid "Feeds in profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:255
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:235
|
||||
msgid "Remove feed from profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:257
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:260
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:237
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:240
|
||||
msgid "Add feed to profile"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:258
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:238
|
||||
msgid "&Feed title:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:259
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:239
|
||||
msgid "Feed &URL:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:261
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:241
|
||||
msgid "&Add feed"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:262
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:242
|
||||
msgid ""
|
||||
"For help with writing advanced news profiles, please visit <a href=\"https://"
|
||||
"libprs500.kovidgoyal.net/wiki/UserProfiles\">UserProfiles</a>"
|
||||
"For help with writing advanced news recipes, please visit <a href=\"http://"
|
||||
"libprs500.kovidgoyal.net/user_manual/news.html\">User Recipes</a>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:263
|
||||
#: /home/kovid/work/libprs500/src/libprs500/gui2/dialogs/user_profiles_ui.py:243
|
||||
msgid "Profile source code (python)"
|
||||
msgstr ""
|
||||
|
||||
@ -1857,7 +1886,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:91
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/main.py:95
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:512
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:511
|
||||
msgid "Fetching feeds..."
|
||||
msgstr ""
|
||||
|
||||
@ -1865,78 +1894,78 @@ msgstr ""
|
||||
msgid "Unknown News Source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:413
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:412
|
||||
msgid "Download finished"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:415
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:414
|
||||
msgid "Failed to download the following articles:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:417
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:423
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:416
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:422
|
||||
msgid " from "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:421
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:420
|
||||
msgid "Failed to download parts of the following articles:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:425
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:424
|
||||
msgid "\tFailed links:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:494
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:493
|
||||
msgid "Could not fetch article. Run with --debug to see the reason"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:516
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:515
|
||||
msgid "Got feeds from index page"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:520
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:519
|
||||
msgid "Trying to download cover..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:570
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:569
|
||||
msgid "Starting download [%d thread(s)]..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:584
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:583
|
||||
msgid "Feeds downloaded to %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:593
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:592
|
||||
msgid "Could not download cover: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:598
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:597
|
||||
msgid "Downloading cover from %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:633
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:632
|
||||
msgid "Untitled Article"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:674
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:673
|
||||
msgid ""
|
||||
"\n"
|
||||
"Downloaded article %s from %s\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:680
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:679
|
||||
msgid "Article downloaded: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:686
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:685
|
||||
msgid "Failed to download article: %s from %s\n"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:691
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:690
|
||||
msgid "Article download failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:707
|
||||
#: /home/kovid/work/libprs500/src/libprs500/web/feeds/news.py:706
|
||||
msgid "Fetching feed"
|
||||
msgstr ""
|
||||
|
@ -25,12 +25,15 @@ from libprs500.ebooks.lrf.web.profiles import DefaultProfile, FullContentProfile
|
||||
def option_parser(usage='''\
|
||||
%%prog [options] ARG
|
||||
|
||||
%%prog parsers an online source of articles, like an RSS or ATOM feed and
|
||||
%%prog parses an online source of articles, like an RSS or ATOM feed and
|
||||
fetches the article contents organized in a nice hierarchy.
|
||||
|
||||
ARG can be one of:
|
||||
|
||||
file name - %%prog will try to load a recipe from the file
|
||||
|
||||
builtin recipe title - %%prog will load the builtin recipe and use it to fetch the feed. For e.g. Newsweek or "The BBC" or "The New York Times"
|
||||
|
||||
recipe as a string - %%prog will load the recipe directly from the string arg.
|
||||
|
||||
Available builtin recipes are:
|
||||
|
@ -14,9 +14,11 @@
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
'''
|
||||
The backend to parse feeds and create HTML that can then be converted
|
||||
to an ebook.
|
||||
Defines various abstract base classes that can be subclassed to create powerful news fetching recipes.
|
||||
'''
|
||||
__docformat__ = "restructuredtext en"
|
||||
|
||||
|
||||
import logging, os, cStringIO, time, traceback, re, urlparse
|
||||
from collections import defaultdict
|
||||
|
||||
@ -40,57 +42,45 @@ class BasicNewsRecipe(object):
|
||||
'''
|
||||
|
||||
#: The title to use for the ebook
|
||||
#: @type: string
|
||||
title = _('Unknown News Source')
|
||||
|
||||
#: The author of this recipe
|
||||
__author__ = __appname__
|
||||
|
||||
#: Maximum number of articles to download from each feed
|
||||
#: @type: integer
|
||||
max_articles_per_feed = 100
|
||||
|
||||
#: Oldest article to download from this news source. In days.
|
||||
#: @type: float
|
||||
oldest_article = 7.0
|
||||
|
||||
#: Number of levels of links to follow on webpages that are linked
|
||||
#: to by the feed.
|
||||
#: @type: integer
|
||||
#: Number of levels of links to follow on article webpages
|
||||
recursions = 0
|
||||
|
||||
#: Delay between consecutive downloads in seconds
|
||||
#: @type: integer
|
||||
delay = 0
|
||||
|
||||
#: Number of simultaneous downloads. Set to 1 if the server is picky.
|
||||
#: Automatically reduced to 1 if L{delay} > 0
|
||||
#: @type: integer
|
||||
#: Automatically reduced to 1 if :attr:`BasicNewsRecipe.delay` > 0
|
||||
simultaneous_downloads = 5
|
||||
|
||||
#: Timeout for fetching files from server in seconds
|
||||
#: @type: integer
|
||||
timeout = 120
|
||||
timeout = 120.0
|
||||
|
||||
#: The format string for the date shown on the first page
|
||||
#: By default: Day Name Day Number Month Name Year
|
||||
#: @type: string
|
||||
#: The format string for the date shown on the first page.
|
||||
#: By default: Day_Name, Day_Number Month_Name Year
|
||||
timefmt = ' [%a, %d %b %Y]'
|
||||
|
||||
#: List of feeds to download
|
||||
#: Can be either C{[url1, url2, ...]} or C{[('title1', url1), ('title2', url2),...]}
|
||||
#: @type: List of strings or list of 2-tuples
|
||||
#: Can be either ``[url1, url2, ...]`` or ``[('title1', url1), ('title2', url2),...]``
|
||||
feeds = None
|
||||
|
||||
#: Max number of characters in the short description.
|
||||
#: @type: integer
|
||||
#: Max number of characters in the short description
|
||||
summary_length = 500
|
||||
|
||||
#: If True stylesheets are not downloaded and processed
|
||||
#: Convenient flag to disable loading of stylesheets for websites
|
||||
#: that have overly complex stylesheets unsuitable for conversion
|
||||
#: to ebooks formats
|
||||
#: @type: boolean
|
||||
#: If True stylesheets are not downloaded and processed
|
||||
no_stylesheets = False
|
||||
|
||||
#: If True the GUI will ask the user for a username and password
|
||||
@ -99,94 +89,133 @@ class BasicNewsRecipe(object):
|
||||
needs_subscription = False
|
||||
|
||||
#: Specify an override encoding for sites that have an incorrect
|
||||
#: charset specification. The most common being specifying latin1 and
|
||||
#: using cp1252. If None, try to detect the encoding.
|
||||
#: charset specification. The most common being specifying ``latin1`` and
|
||||
#: using ``cp1252``. If None, try to detect the encoding.
|
||||
encoding = None
|
||||
|
||||
#: Normally we try to guess if a feed has full articles embedded in it
|
||||
#: based on the length of the embedded content. If C{None}, then the
|
||||
#: default guessing is used. If C{True} then the we always assume the feeds has
|
||||
#: embedded content and if False we always assume the feed does not have
|
||||
#: based on the length of the embedded content. If `None`, then the
|
||||
#: default guessing is used. If `True` then the we always assume the feeds has
|
||||
#: embedded content and if `False` we always assume the feed does not have
|
||||
#: embedded content.
|
||||
use_embedded_content = None
|
||||
|
||||
#: Specify any extra CSS that should be addded to downloaded HTML files
|
||||
#: It will be inserted into C{<style></style>} just before the closing
|
||||
#: C{</head>} tag thereby overrinding all CSS except that which is
|
||||
#: declared using the style attribute on individual HTML tags.
|
||||
#: type: string
|
||||
#: Specify any extra :term:`CSS` that should be addded to downloaded :term:`HTML` files
|
||||
#: It will be inserted into `<style>` tags, just before the closing
|
||||
#: `</head>` tag thereby overrinding all :term:`CSS` except that which is
|
||||
#: declared using the style attribute on individual :term:`HTML` tags.
|
||||
#: For example::
|
||||
#:
|
||||
#: extra_css = '.heading { font: serif x-large }'
|
||||
#:
|
||||
extra_css = None
|
||||
|
||||
#: List of regular expressions that determines which links to follow
|
||||
#: If empty, it is ignored.
|
||||
#: Only one of L{match_regexps} or L{filter_regexps} should be defined
|
||||
#: @type: list of strings
|
||||
#: If empty, it is ignored. For example::
|
||||
#:
|
||||
#: match_regexps = [r'page=[0-9]+']
|
||||
#:
|
||||
#: will match all URLs that have `page=some number` in them.
|
||||
#:
|
||||
#: Only one of :attr:`BasicNewsRecipe.match_regexps` or
|
||||
#: :attr:`BasicNewsRecipe.filter_regexps` should be defined.
|
||||
match_regexps = []
|
||||
|
||||
#: List of regular expressions that determines which links to ignore
|
||||
#: If empty it is ignored
|
||||
#: Only one of L{match_regexps} or L{filter_regexps} should be defined
|
||||
#: @type: list of strings
|
||||
#: If empty it is ignored. For example::
|
||||
#:
|
||||
#: filter_regexps = [r'ads\.doubleclick\.net']
|
||||
#:
|
||||
#: will remove all URLs that have `ads.doubleclick.net` in them.
|
||||
#:
|
||||
#: Only one of :attr:`BasicNewsRecipe.match_regexps` or
|
||||
#: :attr:`BasicNewsRecipe.filter_regexps` should be defined.
|
||||
filter_regexps = []
|
||||
|
||||
#: List of options to pass to html2lrf, to customize generation of LRF ebooks.
|
||||
#: @type: list of strings
|
||||
html2lrf_options = []
|
||||
|
||||
#: List of tags to be removed. Specified tags are removed from downloaded HTML.
|
||||
#: A tag is specified as a dictionary of the form::
|
||||
#: {
|
||||
#:
|
||||
#: {
|
||||
#: name : 'tag name', #e.g. 'div'
|
||||
#: attrs : a dictionary, #e.g. {class: 'advertisment'}
|
||||
#: }
|
||||
#: }
|
||||
#:
|
||||
#: All keys are optional. For a full explanantion of the search criteria, see
|
||||
#: U{http://www.crummy.com/software/BeautifulSoup/documentation.html#The basic find method: findAll(name, attrs, recursive, text, limit, **kwargs)}
|
||||
#: `Beautiful Soup <http://www.crummy.com/software/BeautifulSoup/documentation.html#The basic find method: findAll(name, attrs, recursive, text, limit, **kwargs)>`_
|
||||
#: A common example::
|
||||
#:
|
||||
#: remove_tags = [dict(name='div', attrs={'class':'advert'})]
|
||||
#: This will remove all <div class="advert"> tags and all their children from the downloaded HTML.
|
||||
#: @type: list
|
||||
#:
|
||||
#: This will remove all `<div class="advert">` tags and all
|
||||
#: their children from the downloaded :term:`HTML`.
|
||||
remove_tags = []
|
||||
|
||||
#: Remove all tags that occur after the specified tag.
|
||||
#: For the format for specifying a tag see L{remove_tags}.
|
||||
#: For example, C{remove_tags_after = [dict(id='content')]} will remove all
|
||||
#: tags after the element with id C{content}.
|
||||
#: For the format for specifying a tag see :attr:`BasicNewsRecipe.remove_tags`.
|
||||
#: For example::
|
||||
#:
|
||||
#: remove_tags_after = [dict(id='content')]
|
||||
#:
|
||||
#: will remove all
|
||||
#: tags after the first element with `id="content"`.
|
||||
remove_tags_after = None
|
||||
|
||||
#: Remove all tags that occur before the specified tag.
|
||||
#: For the format for specifying a tag see L{remove_tags}.
|
||||
#: For example, C{remove_tags_before = [dict(id='content')]} will remove all
|
||||
#: tags before the element with id C{content}.
|
||||
#: For the format for specifying a tag see :attr:`BasicNewsRecipe.remove_tags`.
|
||||
#: For example::
|
||||
#:
|
||||
#: remove_tags_before = [dict(id='content')]
|
||||
#:
|
||||
#: will remove all
|
||||
#: tags before the first element with `id="content"`.
|
||||
remove_tags_before = None
|
||||
|
||||
#: Keep only the specified tags and their children.
|
||||
#: For the format for specifying tags see L{remove_tags}.
|
||||
#: If this list is not empty, then the <body> element will be emptied and re-filled with
|
||||
#: the tags that match the entries in this list.
|
||||
#: @type: list
|
||||
#: For the format for specifying a tag see :attr:`BasicNewsRecipe.remove_tags`.
|
||||
#: If this list is not empty, then the `<body>` tag will be emptied and re-filled with
|
||||
#: the tags that match the entries in this list. For example::
|
||||
#:
|
||||
#: keep_only_tags = [dict(id=['content', 'heading'])]
|
||||
#:
|
||||
#: will keep only tags that have an `id` attribute of `"content"` or `"heading"`.
|
||||
keep_only_tags = []
|
||||
|
||||
#: List of regexp substitution rules to run on the downloaded HTML. Each element of the
|
||||
#: List of :term:`regexp` substitution rules to run on the downloaded :term:`HTML`.
|
||||
#: Each element of the
|
||||
#: list should be a two element tuple. The first element of the tuple should
|
||||
#: be a compiled regular expression and the second a callable that takes
|
||||
#: a single match object and returns a string to replace the match.
|
||||
#: @type: list of tuples
|
||||
#: a single match object and returns a string to replace the match. For example::
|
||||
#:
|
||||
#: preprocess_regexps = [
|
||||
#: (re.compile(r'<!--Article ends here-->.*</body>', re.DOTALL|re.IGNORECASE),
|
||||
#: lambda match: '</body>'),
|
||||
#: ]
|
||||
#:
|
||||
#: will remove everythong from `<!--Article ends here-->` to `</body>`.
|
||||
preprocess_regexps = []
|
||||
|
||||
# See the built-in profiles for examples of these settings.
|
||||
|
||||
def get_cover_url(self):
|
||||
'''
|
||||
Return a URL to the cover image for this issue or None.
|
||||
@rtype: string or None
|
||||
Return a :term:`URL` to the cover image for this issue or `None`.
|
||||
By default it returns the value of the member `self.cover_url` which
|
||||
is normally `None`. If you want your recipe to download a cover for the e-book
|
||||
override this method in your subclass, or set the member variable `self.cover_url`
|
||||
before this method is called.
|
||||
'''
|
||||
return getattr(self, 'cover_url', None)
|
||||
|
||||
def get_feeds(self):
|
||||
'''
|
||||
Return a list of RSS feeds to fetch for this profile. Each element of the list
|
||||
Return a list of :term:RSS feeds to fetch for this profile. Each element of the list
|
||||
must be a 2-element tuple of the form (title, url). If title is None or an
|
||||
empty string, the title from the feed is used.
|
||||
empty string, the title from the feed is used. This method is useful if your recipe
|
||||
needs to do some processing to figure out the list of feeds to download. If
|
||||
so, override in your subclass.
|
||||
'''
|
||||
if not self.feeds:
|
||||
raise NotImplementedError
|
||||
@ -195,54 +224,74 @@ class BasicNewsRecipe(object):
|
||||
return self.feeds
|
||||
|
||||
@classmethod
|
||||
def print_version(cls, url):
|
||||
def print_version(self, url):
|
||||
'''
|
||||
Take a URL pointing to an article and returns the URL pointing to the
|
||||
print version of the article.
|
||||
Take a `url` pointing to the webpage with article content and return the
|
||||
:term:`URL` pointing to the print version of the article. By default does
|
||||
nothing. For example::
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?&pagewanted=print'
|
||||
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def get_browser(cls):
|
||||
def get_browser(self):
|
||||
'''
|
||||
Return a browser instance used to fetch documents from the web.
|
||||
Return a browser instance used to fetch documents from the web. By default
|
||||
it returns a `mechanize <http://wwwsearch.sourceforge.net/mechanize/>`_
|
||||
browser instance that supports cookies, ignores robots.txt, handles
|
||||
refreshes and has a mozilla firefox user agent.
|
||||
|
||||
If your recipe requires that you login first, override this method
|
||||
in your subclass. For example, the following code is used in the New York
|
||||
Times recipe to login for full access::
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open('http://www.nytimes.com/auth/login')
|
||||
br.select_form(name='login')
|
||||
br['USERID'] = self.username
|
||||
br['PASSWORD'] = self.password
|
||||
br.submit()
|
||||
return br
|
||||
|
||||
If your profile requires that you login first, override this method
|
||||
in your subclass. See for example the nytimes profile.
|
||||
'''
|
||||
return browser()
|
||||
|
||||
def get_article_url(self, item):
|
||||
def get_article_url(self, article):
|
||||
'''
|
||||
Override to perform extraction of URL for each article.
|
||||
@param item: An article instance from L{feedparser}.
|
||||
@type item: L{FeedParserDict}
|
||||
Override in a subclass to customize extraction of the :term:`URL` that points
|
||||
to the content for each article. Return the
|
||||
article URL. It is called with `article`, an object representing a parsed article
|
||||
from a feed. See `feedsparser <http://www.feedparser.org/docs/>`_.
|
||||
By default it returns `article.link <http://www.feedparser.org/docs/reference-entry-link.html>`_.
|
||||
'''
|
||||
return item.get('link', None)
|
||||
return article.get('link', None)
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
'''
|
||||
This function is called with the source of each downloaded HTML file, before
|
||||
This method is called with the source of each downloaded :term:`HTML` file, before
|
||||
it is parsed for links and images.
|
||||
It can be used to do arbitrarily powerful pre-processing on the HTML.
|
||||
@param soup: A U{BeautifulSoup<http://www.crummy.com/software/BeautifulSoup/documentation.html>}
|
||||
instance containing the downloaded HTML.
|
||||
@type soup: A U{BeautifulSoup<http://www.crummy.com/software/BeautifulSoup/documentation.html>} instance
|
||||
@return: It must return soup (after having done any needed preprocessing)
|
||||
@rtype: A U{BeautifulSoup<http://www.crummy.com/software/BeautifulSoup/documentation.html>} instance
|
||||
It can be used to do arbitrarily powerful pre-processing on the :term:`HTML`.
|
||||
It should return `soup` after processing it.
|
||||
|
||||
`soup`: A `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_
|
||||
instance containing the downloaded :term:`HTML`.
|
||||
'''
|
||||
return soup
|
||||
|
||||
def postprocess_html(self, soup):
|
||||
'''
|
||||
This function is called with the source of each downloaded HTML file, after
|
||||
This method is called with the source of each downloaded :term:`HTML` file, after
|
||||
it is parsed for links and images.
|
||||
It can be used to do arbitrarily powerful pre-processing on the HTML.
|
||||
@param soup: A U{BeautifulSoup<http://www.crummy.com/software/BeautifulSoup/documentation.html>}
|
||||
instance containing the downloaded HTML.
|
||||
@type soup: A U{BeautifulSoup<http://www.crummy.com/software/BeautifulSoup/documentation.html>} instance
|
||||
@return: It must return soup (after having done any needed preprocessing)
|
||||
@rtype: A U{BeautifulSoup<http://www.crummy.com/software/BeautifulSoup/documentation.html>} instance
|
||||
It can be used to do arbitrarily powerful post-processing on the :term:`HTML`.
|
||||
It should return `soup` after processing it.
|
||||
|
||||
`soup`: A `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_
|
||||
instance containing the downloaded :term:`HTML`.
|
||||
'''
|
||||
return soup
|
||||
|
||||
@ -256,8 +305,10 @@ class BasicNewsRecipe(object):
|
||||
def index_to_soup(self, url_or_raw):
|
||||
'''
|
||||
Convenience method that takes an URL to the index page and returns
|
||||
a BeautifulSoup of it.
|
||||
@param url_or_raw: Either a URL or the downloaded index page as a string
|
||||
a `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_
|
||||
of it.
|
||||
|
||||
`url_or_raw`: Either a URL or the downloaded index page as a string
|
||||
'''
|
||||
if re.match(r'\w+://', url_or_raw):
|
||||
raw = self.browser.open(url_or_raw).read()
|
||||
@ -272,11 +323,13 @@ class BasicNewsRecipe(object):
|
||||
|
||||
def sort_index_by(self, index, weights):
|
||||
'''
|
||||
Convenience method to sort the titles in index according to weights.
|
||||
@param index: A list of titles.
|
||||
@param weights: A dictionary that maps weights to titles. If any titles
|
||||
Convenience method to sort the titles in `index` according to `weights`.
|
||||
`index` is sorted in place. Returns `index`.
|
||||
|
||||
`index`: A list of titles.
|
||||
|
||||
`weights`: A dictionary that maps weights to titles. If any titles
|
||||
in index are not in weights, they are assumed to have a weight of 0.
|
||||
@return: Sorted index
|
||||
'''
|
||||
weights = defaultdict(lambda : 0, weights)
|
||||
index.sort(cmp=lambda x, y: cmp(weights[x], weights[y]))
|
||||
@ -288,10 +341,13 @@ class BasicNewsRecipe(object):
|
||||
instead of feeds to generate a list of articles. Typical uses are for
|
||||
news sources that have a "Print Edition" webpage that lists all the
|
||||
articles in the current print edition. If this function is implemented,
|
||||
it will be used in preference to L{parse_feeds}.
|
||||
@rtype: list
|
||||
@return: A list of two element tuples of the form ('feed title', list of articles).
|
||||
Each list of articles contains dictionaries of the form::
|
||||
it will be used in preference to :meth:`BasicNewsRecipe.parse_feeds`.
|
||||
|
||||
It must return a list. Each element of the list must be a 2-element tuple
|
||||
of the form ``('feed title', list of articles)``.
|
||||
|
||||
Each list of articles must contain dictionaries of the form::
|
||||
|
||||
{
|
||||
'title' : article title,
|
||||
'url' : URL of print version,
|
||||
@ -299,6 +355,8 @@ class BasicNewsRecipe(object):
|
||||
'description' : A summary of the article
|
||||
'content' : The full article (can be an empty string). This is used by FullContentProfile
|
||||
}
|
||||
|
||||
For an example, see the recipe for downloading `The Atlantic`.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@ -692,9 +750,8 @@ class BasicNewsRecipe(object):
|
||||
|
||||
def parse_feeds(self):
|
||||
'''
|
||||
Create a list of articles from a list of feeds.
|
||||
@rtype: list
|
||||
@return: A list of L{Feed}s.
|
||||
Create a list of articles from the list of feeds returned by :meth:`BasicNewsRecipe.get_feeds`.
|
||||
Return a list of :class:`Feed` objects.
|
||||
'''
|
||||
feeds = self.get_feeds()
|
||||
parsed_feeds = []
|
||||
@ -713,14 +770,18 @@ class BasicNewsRecipe(object):
|
||||
return parsed_feeds
|
||||
|
||||
@classmethod
|
||||
def tag_to_string(cls, tag, use_alt=True):
|
||||
def tag_to_string(self, tag, use_alt=True):
|
||||
'''
|
||||
Convenience method to take a BeautifulSoup Tag and extract the text from it
|
||||
recursively, including any CDATA sections and alt tag attributes.
|
||||
@param use_alt: If True try to use the alt attribute for tags that don't have any textual content
|
||||
@type use_alt: boolean
|
||||
@return: A unicode (possibly empty) object
|
||||
@rtype: unicode string
|
||||
Convenience method to take a
|
||||
`BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_
|
||||
`Tag` and extract the text from it recursively, including any CDATA sections
|
||||
and alt tag attributes. Return a possibly empty unicode string.
|
||||
|
||||
`use_alt`: If `True` try to use the alt attribute for tags that don't
|
||||
have any textual content
|
||||
|
||||
`tag`: `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/documentation.html>`_
|
||||
`Tag`
|
||||
'''
|
||||
if not tag:
|
||||
return ''
|
||||
@ -731,7 +792,7 @@ class BasicNewsRecipe(object):
|
||||
if isinstance(item, (NavigableString, CData)):
|
||||
strings.append(item.string)
|
||||
elif isinstance(item, Tag):
|
||||
res = cls.tag_to_string(item)
|
||||
res = self.tag_to_string(item)
|
||||
if res:
|
||||
strings.append(res)
|
||||
elif use_alt and item.has_key('alt'):
|
||||
@ -770,9 +831,9 @@ class CustomIndexRecipe(BasicNewsRecipe):
|
||||
|
||||
def custom_index(self):
|
||||
'''
|
||||
Return the path to a custom HTML document that will serve as the index for
|
||||
this recipe.
|
||||
@rtype: string
|
||||
Return the filesystem path to a custom HTML document that will serve as the index for
|
||||
this recipe. The index document will typically contain many `<a href="...">`
|
||||
tags that point to resources on the internet that should be downloaded.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@ -794,4 +855,13 @@ class CustomIndexRecipe(BasicNewsRecipe):
|
||||
fetcher.show_progress = False
|
||||
res = fetcher.start_fetch(url)
|
||||
self.create_opf()
|
||||
return res
|
||||
return res
|
||||
|
||||
class AutomaticNewsRecipe(BasicNewsRecipe):
|
||||
|
||||
keep_only_tags = [dict(name=['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'])]
|
||||
|
||||
def fetch_embedded_article(self, article, dir, logger, f, a, num_of_feeds):
|
||||
if self.use_embedded_content:
|
||||
self.web2disk_options.keep_only_tags = []
|
||||
return BasicNewsRecipe.fetch_embedded_article(self, article, dir, logger, f, a, num_of_feeds)
|
Loading…
x
Reference in New Issue
Block a user