Sync to trunk.

This commit is contained in:
John Schember 2011-02-10 19:14:52 -05:00
commit 2cdf67776b
36 changed files with 1705 additions and 771 deletions

View File

@ -10,7 +10,7 @@ you know what you are doing. If you delete this file, it will be recreated from
defaults.
'''
#: Auto increment series index
# The algorithm used to assign a new book in an existing series a series number.
# New series numbers assigned using this tweak are always integer values, except
# if a constant non-integer is specified.
@ -29,7 +29,7 @@ defaults.
# series_index_auto_increment = 16.5
series_index_auto_increment = 'next'
#: Add separator after completing an author name
# Should the completion separator be append
# to the end of the completed text to
# automatically begin a new completion operation
@ -38,6 +38,7 @@ series_index_auto_increment = 'next'
authors_completer_append_separator = False
#: Author sort name algorithm
# The algorithm used to copy author to author_sort
# Possible values are:
# invert: use "fn ln" -> "ln, fn" (the default algorithm)
@ -49,6 +50,7 @@ authors_completer_append_separator = False
# selecting 'manage authors', and pressing 'Recalculate all author sort values'.
author_sort_copy_method = 'invert'
#: Use author sort in Tag Browser
# Set which author field to display in the tags pane (the list of authors,
# series, publishers etc on the left hand side). The choices are author and
# author_sort. This tweak affects only what is displayed under the authors
@ -63,6 +65,7 @@ author_sort_copy_method = 'invert'
# categories_use_field_for_author_name = 'author_sort'
categories_use_field_for_author_name = 'author'
#: Control partitioning of Tag Browser
# When partitioning the tags browser, the format of the subcategory label is
# controlled by a template: categories_collapsed_name_template if sorting by
# name, categories_collapsed_rating_template if sorting by average rating, and
@ -74,24 +77,25 @@ categories_use_field_for_author_name = 'author'
# author category will be the name of the author. The sub-values available are:
# name: the printable name of the item
# count: the number of books that references this item
# avg_rating: the averate rating of all the books referencing this item
# avg_rating: the average rating of all the books referencing this item
# sort: the sort value. For authors, this is the author_sort for that author
# category: the category (e.g., authors, series) that the item is in.
# Note that the "r'" in front of the { is necessary if there are backslashes
# (\ characters) in the template. It doesn't hurt anything to leave it there
# even if there aren't any backslashes.
categories_collapsed_name_template = r'{first.sort:shorten(4,'',0)} - {last.sort:shorten(4,'',0)}'
categories_collapsed_name_template = r'{first.sort:shorten(4,"",0)} - {last.sort:shorten(4,"",0)}'
categories_collapsed_rating_template = r'{first.avg_rating:4.2f:ifempty(0)} - {last.avg_rating:4.2f:ifempty(0)}'
categories_collapsed_popularity_template = r'{first.count:d} - {last.count:d}'
#: Set boolean custom columns to be tristate
# Set whether boolean custom columns are two- or three-valued.
# Two-values for true booleans
# three-values for yes/no/unknown
# Set to 'yes' for three-values, 'no' for two-values
bool_custom_columns_are_tristate = 'yes'
#: Specify columns to sort the booklist by on startup
# Provide a set of columns to be sorted on when calibre starts
# The argument is None if saved sort history is to be used
# otherwise it is a list of column,order pairs. Column is the
@ -101,6 +105,7 @@ bool_custom_columns_are_tristate = 'yes'
# title within authors.
sort_columns_at_startup = None
#; Control how dates are displayed
# Format to be used for publication date and the timestamp (date).
# A string controlling how the publication date is displayed in the GUI
# d the day as number without a leading zero (1 to 31)
@ -121,6 +126,7 @@ sort_columns_at_startup = None
gui_pubdate_display_format = 'MMM yyyy'
gui_timestamp_display_format = 'dd MMM yyyy'
#: Control sorting of titles and series in the display
# Control title and series sorting in the library view.
# If set to 'library_order', Leading articles such as The and A will be ignored.
# If set to 'strictly_alphabetic', the titles will be sorted without processing
@ -132,6 +138,7 @@ gui_timestamp_display_format = 'dd MMM yyyy'
# without changing anything is sufficient to change the sort.
title_series_sorting = 'library_order'
#: Control formatting of title and series when used in templates
# Control how title and series names are formatted when saving to disk/sending
# to device. If set to library_order, leading articles such as The and A will
# be put at the end
@ -140,6 +147,7 @@ title_series_sorting = 'library_order'
# strictly_alphabetic, it would remain "The Client".
save_template_title_series_sorting = 'library_order'
#: Set the list of words considered to be "articles" for sort strings
# Set the list of words that are to be considered 'articles' when computing the
# title sort strings. The list is a regular expression, with the articles
# separated by 'or' bars. Comparisons are case insensitive, and that cannot be
@ -149,7 +157,7 @@ save_template_title_series_sorting = 'library_order'
# Default: '^(A|The|An)\s+'
title_sort_articles=r'^(A|The|An)\s+'
#: Specify a folder calibre should connect to at startup
# Specify a folder that calibre should connect to at startup using
# connect_to_folder. This must be a full path to the folder. If the folder does
# not exist when calibre starts, it is ignored. If there are '\' characters in
@ -159,7 +167,7 @@ title_sort_articles=r'^(A|The|An)\s+'
# auto_connect_to_folder = '/home/dropbox/My Dropbox/someone/library'
auto_connect_to_folder = ''
#: Specify renaming rules for SONY collections
# Specify renaming rules for sony collections. This tweak is only applicable if
# metadata management is set to automatic. Collections on Sonys are named
# depending upon whether the field is standard or custom. A collection derived
@ -212,7 +220,7 @@ auto_connect_to_folder = ''
sony_collection_renaming_rules={}
sony_collection_name_template='{value}{category:| (|)}'
#: Specify how SONY collections are sorted
# Specify how sony collections are sorted. This tweak is only applicable if
# metadata management is set to automatic. You can indicate which metadata is to
# be used to sort on a collection-by-collection basis. The format of the tweak
@ -231,7 +239,7 @@ sony_collection_name_template='{value}{category:| (|)}'
sony_collection_sorting_rules = []
# Create search terms to apply a query across several built-in search terms.
#: Create search terms to apply a query across several built-in search terms.
# Syntax: {'new term':['existing term 1', 'term 2', ...], 'new':['old'...] ...}
# Example: create the term 'myseries' that when used as myseries:foo would
# search all of the search categories 'series', '#myseries', and '#myseries2':
@ -244,15 +252,17 @@ sony_collection_sorting_rules = []
grouped_search_terms = {}
# Set this to True (not 'True') to ensure that tags in 'Tags to add when adding
#: Control how tags are applied when copying books to another library
# Set this to True to ensure that tags in 'Tags to add when adding
# a book' are added when copying books to another library
add_new_book_tags_when_importing_books = False
# Set the maximum number of tags to show per book in the content server
#: Set the maximum number of tags to show per book in the content server
max_content_server_tags_shown=5
# Set custom metadata fields that the content server will or will not display.
#: Set custom metadata fields that the content server will or will not display.
# content_server_will_display is a list of custom fields to be displayed.
# content_server_wont_display is a list of custom fields not to be displayed.
# wont_display has priority over will_display.
@ -270,13 +280,27 @@ max_content_server_tags_shown=5
content_server_will_display = ['*']
content_server_wont_display = []
# Same as above (content server) but for the book details pane. Same syntax.
#: Set custom metadata fields that the book details panel will or will not display.
# book_details_will_display is a list of custom fields to be displayed.
# book_details_wont_display is a list of custom fields not to be displayed.
# wont_display has priority over will_display.
# The special value '*' means all custom fields. The value [] means no entries.
# Defaults:
# book_details_will_display = ['*']
# book_details_wont_display = []
# Examples:
# To display only the custom fields #mytags and #genre:
# book_details_will_display = ['#mytags', '#genre']
# book_details_wont_display = []
# To display all fields except #mycomments:
# book_details_will_display = ['*']
# book_details_wont_display['#mycomments']
# As above, this tweak affects only display of custom fields. The standard
# fields are not affected
book_details_will_display = ['*']
book_details_wont_display = []
#: Set the maximum number of sort 'levels'
# Set the maximum number of sort 'levels' that calibre will use to resort the
# library after certain operations such as searches or device insertion. Each
# sort level adds a performance penalty. If the database is large (thousands of
@ -284,16 +308,14 @@ book_details_wont_display = []
# level sorts, and if you are seeing a slowdown, reduce the value of this tweak.
maximum_resort_levels = 5
# Absolute path to a TTF font file to use as the font for the title and author
# when generating a default cover. Useful if the default font (Liberation
#: Specify which font to use when generating a default cover
# Absolute path to .ttf font files to use as the fonts for the title, author
# and footer when generating a default cover. Useful if the default font (Liberation
# Serif) does not contain glyphs for the language of the books in your library.
generate_cover_title_font = None
# Absolute path to a TTF font file to use as the font for the footer in the
# default cover
generate_cover_foot_font = None
#: Control behavior of double clicks on the book list
# Behavior of doubleclick on the books list. Choices: open_viewer, do_nothing,
# edit_cell, edit_metadata. Selecting edit_metadata has the side effect of
# disabling editing a field using a single click.
@ -302,7 +324,8 @@ generate_cover_foot_font = None
doubleclick_on_library_view = 'open_viewer'
# Language to use when sorting. Setting this tweak will force sorting to use the
#: Language to use when sorting.
# Setting this tweak will force sorting to use the
# collating order for the specified language. This might be useful if you run
# calibre in English but want sorting to work in the language where you live.
# Set the tweak to the desired ISO 639-1 language code, in lower case.
@ -313,12 +336,13 @@ doubleclick_on_library_view = 'open_viewer'
# Example: locale_for_sorting = 'nb' -- sort using Norwegian rules.
locale_for_sorting = ''
#: Number of columns for custom metadata in the edit metadata dialog
# Set whether to use one or two columns for custom metadata when editing
# metadata one book at a time. If True, then the fields are laid out using two
# columns. If False, one column is used.
metadata_single_use_2_cols_for_custom_fields = True
#: The number of seconds to wait before sending emails
# The number of seconds to wait before sending emails when using a
# public email server like gmail or hotmail. Default is: 5 minutes
# Setting it to lower may cause the server's SPAM controls to kick in,
@ -326,3 +350,9 @@ metadata_single_use_2_cols_for_custom_fields = True
# calibre.
public_smtp_relay_delay = 301
#: Remove the bright yellow lines at the edges of the book list
# Control whether the bright yellow lines at the edges of book list are drawn
# when a section of the user interface is hidden. Changes will take effect
# after a restart of calibre.
draw_hidden_section_indicators = True

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

View File

@ -1,6 +1,6 @@
__license__ = 'GPL v3'
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2008-2011, Darko Miletic <darko.miletic at gmail.com>'
'''
b92.net
'''
@ -10,7 +10,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class B92(BasicNewsRecipe):
title = 'B92'
__author__ = 'Darko Miletic'
description = 'B92 info, najnovije vesti iz Srbije, regiona i sveta'
description = 'Najnovije vesti iz Srbije, regiona i sveta, aktuelne teme iz sveta politike, ekonomije, drustva, foto galerija, kolumne'
publisher = 'B92'
category = 'news, politics, Serbia'
oldest_article = 2
@ -20,7 +20,14 @@ class B92(BasicNewsRecipe):
encoding = 'cp1250'
language = 'sr'
publication_type = 'newsportal'
extra_css = ' @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif} '
masthead_url = 'http://www.b92.net/images/fp/logo.gif'
extra_css = """
@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
body{font-family: Arial,Helvetica,sans1,sans-serif}
.articledescription{font-family: serif1, serif}
.article-info2,.article-info1{text-transform: uppercase; font-size: small}
"""
conversion_options = {
'comment' : description
@ -32,22 +39,25 @@ class B92(BasicNewsRecipe):
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
keep_only_tags = [dict(name='table', attrs={'class':'maindocument'})]
remove_tags = [
dict(name='ul', attrs={'class':'comment-nav'})
,dict(name=['embed','link','base'] )
,dict(name='div', attrs={'class':'udokum'} )
]
keep_only_tags = [dict(attrs={'class':['article-info1','article-text']})]
remove_attributes = ['width','height','align','hspace','vspace','border']
remove_tags = [dict(name=['embed','link','base','meta'])]
feeds = [
(u'Vesti' , u'http://www.b92.net/info/rss/vesti.xml' )
,(u'Biz' , u'http://www.b92.net/info/rss/biz.xml' )
,(u'Sport' , u'http://www.b92.net/info/rss/sport.xml' )
,(u'Zivot' , u'http://www.b92.net/info/rss/zivot.xml' )
,(u'Kultura' , u'http://www.b92.net/info/rss/kultura.xml' )
,(u'Automobili' , u'http://www.b92.net/info/rss/automobili.xml')
,(u'Tehnopolis' , u'http://www.b92.net/info/rss/tehnopolis.xml')
]
def print_version(self, url):
return url + '&version=print'
def preprocess_html(self, soup):
return self.adeify_images(soup)
for item in soup.findAll(style=True):
del item['style']
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup

View File

@ -5,8 +5,8 @@ __license__ = 'GPL v3'
__copyright__ = '04 December 2010, desUBIKado'
__author__ = 'desUBIKado'
__description__ = 'Daily newspaper from Aragon'
__version__ = 'v0.05'
__date__ = '07, December 2010'
__version__ = 'v0.07'
__date__ = '06, February 2011'
'''
elperiodicodearagon.com
'''
@ -38,7 +38,8 @@ class elperiodicodearagon(BasicNewsRecipe):
,'publisher' : publisher
}
feeds = [(u'Arag\xf3n', u'http://elperiodicodearagon.com/RSS/2.xml'),
feeds = [
(u'Arag\xf3n', u'http://elperiodicodearagon.com/RSS/2.xml'),
(u'Internacional', u'http://elperiodicodearagon.com/RSS/4.xml'),
(u'Espa\xf1a', u'http://elperiodicodearagon.com/RSS/3.xml'),
(u'Econom\xeda', u'http://elperiodicodearagon.com/RSS/5.xml'),
@ -47,13 +48,16 @@ class elperiodicodearagon(BasicNewsRecipe):
(u'Opini\xf3n', u'http://elperiodicodearagon.com/RSS/103.xml'),
(u'Escenarios', u'http://elperiodicodearagon.com/RSS/105.xml'),
(u'Sociedad', u'http://elperiodicodearagon.com/RSS/104.xml'),
(u'Gente', u'http://elperiodicodearagon.com/RSS/330.xml')]
(u'Gente', u'http://elperiodicodearagon.com/RSS/330.xml')
]
extra_css = '''
h3{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:xx-large;}
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
dd{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
h3 {font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:30px;}
h2 {font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:18px;}
h4 {font-family:Arial,Helvetica,sans-serif; font-style:italic; font-weight:normal;font-size:20px;}
.columnaDeRecursosRelacionados {font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:14px;}
img{margin-bottom: 0.4em}
'''
remove_attributes = ['height','width']
@ -82,6 +86,7 @@ class elperiodicodearagon(BasicNewsRecipe):
dict(name='a', attrs={'class':'AvisoComentario'}),
dict(name='div', attrs={'class':'CajaAvisoComentario'}),
dict(name='div', attrs={'class':'navegaNoticias'}),
dict(name='div', attrs={'class':'Mensaje'}),
dict(name='div', attrs={'id':'PaginadorDiCom'}),
dict(name='div', attrs={'id':'CajaAccesoCuentaUsuario'}),
dict(name='div', attrs={'id':'CintilloComentario'}),
@ -107,3 +112,15 @@ class elperiodicodearagon(BasicNewsRecipe):
(re.compile(r'<p> </p>', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'<p id="">', re.DOTALL|re.IGNORECASE), lambda match: '<p>')
]
# Para sustituir el video incrustado de YouTube por una imagen
def preprocess_html(self, soup):
for video_yt in soup.findAll('iframe',{'title':'YouTube video player'}):
if video_yt:
video_yt.name = 'img'
fuente = video_yt['src']
fuente2 = fuente.replace('http://www.youtube.com/embed/','http://img.youtube.com/vi/')
video_yt['src'] = fuente2 + '/0.jpg'
return soup

View File

@ -0,0 +1,67 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2011, Adrian Gunawan <agunawan at adrnalin.com>'
__author__ = 'Adrian Gunawan'
__version__ = 'v1.0'
__date__ = '02 February 2011'
'''
http://www.thejakartapost.com/
'''
from calibre.web.feeds.news import BasicNewsRecipe
class JakartaPost(BasicNewsRecipe):
title = u'Jakarta Post'
masthead_url = 'http://www.thejakartapost.com/images/jakartapost_logo.jpg'
cover_url = 'http://www.thejakartapost.com/images/jakartapost_logo.jpg'
__author__ = u'Adrian Gunawan'
description = u'Indonesian Newspaper in English from Jakarta Post Online Edition'
category = 'breaking news, national, business, international, Indonesia'
language = 'en_ID'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
no_javascript = True
remove_empty_feeds = True
timefmt = ' [%A, %d %B, %Y]'
encoding = 'utf-8'
keep_only_tags = [dict(name='div', attrs ={'id':'news-main'})]
extra_css = '''
h1{font-family:Georgia,"Times New Roman",Times,serif; font-weight:bold; font-size:large;}
.cT-storyDetails{font-family:Arial,Helvetica,sans-serif; color:#666666;font-size:x-small;}
.articleBody{font-family:Arial,Helvetica,sans-serif; color:black;font-size:small;}
.cT-imageLandscape{font-family:Arial,Helvetica,sans-serif; color:#333333 ;font-size:x-small;}
.source{font-family:Arial,Helvetica,sans-serif; color:#333333 ;font-size:xx-small;}
#content{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
.pageprint{font-family:Arial,Helvetica,sans-serif;font-size:small;}
#bylineDetails{font-family:Arial,Helvetica,sans-serif; color:#666666;font-size:x-small;}
.featurePic-wide{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
#idfeaturepic{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
h3{font-family:Georgia,"Times New Roman",Times,serif; font-size:small;}
h2{font-family:Georgia,"Times New Roman",Times,serif; font-size:small;}
h4{font-family:Georgia,"Times New Roman",Times,serif; font-size:small;}
h5{font-family:Georgia,"Times New Roman",Times,serif; font-size:small;}
body{font-family:Arial,Helvetica,sans-serif; font-size:x-small;}
'''
remove_tags = [
dict(name='div', attrs ={'class':['text-size']}),
]
feeds = [
(u'Breaking News', u'http://www.thejakartapost.com/breaking/feed'),
(u'National', u'http://www.thejakartapost.com/channel/national/feed'),
(u'Archipelago', u'http://www.thejakartapost.com/channel/archipelago/feed'),
(u'Business', u'http://www.thejakartapost.com/channel/business/feed'),
(u'Jakarta', u'http://www.thejakartapost.com/channel/jakarta/feed'),
(u'World', u'http://www.thejakartapost.com/channel/world/feed'),
(u'Sports', u'http://www.thejakartapost.com/channel/sports/feed'),
]

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2011, Adrian Gunawan <agunawan at adrnalin.com>'
__author__ = 'Adrian Gunawan'
__version__ = 'v1.0'
__date__ = '02 February 2011'
'''
http://www.kompas.com/
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class Kompas(BasicNewsRecipe):
title = u'Kompas'
masthead_url = 'http://stat.k.kidsklik.com/data/2k10/kompascom2011/images/logo_kompas.png'
cover_url = 'http://stat.k.kidsklik.com/data/2k10/kompascom2011/images/logo_kompas.png'
__author__ = u'Adrian Gunawan'
description = u'Indonesian News from Kompas Online Edition'
category = 'local news, international, business, Indonesia'
language = 'id'
oldest_article = 5
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
no_javascript = True
remove_empty_feeds = True
timefmt = ' [%A, %d %B, %Y]'
encoding = 'utf-8'
keep_only_tags = [dict(name='div', attrs ={'class':'content_kiri_detail'})]
extra_css = '''
h1{font-family:Georgia,"Times New Roman",Times,serif; font-weight:bold; font-size:large;}
.cT-storyDetails{font-family:Arial,Helvetica,sans-serif; color:#666666;font-size:x-small;}
.articleBody{font-family:Arial,Helvetica,sans-serif; color:black;font-size:small;}
.cT-imageLandscape{font-family:Arial,Helvetica,sans-serif; color:#333333 ;font-size:x-small;}
.source{font-family:Arial,Helvetica,sans-serif; color:#333333 ;font-size:xx-small;}
#content{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
.pageprint{font-family:Arial,Helvetica,sans-serif;font-size:small;}
#bylineDetails{font-family:Arial,Helvetica,sans-serif; color:#666666;font-size:x-small;}
.featurePic-wide{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
#idfeaturepic{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
h3{font-family:Georgia,"Times New Roman",Times,serif; font-size:small;}
h2{font-family:Georgia,"Times New Roman",Times,serif; font-size:small;}
h4{font-family:Georgia,"Times New Roman",Times,serif; font-size:small;}
h5{font-family:Georgia,"Times New Roman",Times,serif; font-size:small;}
body{font-family:Arial,Helvetica,sans-serif; font-size:x-small;}
'''
remove_tags = [
dict(name='div', attrs ={'class':['c_biru_kompas2011', 'c_abu01_kompas2011', 'c_abu_01_kompas2011', 'right', 'clearit']}),
dict(name='div', attrs ={'id':['comment_list', 'comment_paging', 'share']}),
dict(name='form'),
dict(name='ul'),
]
preprocess_regexps = [
(re.compile(r'<!--TERKAIT -->.*<!--TERKAIT END -->', re.DOTALL|re.IGNORECASE),lambda match: ''),
(re.compile(r'<strong>Sent Using.*</body>', re.DOTALL|re.IGNORECASE),lambda match: ''),
(re.compile(r'<strong>Kirim Komentar Anda</strong>', re.DOTALL|re.IGNORECASE),lambda match: ''),
(re.compile(r'<a[^>]*>Kembali ke Index Topik Pilihan</a>', re.DOTALL|re.IGNORECASE),lambda match: ''),
]
feeds = [
(u'Nasional', u'http://www.kompas.com/getrss/nasional'),
(u'Regional', u'http://www.kompas.com/getrss/regional'),
(u'Internasional', u'http://www.kompas.com/getrss/internasional'),
(u'Megapolitan', u'http://www.kompas.com/getrss/megapolitan'),
(u'Bisnis Keuangan', u'http://www.kompas.com/getrss/bisniskeuangan'),
(u'Kesehatan', u'http://www.kompas.com/getrss/kesehatan'),
(u'Olahraga', u'http://www.kompas.com/getrss/olahraga'),
]

View File

@ -0,0 +1,61 @@
__license__ = 'GPL v3'
__copyright__ = '2011, Darko Miletic <darko.miletic at gmail.com>'
'''
njuz.net
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class NjuzNet(BasicNewsRecipe):
title = 'Njuz.net'
__author__ = 'Darko Miletic'
description = 'Iscasene vesti iz Srbije, regiona i sveta'
publisher = 'njuz.net'
category = 'news, politics, humor, Serbia'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'utf8'
language = 'sr'
publication_type = 'newsportal'
masthead_url = 'http://www.njuz.net/njuznet.jpg'
extra_css = """
@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
body{font-family: serif1, serif}
.articledescription{font-family: serif1, serif}
.wp-caption-text{font-size: x-small}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
keep_only_tags = [
dict(attrs={'id':'entryMeta'})
,dict(attrs={'class':'post'})
]
remove_tags = [
dict(name=['embed','link','base','iframe','object','meta','fb:like'])
,dict(name='div', attrs={'id':'tagsandcats'})
]
remove_tags_after= dict(name='div', attrs={'id':'tagsandcats'})
remove_attributes= ['lang']
feeds = [(u'Clanci', u'http://www.njuz.net/feed/')]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return soup

View File

@ -27,7 +27,7 @@ class TazDigiabo(BasicNewsRecipe):
}
def build_index(self):
domain = "http://www.taz.de"
domain = "http://dl.taz.de"
url = domain + "/epub/"

View File

@ -0,0 +1,33 @@
__license__ = 'GPL v3'
__copyright__ = '2011, Darko Miletic <darko.miletic at gmail.com>'
'''
blogs.tedneward.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class InteroperabilityHappens(BasicNewsRecipe):
title = 'Interoperability Happens'
__author__ = 'Darko Miletic'
description = 'Tech blog by Ted Neward'
oldest_article = 15
max_articles_per_feed = 100
language = 'en'
encoding = 'utf-8'
no_stylesheets = True
use_embedded_content = True
publication_type = 'blog'
extra_css = """
body{font-family: Verdana,Arial,Helvetica,sans-serif}
"""
conversion_options = {
'comment' : description
, 'tags' : 'blog, technology, microsoft, programming, C#, Java'
, 'publisher': 'Ted Neward'
, 'language' : language
}
feeds = [(u'Posts', u'http://blogs.tedneward.com/SyndicationService.asmx/GetRss')]

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '4 February 2011, desUBIKado'
__author__ = 'desUBIKado'
__version__ = 'v0.05'
__date__ = '9, February 2011'
'''
http://www.weblogssl.com/
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class weblogssl(BasicNewsRecipe):
__author__ = 'desUBIKado'
description = u'Weblogs colectivos dedicados a seguir la actualidad sobre tecnologia, entretenimiento, estilos de vida, motor, deportes y economia.'
title = u'Weblogs SL (Xataka, Genbeta, VidaExtra, Blog de Cine y otros)'
publisher = 'Weblogs SL'
category = 'Gadgets, Tech news, Product reviews, mobiles, science, cinema, entertainment, culture, tv, food, recipes, life style, motor, F1, sports, economy'
language = 'es'
timefmt = '[%a, %d %b, %Y]'
oldest_article = 1.5
max_articles_per_feed = 100
encoding = 'utf-8'
use_embedded_content = False
remove_empty_feeds = True
remove_javascript = True
no_stylesheets = True
# Si no se quiere recuperar todos los blogs se puede suprimir la descarga del que se desee poniendo
# un caracter # por delante, es decir, # (u'Applesfera', u'http://feeds.weblogssl.com/applesfera'),
# haría que no se descargase Applesfera. OJO: El último feed no debe llevar la coma al final
feeds = [
(u'Xataka', u'http://feeds.weblogssl.com/xataka2'),
(u'Xataka M\xf3vil', u'http://feeds.weblogssl.com/xatakamovil'),
(u'Xataka Android', u'http://feeds.weblogssl.com/xatakandroid'),
(u'Xataka Foto', u'http://feeds.weblogssl.com/xatakafoto'),
(u'Xataka ON', u'http://feeds.weblogssl.com/xatakaon'),
(u'Xataka Ciencia', u'http://feeds.weblogssl.com/xatakaciencia'),
(u'Genbeta', u'http://feeds.weblogssl.com/genbeta'),
(u'Applesfera', u'http://feeds.weblogssl.com/applesfera'),
(u'Vida Extra', u'http://feeds.weblogssl.com/vidaextra'),
(u'Naci\xf3n Red', u'http://feeds.weblogssl.com/nacionred'),
(u'Blog de Cine', u'http://feeds.weblogssl.com/blogdecine'),
(u'Vaya tele', u'http://feeds.weblogssl.com/vayatele2'),
(u'Hipers\xf3nica', u'http://feeds.weblogssl.com/hipersonica'),
(u'Diario del viajero', u'http://feeds.weblogssl.com/diariodelviajero'),
(u'Papel en blanco', u'http://feeds.weblogssl.com/papelenblanco'),
(u'Pop rosa', u'http://feeds.weblogssl.com/poprosa'),
(u'Zona FandoM', u'http://feeds.weblogssl.com/zonafandom'),
(u'Fandemia', u'http://feeds.weblogssl.com/fandemia'),
(u'Noctamina', u'http://feeds.weblogssl.com/noctamina'),
(u'Tendencias', u'http://feeds.weblogssl.com/trendencias'),
(u'Beb\xe9s y m\xe1s', u'http://feeds.weblogssl.com/bebesymas'),
(u'Directo al paladar', u'http://feeds.weblogssl.com/directoalpaladar'),
(u'Compradicci\xf3n', u'http://feeds.weblogssl.com/compradiccion'),
(u'Decoesfera', u'http://feeds.weblogssl.com/decoesfera'),
(u'Embelezzia', u'http://feeds.weblogssl.com/embelezzia'),
(u'Vit\xf3nica', u'http://feeds.weblogssl.com/vitonica'),
(u'Ambiente G', u'http://feeds.weblogssl.com/ambienteg'),
(u'Arrebatadora', u'http://feeds.weblogssl.com/arrebatadora'),
(u'Mensencia', u'http://feeds.weblogssl.com/mensencia'),
(u'Peques y m\xe1s', u'http://feeds.weblogssl.com/pequesymas'),
(u'Motorpasi\xf3n', u'http://feeds.weblogssl.com/motorpasion'),
(u'Motorpasi\xf3n F1', u'http://feeds.weblogssl.com/motorpasionf1'),
(u'Motorpasi\xf3n Moto', u'http://feeds.weblogssl.com/motorpasionmoto'),
(u'Notas de futbol', u'http://feeds.weblogssl.com/notasdefutbol'),
(u'Fuera de l\xedmites', u'http://feeds.weblogssl.com/fueradelimites'),
(u'Salir a ganar', u'http://feeds.weblogssl.com/saliraganar'),
(u'El blog salm\xf3n', u'http://feeds.weblogssl.com/elblogsalmon2'),
(u'Pymes y aut\xf3nomos', u'http://feeds.weblogssl.com/pymesyautonomos'),
(u'Tecnolog\xeda Pyme', u'http://feeds.weblogssl.com/tecnologiapyme'),
(u'Ahorro diario', u'http://feeds.weblogssl.com/ahorrodiario')
]
keep_only_tags = [dict(name='div', attrs={'id':'infoblock'}),
dict(name='div', attrs={'class':'post'}),
dict(name='div', attrs={'id':'blog-comments'})
]
remove_tags = [dict(name='div', attrs={'id':'comment-nav'})]
def print_version(self, url):
return url.replace('http://www.', 'http://m.')
preprocess_regexps = [
# Para poner una linea en blanco entre un comentario y el siguiente
(re.compile(r'<li id="c', re.DOTALL|re.IGNORECASE), lambda match: '<br><br><li id="c')
]
# Para sustituir el video incrustado de YouTube por una imagen
def preprocess_html(self, soup):
for video_yt in soup.findAll('iframe',{'title':'YouTube video player'}):
if video_yt:
video_yt.name = 'img'
fuente = video_yt['src']
fuente2 = fuente.replace('http://www.youtube.com/embed/','http://img.youtube.com/vi/')
fuente3 = fuente2.replace('?rel=0','')
video_yt['src'] = fuente3 + '/0.jpg'
return soup

View File

@ -497,7 +497,7 @@ from calibre.devices.binatone.driver import README
from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK
from calibre.devices.edge.driver import EDGE
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, \
SOVOS, PICO, SUNSTECH_EB700, ARCHOS7O
SOVOS, PICO, SUNSTECH_EB700, ARCHOS7O, STASH
from calibre.devices.sne.driver import SNE
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, Q600, LUMIREAD, ALURATEK_COLOR, \
@ -605,9 +605,8 @@ plugins += [
ELONEX,
TECLAST_K3,
NEWSMY,
PICO, SUNSTECH_EB700, ARCHOS7O,
PICO, SUNSTECH_EB700, ARCHOS7O, SOVOS, STASH,
IPAPYRUS,
SOVOS,
EDGE,
SNE,
ALEX,

View File

@ -65,6 +65,9 @@ class ANDROID(USBMS):
# Huawei
0x45e : { 0x00e1 : [0x007], },
# T-Mobile
0x0408 : { 0x03ba : [0x0109], },
}
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
@ -74,13 +77,13 @@ class ANDROID(USBMS):
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS',
'TELECHIP', 'HUAWEI', ]
'TELECHIP', 'HUAWEI', 'T-MOBILE', ]
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
'IDEOS_TABLET']
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
'A70S', 'A101IT']

View File

@ -15,7 +15,7 @@ from calibre.ebooks.pdb.header import PdbHeaderReader
class APNXBuilder(object):
'''
Currently uses the EPUB 1024 byte count equal one page formula.
Currently uses the Adobe 1024 byte count equal one page formula.
'''
def write_apnx(self, mobi_file_path, apnx_path):

View File

@ -3,6 +3,7 @@
__license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
import os
from cStringIO import StringIO
from struct import unpack

View File

@ -13,7 +13,6 @@ import datetime, os, re, sys, json, hashlib
from calibre.devices.kindle.apnx import APNXBuilder
from calibre.devices.kindle.bookmark import Bookmark
from calibre.devices.usbms.driver import USBMS
from calibre.ebooks.oeb.base import OPF
'''
Notes on collections:
@ -221,7 +220,9 @@ class KINDLE2(KINDLE):
try:
apnx_builder.write_apnx(filepath, apnx_path)
except:
pass
print 'Failed to generate APNX'
import traceback
traceback.print_exc()
class KINDLE_DX(KINDLE2):

View File

@ -92,3 +92,15 @@ class SUNSTECH_EB700(TECLAST_K3):
VENDOR_NAME = 'SUNEB700'
WINDOWS_MAIN_MEM = 'USB-MSC'
class STASH(TECLAST_K3):
name = 'Stash device interface'
gui_name = 'Stash'
description = _('Communicate with the Stash W950 reader.')
FORMATS = ['epub', 'fb2', 'lrc', 'pdb', 'html', 'fb2', 'wtxt',
'txt', 'pdf']
VENDOR_NAME = 'STASH'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'W950'

View File

@ -342,11 +342,9 @@ class HeuristicProcessor(object):
return content
def txt_process(self, match):
from calibre.ebooks.txt.processor import convert_basic, preserve_spaces, \
separate_paragraphs_single_line
from calibre.ebooks.txt.processor import convert_basic, separate_paragraphs_single_line
content = match.group('text')
content = separate_paragraphs_single_line(content)
content = preserve_spaces(content)
content = convert_basic(content, epub_split_size_kb=0)
return content
@ -356,6 +354,8 @@ class HeuristicProcessor(object):
self.log.debug("Running Text Processing")
outerhtml = re.compile(r'.*?(?<=<pre>)(?P<text>.*?)</pre>', re.IGNORECASE|re.DOTALL)
html = outerhtml.sub(self.txt_process, html)
from calibre.ebooks.conversion.preprocess import convert_entities
html = re.sub(r'&(\S+?);', convert_entities, html)
else:
# Add markup naively
# TODO - find out if there are cases where there are more than one <pre> tag or

View File

@ -216,8 +216,8 @@ class EPUBOutput(OutputFormatPlugin):
encryption = self.encrypt_fonts(encrypted_fonts, tdir, uuid)
from calibre.ebooks.epub import initialize_container
epub = initialize_container(output_path, os.path.basename(opf),
extra_entries=extra_entries)
with initialize_container(output_path, os.path.basename(opf),
extra_entries=extra_entries) as epub:
epub.add_dir(tdir)
if encryption is not None:
epub.writestr('META-INF/encryption.xml', encryption)
@ -225,12 +225,13 @@ class EPUBOutput(OutputFormatPlugin):
epub.writestr('META-INF/metadata.xml',
metadata_xml.encode('utf-8'))
if opts.extract_to is not None:
from calibre.utils.zipfile import ZipFile
if os.path.exists(opts.extract_to):
shutil.rmtree(opts.extract_to)
os.mkdir(opts.extract_to)
epub.extractall(path=opts.extract_to)
with ZipFile(output_path) as zf:
zf.extractall(path=opts.extract_to)
self.log.info('EPUB extracted to', opts.extract_to)
epub.close()
def encrypt_fonts(self, uris, tdir, uuid): # {{{
from binascii import unhexlify

View File

@ -37,13 +37,12 @@ class LITInput(InputFormatPlugin):
body = body[0]
if len(body) == 1 and body[0].tag == XHTML('pre'):
pre = body[0]
from calibre.ebooks.txt.processor import convert_basic, preserve_spaces, \
from calibre.ebooks.txt.processor import convert_basic, \
separate_paragraphs_single_line
from calibre.ebooks.chardet import xml_to_unicode
from lxml import etree
import copy
html = separate_paragraphs_single_line(pre.text)
html = preserve_spaces(html)
html = convert_basic(html).replace('<html>',
'<html xmlns="%s">'%XHTML_NS)
html = xml_to_unicode(html, strip_encoding_pats=True,

View File

@ -129,9 +129,6 @@ class Metadata(object):
val = NULL_VALUES.get(field, None)
_data[field] = val
elif field in _data['user_metadata'].iterkeys():
if _data['user_metadata'][field]['datatype'] == 'composite':
_data['user_metadata'][field]['#value#'] = None
else:
_data['user_metadata'][field]['#value#'] = val
_data['user_metadata'][field]['#extra#'] = extra
else:

View File

@ -39,6 +39,13 @@ def asfloat(value):
return 0.0
return float(value)
def isspace(text):
if not text:
return True
if u'\xa0' in text:
return False
return text.isspace()
class BlockState(object):
def __init__(self, body):
self.body = body
@ -438,7 +445,7 @@ class MobiMLizer(object):
if elem.text:
if istate.preserve:
text = elem.text
elif len(elem) > 0 and elem.text.isspace():
elif len(elem) > 0 and isspace(elem.text):
text = None
else:
text = COLLAPSE.sub(' ', elem.text)
@ -481,7 +488,7 @@ class MobiMLizer(object):
if child.tail:
if istate.preserve:
tail = child.tail
elif bstate.para is None and child.tail.isspace():
elif bstate.para is None and isspace(child.tail):
tail = None
else:
tail = COLLAPSE.sub(' ', child.tail)

View File

@ -103,6 +103,8 @@ class OEBReader(object):
data = self.oeb.container.read(None)
data = self.oeb.decode(data)
data = XMLDECL_RE.sub('', data)
data = data.replace('http://openebook.org/namespaces/oeb-package/1.0',
OPF1_NS)
try:
opf = etree.fromstring(data)
except etree.XMLSyntaxError:

View File

@ -237,6 +237,7 @@ class ChooseLibraryAction(InterfaceAction):
return
self.stats.rename(location, newloc)
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
def delete_requested(self, name, location):
loc = location.replace('/', os.sep)
@ -253,6 +254,7 @@ class ChooseLibraryAction(InterfaceAction):
pass
self.stats.remove(location)
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
def backup_status(self, location):
dirty_text = 'no'
@ -329,6 +331,7 @@ class ChooseLibraryAction(InterfaceAction):
' libraries.')%loc, show=True)
self.stats.remove(location)
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
return
prefs['library_path'] = loc
@ -371,9 +374,20 @@ class ChooseLibraryAction(InterfaceAction):
if not self.change_library_allowed():
return
from calibre.gui2.dialogs.choose_library import ChooseLibrary
self.gui.library_view.save_state()
db = self.gui.library_view.model().db
c = ChooseLibrary(db, self.gui.library_moved, self.gui)
location = self.stats.canonicalize_path(db.library_path)
self.pre_choose_dialog_location = location
c = ChooseLibrary(db, self.choose_library_callback, self.gui)
c.exec_()
self.choose_dialog_library_renamed = getattr(c, 'library_renamed', False)
def choose_library_callback(self, newloc, copy_structure=False):
self.gui.library_moved(newloc, copy_structure=copy_structure)
if getattr(self, 'choose_dialog_library_renamed', False):
self.stats.rename(self.pre_choose_dialog_location, prefs['library_path'])
self.build_menus()
self.gui.iactions['Copy To Library'].build_menus()
def change_library_allowed(self):
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):

View File

@ -11,7 +11,7 @@ from PyQt4.Qt import QLineEdit, QAbstractListModel, Qt, \
from calibre.utils.icu import sort_key, lower
from calibre.gui2 import NONE
from calibre.gui2.widgets import EnComboBox
from calibre.gui2.widgets import EnComboBox, LineEditECM
class CompleteModel(QAbstractListModel):
@ -38,7 +38,7 @@ class CompleteModel(QAbstractListModel):
return NONE
class MultiCompleteLineEdit(QLineEdit):
class MultiCompleteLineEdit(QLineEdit, LineEditECM):
'''
A line edit that completes on multiple items separated by a
separator. Use the :meth:`update_items_cache` to set the list of

View File

@ -1026,6 +1026,20 @@ class DeviceMixin(object): # {{{
self.location_manager.free[1] : 'carda',
self.location_manager.free[2] : 'cardb' }
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
try:
total_size = sum([os.stat(f).st_size for f in files])
except:
try:
import traceback
traceback.print_exc()
except:
pass
total_size = self.location_manager.free[0]
if self.location_manager.free[0] > total_size + (1024**2):
# Send news to main memory if enough space available
# as some devices like the Nook Color cannot handle
# periodicals on SD cards properly
on_card = None
self.upload_books(files, names, metadata,
on_card=on_card,
memory=[files, remove])

View File

@ -71,6 +71,8 @@ class ChooseLibrary(QDialog, Ui_Dialog):
prefs['library_path'] = loc
self.callback(loc, copy_structure=self.copy_structure.isChecked())
else:
self.db.prefs.disable_setting = True
self.library_renamed = True
move_library(self.db.library_path, loc, self.parent(),
self.callback)

View File

@ -109,7 +109,7 @@ class PluginModel(QAbstractItemModel, SearchQueryParser): # {{{
return self.index(ans[0], 0, QModelIndex()) if ans[1] < 0 else \
self.index(ans[1], 0, self.index(ans[0], 0, QModelIndex()))
def index(self, row, column, parent):
def index(self, row, column, parent=QModelIndex()):
if not self.hasIndex(row, column, parent):
return QModelIndex()
@ -165,8 +165,6 @@ class PluginModel(QAbstractItemModel, SearchQueryParser): # {{{
def flags(self, index):
if not index.isValid():
return 0
if index.internalId() == 0:
return Qt.ItemIsEnabled
flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
return flags

View File

@ -5,37 +5,316 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import textwrap
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, AbortCommit
from calibre.gui2.preferences.tweaks_ui import Ui_Form
from calibre.gui2 import error_dialog
from calibre.gui2 import error_dialog, NONE
from calibre.utils.config import read_raw_tweaks, write_tweaks
from calibre.gui2.widgets import PythonHighlighter
from calibre import isbytestring
from PyQt4.Qt import QAbstractListModel, Qt, QStyledItemDelegate, QStyle, \
QStyleOptionViewItem, QFont, QDialogButtonBox, QDialog, \
QVBoxLayout, QPlainTextEdit, QLabel
class Delegate(QStyledItemDelegate): # {{{
def __init__(self, view):
QStyledItemDelegate.__init__(self, view)
self.view = view
def paint(self, p, opt, idx):
copy = QStyleOptionViewItem(opt)
copy.showDecorationSelected = True
if self.view.currentIndex() == idx:
copy.state |= QStyle.State_HasFocus
QStyledItemDelegate.paint(self, p, copy, idx)
# }}}
class Tweak(object): # {{{
def __init__(self, name, doc, var_names, defaults, custom):
self.name = name
self.doc = doc.strip()
self.var_names = var_names
self.default_values = {}
for x in var_names:
self.default_values[x] = defaults[x]
self.custom_values = {}
for x in var_names:
if x in custom:
self.custom_values[x] = custom[x]
def __str__(self):
ans = ['#: ' + self.name]
for line in self.doc.splitlines():
if line:
ans.append('# ' + line)
for key, val in self.default_values.iteritems():
val = self.custom_values.get(key, val)
ans.append('%s = %r'%(key, val))
ans = '\n'.join(ans)
if isinstance(ans, unicode):
ans = ans.encode('utf-8')
return ans
def __cmp__(self, other):
return cmp(self.is_customized, getattr(other, 'is_customized', False))
@property
def is_customized(self):
for x, val in self.default_values.iteritems():
if self.custom_values.get(x, val) != val:
return True
return False
@property
def edit_text(self):
ans = ['# %s'%self.name]
for x, val in self.default_values.iteritems():
val = self.custom_values.get(x, val)
ans.append('%s = %r'%(x, val))
return '\n\n'.join(ans)
def restore_to_default(self):
self.custom_values.clear()
def update(self, varmap):
self.custom_values.update(varmap)
# }}}
class Tweaks(QAbstractListModel): # {{{
def __init__(self, parent=None):
QAbstractListModel.__init__(self, parent)
raw_defaults, raw_custom = read_raw_tweaks()
self.parse_tweaks(raw_defaults, raw_custom)
def rowCount(self, *args):
return len(self.tweaks)
def data(self, index, role):
row = index.row()
try:
tweak = self.tweaks[row]
except:
return NONE
if role == Qt.DisplayRole:
return textwrap.fill(tweak.name, 40)
if role == Qt.FontRole and tweak.is_customized:
ans = QFont()
ans.setBold(True)
return ans
if role == Qt.ToolTipRole:
tt = _('This tweak has it default value')
if tweak.is_customized:
tt = _('This tweak has been customized')
return tt
if role == Qt.UserRole:
return tweak
return NONE
def parse_tweaks(self, defaults, custom):
l, g = {}, {}
try:
exec custom in g, l
except:
print 'Failed to load custom tweaks file'
import traceback
traceback.print_exc()
dl, dg = {}, {}
exec defaults in dg, dl
lines = defaults.splitlines()
pos = 0
self.tweaks = []
while pos < len(lines):
line = lines[pos]
if line.startswith('#:'):
pos = self.read_tweak(lines, pos, dl, l)
pos += 1
default_keys = set(dl.iterkeys())
custom_keys = set(l.iterkeys())
self.plugin_tweaks = {}
for key in custom_keys - default_keys:
self.plugin_tweaks[key] = l[key]
def read_tweak(self, lines, pos, defaults, custom):
name = lines[pos][2:].strip()
doc, var_names = [], []
while True:
pos += 1
line = lines[pos]
if not line.startswith('#'):
break
doc.append(line[1:].strip())
doc = '\n'.join(doc)
while True:
line = lines[pos]
if not line.strip():
break
spidx1 = line.find(' ')
spidx2 = line.find('=')
spidx = spidx1 if spidx1 > 0 and (spidx2 == 0 or spidx2 > spidx1) else spidx2
if spidx > 0:
var = line[:spidx]
if var not in defaults:
raise ValueError('%r not in default tweaks dict'%var)
var_names.append(var)
pos += 1
if not var_names:
raise ValueError('Failed to find any variables for %r'%name)
self.tweaks.append(Tweak(name, doc, var_names, defaults, custom))
#print '\n\n', self.tweaks[-1]
return pos
def restore_to_default(self, idx):
tweak = self.data(idx, Qt.UserRole)
if tweak is not NONE:
tweak.restore_to_default()
self.dataChanged.emit(idx, idx)
def restore_to_defaults(self):
for r in range(self.rowCount()):
self.restore_to_default(self.index(r))
def update_tweak(self, idx, varmap):
tweak = self.data(idx, Qt.UserRole)
if tweak is not NONE:
tweak.update(varmap)
self.dataChanged.emit(idx, idx)
def to_string(self):
ans = ['#!/usr/bin/env python',
'# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai', '',
'# This file was automatically generated by calibre, do not'
' edit it unless you know what you are doing.', '',
]
for tweak in self.tweaks:
ans.extend(['', str(tweak), ''])
if self.plugin_tweaks:
ans.extend(['', '',
'# The following are tweaks for installed plugins', ''])
for key, val in self.plugin_tweaks.iteritems():
ans.extend(['%s = %r'%(key, val), '', ''])
return '\n'.join(ans)
@property
def plugin_tweaks_string(self):
ans = []
for key, val in self.plugin_tweaks.iteritems():
ans.extend(['%s = %r'%(key, val), '', ''])
ans = '\n'.join(ans)
if isbytestring(ans):
ans = ans.decode('utf-8')
return ans
def set_plugin_tweaks(self, d):
self.plugin_tweaks = d
# }}}
class PluginTweaks(QDialog): # {{{
def __init__(self, raw, parent=None):
QDialog.__init__(self, parent)
self.edit = QPlainTextEdit(self)
self.highlighter = PythonHighlighter(self.edit.document())
self.l = QVBoxLayout()
self.setLayout(self.l)
self.msg = QLabel(
_('Add/edit tweaks for any custom plugins you have installed. '
'Documentation for these tweaks should be available '
'on the website from where you downloaded the plugins.'))
self.msg.setWordWrap(True)
self.l.addWidget(self.msg)
self.l.addWidget(self.edit)
self.edit.setPlainText(raw)
self.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel,
Qt.Horizontal, self)
self.bb.accepted.connect(self.accept)
self.bb.rejected.connect(self.reject)
self.l.addWidget(self.bb)
self.resize(550, 300)
# }}}
class ConfigWidget(ConfigWidgetBase, Ui_Form):
def genesis(self, gui):
self.gui = gui
self.current_tweaks.textChanged.connect(self.changed)
self.delegate = Delegate(self.tweaks_view)
self.tweaks_view.setItemDelegate(self.delegate)
self.tweaks_view.currentChanged = self.current_changed
self.highlighter = PythonHighlighter(self.edit_tweak.document())
self.restore_default_button.clicked.connect(self.restore_to_default)
self.apply_button.clicked.connect(self.apply_tweak)
self.plugin_tweaks_button.clicked.connect(self.plugin_tweaks)
def plugin_tweaks(self):
raw = self.tweaks.plugin_tweaks_string
d = PluginTweaks(raw, self)
if d.exec_() == d.Accepted:
g, l = {}, {}
try:
exec unicode(d.edit.toPlainText()) in g, l
except:
import traceback
return error_dialog(self, _('Failed'),
_('There was a syntax error in your tweak. Click '
'the show details button for details.'), show=True,
det_msg=traceback.format_exc())
self.tweaks.set_plugin_tweaks(l)
self.changed()
def current_changed(self, current, previous):
tweak = self.tweaks.data(current, Qt.UserRole)
self.help.setPlainText(tweak.doc)
self.edit_tweak.setPlainText(tweak.edit_text)
def changed(self, *args):
self.changed_signal.emit()
def initialize(self):
deft, curt = read_raw_tweaks()
self.current_tweaks.blockSignals(True)
self.current_tweaks.setPlainText(curt.decode('utf-8'))
self.current_tweaks.blockSignals(False)
self.tweaks = Tweaks()
self.tweaks_view.setModel(self.tweaks)
self.default_tweaks.setPlainText(deft.decode('utf-8'))
def restore_to_default(self, *args):
idx = self.tweaks_view.currentIndex()
if idx.isValid():
self.tweaks.restore_to_default(idx)
tweak = self.tweaks.data(idx, Qt.UserRole)
self.edit_tweak.setPlainText(tweak.edit_text)
self.changed()
def restore_defaults(self):
ConfigWidgetBase.restore_defaults(self)
deft, curt = read_raw_tweaks()
self.current_tweaks.setPlainText(deft.decode('utf-8'))
self.tweaks.restore_to_defaults()
self.changed()
def apply_tweak(self):
idx = self.tweaks_view.currentIndex()
if idx.isValid():
l, g = {}, {}
try:
exec unicode(self.edit_tweak.toPlainText()) in g, l
except:
import traceback
error_dialog(self.gui, _('Failed'),
_('There was a syntax error in your tweak. Click '
'the show details button for details.'),
det_msg=traceback.format_exc(), show=True)
return
self.tweaks.update_tweak(idx, l)
self.changed()
def commit(self):
raw = unicode(self.current_tweaks.toPlainText()).encode('utf-8')
raw = self.tweaks.to_string()
try:
exec raw
except:
@ -54,5 +333,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
if __name__ == '__main__':
from PyQt4.Qt import QApplication
app = QApplication([])
#Tweaks()
#test_widget
test_widget('Advanced', 'Tweaks')

View File

@ -7,17 +7,19 @@
<x>0</x>
<y>0</y>
<width>660</width>
<height>351</height>
<height>531</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" rowspan="2">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Values for the tweaks are shown below. Edit them to change the behavior of calibre. Your changes will only take effect after a restart of calibre.</string>
<string>Values for the tweaks are shown below. Edit them to change the behavior of calibre. Your changes will only take effect &lt;b&gt;after a restart&lt;/b&gt; of calibre.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -25,13 +27,53 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>All available tweaks</string>
<widget class="QListView" name="tweaks_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="spacing">
<number>5</number>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="plugin_tweaks_button">
<property name="toolTip">
<string>Edit tweaks for any custom plugins you have installed</string>
</property>
<property name="text">
<string>&amp;Plugin tweaks</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Help</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPlainTextEdit" name="help">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<layout class="QGridLayout" name="gridLayout_11">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="default_tweaks">
<property name="readOnly">
<bool>true</bool>
</property>
@ -40,14 +82,38 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_7">
<item row="1" column="1">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>&amp;Current tweaks</string>
<string>Edit tweak</string>
</property>
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="current_tweaks"/>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QPlainTextEdit" name="edit_tweak">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="restore_default_button">
<property name="toolTip">
<string>Restore this tweak to its default value</string>
</property>
<property name="text">
<string>Restore &amp;default</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="apply_button">
<property name="toolTip">
<string>Apply any changes you made to this tweak</string>
</property>
<property name="text">
<string>&amp;Apply</string>
</property>
</widget>
</item>
</layout>
</widget>

View File

@ -440,16 +440,17 @@ class Document(QWebPage): # {{{
@property
def height(self):
j = self.javascript('document.body.offsetHeight', 'int')
# Note that document.body.offsetHeight does not include top and bottom
# margins on body and in some cases does not include the top margin on
# the first element inside body either. See ticket #8791 for an example
# of the latter.
q = self.mainFrame().contentsSize().height()
if q == j:
return j
if min(j, q) <= 0:
return max(j, q)
window_height = self.window_height
if j == window_height:
return j if q < 1.2*j else q
return j
if q < 0:
# Don't know if this is still needed, but it can't hurt
j = self.javascript('document.body.offsetHeight', 'int')
if j >= 0:
q = j
return q
@property
def width(self):

View File

@ -20,7 +20,7 @@ from calibre.gui2.filename_pattern_ui import Ui_Form
from calibre import fit_image
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.ebooks.metadata.meta import metadata_from_filename
from calibre.utils.config import prefs, XMLConfig
from calibre.utils.config import prefs, XMLConfig, tweaks
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
history = XMLConfig('history')
@ -932,7 +932,7 @@ class SplitterHandle(QSplitterHandle):
def paintEvent(self, ev):
QSplitterHandle.paintEvent(self, ev)
if self.highlight:
if self.highlight and tweaks['draw_hidden_section_indicators']:
painter = QPainter(self)
painter.setClipRect(ev.rect())
painter.fillRect(self.rect(), Qt.yellow)

View File

@ -787,6 +787,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
mi.id = id
for key, meta in self.field_metadata.custom_iteritems():
mi.set_user_metadata(key, meta)
if meta['datatype'] == 'composite':
mi.set(key, val=row[meta['rec_index']])
else:
mi.set(key, val=self.get_custom(idx, label=meta['label'],
index_is_id=index_is_id),
extra=self.get_custom_extra(idx, label=meta['label'],

View File

@ -561,9 +561,10 @@ format, whether input or output are available in the conversion dialog under the
Convert Microsoft Word documents
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|app| does not directly convert .doc files from Microsoft Word. However, in Word, you can save the document
|app| does not directly convert .doc/.docx files from Microsoft Word. However, in Word, you can save the document
as HTML and then convert the resulting HTML file with |app|. When saving as HTML, be sure to use the
"Save as Web Page, Filtered" option as this will produce clean HTML that will convert well.
"Save as Web Page, Filtered" option as this will produce clean HTML that will convert well. Note that Word
produces really messy HTML, converting it can take a long time, so be patient.
There is a Word macro package that can automate the conversion of Word documents using |app|. It also makes
generating the Table of Contents much simpler. It is called BookCreator and is available for free

View File

@ -182,11 +182,6 @@ If you don't want to uninstall it altogether, there are a couple of tricks you c
simplest is to simply re-name the executable file that launches the library program. More detail
`in the forums <http://www.mobileread.com/forums/showthread.php?t=65809>`_.
Can I use the collections feature of the SONY reader?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|app| has full support for collections. When you add tags to a book's metadata, those tags are turned into collections when you upload the book to the SONY reader. Also, the series information is automatically
turned into a collection on the reader. Note that the PRS-500 does not support collections for books stored on the SD card. The PRS-505 does.
How do I use |app| with my iPad/iPhone/iTouch?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -375,13 +370,6 @@ Content From The Web
:depth: 1
:local:
My downloaded news content causes the reader to reset.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is a bug in the SONY firmware. The problem can be mitigated by switching the output format to EPUB
in the configuration dialog. Alternatively, you can use the LRF output format and use the SONY software
to transfer the files to the reader. The SONY software pre-paginates the LRF file,
thereby reducing the number of resets.
I obtained a recipe for a news site as a .py file from somewhere, how do I use it?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Start the :guilabel:`Add custom news sources` dialog (from the :guilabel:`Fetch news` menu) and click the :guilabel:`Switch to advanced mode` button. Delete everything in the box with the recipe source code and copy paste the contents of your .py file into the box. Click :guilabel:`Add/update recipe`.
@ -391,7 +379,7 @@ I want |app| to download news from my favorite news website.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you are reasonably proficient with computers, you can teach |app| to download news from any website of your choosing. To learn how to do this see :ref:`news`.
Otherwise, you can register a request for a particular news site by adding a comment `to this ticket <http://bugs.calibre-ebook.com/ticket/405>`_.
Otherwise, you can request a particular news site by posting in the `calibre Recipes forum <http://www.mobileread.com/forums/forumdisplay.php?f=228>`_.
Can I use web2disk to download an arbitrary website?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -480,7 +468,7 @@ How do I backup |app|?
The most important thing to backup is the |app| library folder, that contains all your books and metadata. This is the folder you chose for your |app| library when you ran |app| for the first time. You can get the path to the library folder by clicking the |app| icon on the main toolbar. You must backup this complete folder with all its files and sub-folders.
You can switch |app| to using a backed up library folder by simply clicking the |app| icon on the toolbar and choosing your backup library folder.
You can switch |app| to using a backed up library folder by simply clicking the |app| icon on the toolbar and choosing your backup library folder. A backed up library folder backs up your custom columns and saved searches as well as all your books and metadata.
If you want to backup the |app| configuration/plugins, you have to backup the config directory. You can find this config directory via :guilabel:`Preferences->Miscellaneous`. Note that restoring configuration directories is not officially supported, but should work in most cases. Just copy the contents of the backup directory into the current configuration directory to restore.
@ -491,7 +479,7 @@ Most purchased EPUB books have `DRM <http://wiki.mobileread.com/wiki/DRM>`_. Thi
I am getting a "Permission Denied" error?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A permission denied error can occur because of many possible reasons, none of them having anything to do with |app|. You can get permission denied errors if you are using an SD card with write protect enabled. Or if you, or some program you used changed the file permissions of the files in question to read only. Or if there is a filesystem error on the device which caused your operating system to mount the filesystem in read only mode or mark a particular file as read only pending recovery. Or if the files have their owner set to a user other than you. You will need to fix the underlying cause of the permissions error before resuming to use |app|. Read the error message carefully, see what file it points to and fix the permissions on that file.
A permission denied error can occur because of many possible reasons, none of them having anything to do with |app|. You can get permission denied errors if you are using an SD card with write protect enabled. Or if you, or some program you used changed the file permissions of the files in question to read only. Or if there is a filesystem error on the device which caused your operating system to mount the filesystem in read only mode or mark a particular file as read only pending recovery. Or if the files have their owner set to a user other than you. Or if your file is open in another program. You will need to fix the underlying cause of the permissions error before resuming to use |app|. Read the error message carefully, see what file it points to and fix the permissions on that file.
Can I have the comment metadata show up on my reader?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -504,7 +492,7 @@ I want some feature added to |app|. What can I do?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You have two choices:
1. Create a patch by hacking on |app| and send it to me for review and inclusion. See `Development <http://calibre-ebook.com/get-involved>`_.
2. `Open a ticket <http://bugs.calibre-ebook.com/newticket>`_ (you have to register and login first) and hopefully I will find the time to implement your feature.
2. `Open a ticket <http://bugs.calibre-ebook.com/newticket>`_ (you have to register and login first). Remember that |app| development is done by volunteers, so if you get no response to your feature request, it means no one feels like implementing it.
Can I include |app| on a CD to be distributed with my product/magazine?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -522,7 +510,7 @@ Why are there so many calibre-parallel processes on my system?
In addition to this some conversion plugins run tasks in their own pool of processes, so for example if you bulk convert comics, each comic conversion will use three separate processes to render the images. The job manager knows this so it will run only a single comic conversion simultaneously.
And since I'm sure someone will ask: The reason adding/saving books are in separate processes is because of PDF. PDF processing libraries can crash on reading PDFs and I dont want the crash to take down all of calibre. Also when adding EPUB books, in order to extract the cover you have to sometimes render the HTML of the first page, which means that it either has to run the GUI thread of the main process or in a separate process.
And since I'm sure someone will ask: The reason adding/saving books are in separate processes is because of PDF. PDF processing libraries can crash on reading PDFs and I dont want the crash to take down all of calibre. Also when adding EPUB books, in order to extract the cover you have to sometimes render the HTML of the first page, which means that it either has to run in the GUI thread of the main process or in a separate process.
Finally, the reason calibre keep workers alive and idle instead of launching on demand is to workaround the slow startup time of python processes.

File diff suppressed because it is too large Load Diff

View File

@ -107,6 +107,7 @@ _extra_lang_codes = {
'en_CZ' : _('English (Czechoslovakia)'),
'en_PK' : _('English (Pakistan)'),
'en_HR' : _('English (Croatia)'),
'en_ID' : _('English (Indonesia)'),
'en_IL' : _('English (Israel)'),
'en_SG' : _('English (Singapore)'),
'en_YE' : _('English (Yemen)'),