Create documentation for feeds2lrf in User Manual

This commit is contained in:
Kovid Goyal 2008-03-25 21:22:49 +00:00
parent 9e23a43f15
commit 416f404328
26 changed files with 2657 additions and 854 deletions

View File

@ -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...')

View File

@ -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):

View File

@ -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):

View File

@ -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 &amp;profile</string>
<string>Add/Update &amp;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>&amp;Remove profile</string>
<string>&amp;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>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;">
&lt;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. &lt;br />For most feeds, you will have to use the "Advanced" setting to further customize the fetch process.&lt;br />The Basic tab is useful mainly for feeds that have the full article content embedded within them.&lt;/p>&lt;/body>&lt;/html></string>
&lt;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. &lt;br />For most feeds, you will have to use the "Advanced mode" to further customize the fetch process.&lt;/p>&lt;/body>&lt;/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 &amp;title:</string>
<string>Recipe &amp;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>&amp;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 &amp;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>&amp;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 &lt;a href="https://__appname__.kovidgoyal.net/wiki/UserProfiles">UserProfiles&lt;/a></string>
<string>For help with writing advanced news recipes, please visit &lt;a href="http://__appname__.kovidgoyal.net/user_manual/news.html">User Recipes&lt;/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>

View File

@ -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

View 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

View File

@ -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

View File

@ -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('&lt;', '<').replace('&gt;', '>')
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)

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -29,6 +29,7 @@ Sections
metadata
cli/cli-index
faq
glossary
Convenience
-----------------------

View File

@ -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

View 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
View 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)

View File

@ -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

View File

@ -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 "Ä&ltester 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"

View File

@ -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 ""

View File

@ -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

View File

@ -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 ""

View File

@ -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:

View File

@ -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)