mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
2cdf67776b
@ -10,7 +10,7 @@ you know what you are doing. If you delete this file, it will be recreated from
|
|||||||
defaults.
|
defaults.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
#: Auto increment series index
|
||||||
# The algorithm used to assign a new book in an existing series a series number.
|
# 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
|
# New series numbers assigned using this tweak are always integer values, except
|
||||||
# if a constant non-integer is specified.
|
# if a constant non-integer is specified.
|
||||||
@ -29,7 +29,7 @@ defaults.
|
|||||||
# series_index_auto_increment = 16.5
|
# series_index_auto_increment = 16.5
|
||||||
series_index_auto_increment = 'next'
|
series_index_auto_increment = 'next'
|
||||||
|
|
||||||
|
#: Add separator after completing an author name
|
||||||
# Should the completion separator be append
|
# Should the completion separator be append
|
||||||
# to the end of the completed text to
|
# to the end of the completed text to
|
||||||
# automatically begin a new completion operation
|
# automatically begin a new completion operation
|
||||||
@ -38,6 +38,7 @@ series_index_auto_increment = 'next'
|
|||||||
authors_completer_append_separator = False
|
authors_completer_append_separator = False
|
||||||
|
|
||||||
|
|
||||||
|
#: Author sort name algorithm
|
||||||
# The algorithm used to copy author to author_sort
|
# The algorithm used to copy author to author_sort
|
||||||
# Possible values are:
|
# Possible values are:
|
||||||
# invert: use "fn ln" -> "ln, fn" (the default algorithm)
|
# 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'.
|
# selecting 'manage authors', and pressing 'Recalculate all author sort values'.
|
||||||
author_sort_copy_method = 'invert'
|
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,
|
# 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
|
# 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
|
# 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_sort'
|
||||||
categories_use_field_for_author_name = 'author'
|
categories_use_field_for_author_name = 'author'
|
||||||
|
|
||||||
|
#: Control partitioning of Tag Browser
|
||||||
# When partitioning the tags browser, the format of the subcategory label is
|
# When partitioning the tags browser, the format of the subcategory label is
|
||||||
# controlled by a template: categories_collapsed_name_template if sorting by
|
# controlled by a template: categories_collapsed_name_template if sorting by
|
||||||
# name, categories_collapsed_rating_template if sorting by average rating, and
|
# 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:
|
# author category will be the name of the author. The sub-values available are:
|
||||||
# name: the printable name of the item
|
# name: the printable name of the item
|
||||||
# count: the number of books that references this 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
|
# 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.
|
# 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
|
# 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
|
# (\ characters) in the template. It doesn't hurt anything to leave it there
|
||||||
# even if there aren't any backslashes.
|
# 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_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}'
|
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.
|
# Set whether boolean custom columns are two- or three-valued.
|
||||||
# Two-values for true booleans
|
# Two-values for true booleans
|
||||||
# three-values for yes/no/unknown
|
# three-values for yes/no/unknown
|
||||||
# Set to 'yes' for three-values, 'no' for two-values
|
# Set to 'yes' for three-values, 'no' for two-values
|
||||||
bool_custom_columns_are_tristate = 'yes'
|
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
|
# Provide a set of columns to be sorted on when calibre starts
|
||||||
# The argument is None if saved sort history is to be used
|
# The argument is None if saved sort history is to be used
|
||||||
# otherwise it is a list of column,order pairs. Column is the
|
# 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.
|
# title within authors.
|
||||||
sort_columns_at_startup = None
|
sort_columns_at_startup = None
|
||||||
|
|
||||||
|
#; Control how dates are displayed
|
||||||
# Format to be used for publication date and the timestamp (date).
|
# Format to be used for publication date and the timestamp (date).
|
||||||
# A string controlling how the publication date is displayed in the GUI
|
# A string controlling how the publication date is displayed in the GUI
|
||||||
# d the day as number without a leading zero (1 to 31)
|
# 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_pubdate_display_format = 'MMM yyyy'
|
||||||
gui_timestamp_display_format = 'dd 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.
|
# 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 'library_order', Leading articles such as The and A will be ignored.
|
||||||
# If set to 'strictly_alphabetic', the titles will be sorted without processing
|
# 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.
|
# without changing anything is sufficient to change the sort.
|
||||||
title_series_sorting = 'library_order'
|
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
|
# 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
|
# to device. If set to library_order, leading articles such as The and A will
|
||||||
# be put at the end
|
# be put at the end
|
||||||
@ -140,6 +147,7 @@ title_series_sorting = 'library_order'
|
|||||||
# strictly_alphabetic, it would remain "The Client".
|
# strictly_alphabetic, it would remain "The Client".
|
||||||
save_template_title_series_sorting = 'library_order'
|
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
|
# 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
|
# title sort strings. The list is a regular expression, with the articles
|
||||||
# separated by 'or' bars. Comparisons are case insensitive, and that cannot be
|
# 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+'
|
# Default: '^(A|The|An)\s+'
|
||||||
title_sort_articles=r'^(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
|
# 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
|
# 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
|
# 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 = '/home/dropbox/My Dropbox/someone/library'
|
||||||
auto_connect_to_folder = ''
|
auto_connect_to_folder = ''
|
||||||
|
|
||||||
|
#: Specify renaming rules for SONY collections
|
||||||
# Specify renaming rules for sony collections. This tweak is only applicable if
|
# Specify renaming rules for sony collections. This tweak is only applicable if
|
||||||
# metadata management is set to automatic. Collections on Sonys are named
|
# metadata management is set to automatic. Collections on Sonys are named
|
||||||
# depending upon whether the field is standard or custom. A collection derived
|
# 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_renaming_rules={}
|
||||||
sony_collection_name_template='{value}{category:| (|)}'
|
sony_collection_name_template='{value}{category:| (|)}'
|
||||||
|
|
||||||
|
#: Specify how SONY collections are sorted
|
||||||
# Specify how sony collections are sorted. This tweak is only applicable if
|
# 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
|
# 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
|
# 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 = []
|
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'...] ...}
|
# Syntax: {'new term':['existing term 1', 'term 2', ...], 'new':['old'...] ...}
|
||||||
# Example: create the term 'myseries' that when used as myseries:foo would
|
# Example: create the term 'myseries' that when used as myseries:foo would
|
||||||
# search all of the search categories 'series', '#myseries', and '#myseries2':
|
# search all of the search categories 'series', '#myseries', and '#myseries2':
|
||||||
@ -244,15 +252,17 @@ sony_collection_sorting_rules = []
|
|||||||
grouped_search_terms = {}
|
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
|
# a book' are added when copying books to another library
|
||||||
add_new_book_tags_when_importing_books = False
|
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
|
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_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.
|
# content_server_wont_display is a list of custom fields not to be displayed.
|
||||||
# wont_display has priority over will_display.
|
# wont_display has priority over will_display.
|
||||||
@ -270,13 +280,27 @@ max_content_server_tags_shown=5
|
|||||||
content_server_will_display = ['*']
|
content_server_will_display = ['*']
|
||||||
content_server_wont_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
|
# As above, this tweak affects only display of custom fields. The standard
|
||||||
# fields are not affected
|
# fields are not affected
|
||||||
book_details_will_display = ['*']
|
book_details_will_display = ['*']
|
||||||
book_details_wont_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
|
# 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
|
# library after certain operations such as searches or device insertion. Each
|
||||||
# sort level adds a performance penalty. If the database is large (thousands of
|
# 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.
|
# level sorts, and if you are seeing a slowdown, reduce the value of this tweak.
|
||||||
maximum_resort_levels = 5
|
maximum_resort_levels = 5
|
||||||
|
|
||||||
# Absolute path to a TTF font file to use as the font for the title and author
|
#: Specify which font to use when generating a default cover
|
||||||
# when generating a default cover. Useful if the default font (Liberation
|
# 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.
|
# Serif) does not contain glyphs for the language of the books in your library.
|
||||||
generate_cover_title_font = None
|
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
|
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,
|
# Behavior of doubleclick on the books list. Choices: open_viewer, do_nothing,
|
||||||
# edit_cell, edit_metadata. Selecting edit_metadata has the side effect of
|
# edit_cell, edit_metadata. Selecting edit_metadata has the side effect of
|
||||||
# disabling editing a field using a single click.
|
# disabling editing a field using a single click.
|
||||||
@ -302,7 +324,8 @@ generate_cover_foot_font = None
|
|||||||
doubleclick_on_library_view = 'open_viewer'
|
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
|
# 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.
|
# 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.
|
# 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.
|
# Example: locale_for_sorting = 'nb' -- sort using Norwegian rules.
|
||||||
locale_for_sorting = ''
|
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
|
# 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
|
# metadata one book at a time. If True, then the fields are laid out using two
|
||||||
# columns. If False, one column is used.
|
# columns. If False, one column is used.
|
||||||
metadata_single_use_2_cols_for_custom_fields = True
|
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
|
# The number of seconds to wait before sending emails when using a
|
||||||
# public email server like gmail or hotmail. Default is: 5 minutes
|
# 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,
|
# 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.
|
# calibre.
|
||||||
public_smtp_relay_delay = 301
|
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
|
||||||
|
|
||||||
|
BIN
resources/images/news/njuz_net.png
Normal file
BIN
resources/images/news/njuz_net.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 914 B |
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
__license__ = 'GPL v3'
|
__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
|
b92.net
|
||||||
'''
|
'''
|
||||||
@ -10,7 +10,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
class B92(BasicNewsRecipe):
|
class B92(BasicNewsRecipe):
|
||||||
title = 'B92'
|
title = 'B92'
|
||||||
__author__ = 'Darko Miletic'
|
__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'
|
publisher = 'B92'
|
||||||
category = 'news, politics, Serbia'
|
category = 'news, politics, Serbia'
|
||||||
oldest_article = 2
|
oldest_article = 2
|
||||||
@ -20,7 +20,14 @@ class B92(BasicNewsRecipe):
|
|||||||
encoding = 'cp1250'
|
encoding = 'cp1250'
|
||||||
language = 'sr'
|
language = 'sr'
|
||||||
publication_type = 'newsportal'
|
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 = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
@ -32,22 +39,25 @@ class B92(BasicNewsRecipe):
|
|||||||
|
|
||||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||||
|
|
||||||
keep_only_tags = [dict(name='table', attrs={'class':'maindocument'})]
|
keep_only_tags = [dict(attrs={'class':['article-info1','article-text']})]
|
||||||
|
remove_attributes = ['width','height','align','hspace','vspace','border']
|
||||||
remove_tags = [
|
remove_tags = [dict(name=['embed','link','base','meta'])]
|
||||||
dict(name='ul', attrs={'class':'comment-nav'})
|
|
||||||
,dict(name=['embed','link','base'] )
|
|
||||||
,dict(name='div', attrs={'class':'udokum'} )
|
|
||||||
]
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Vesti' , u'http://www.b92.net/info/rss/vesti.xml' )
|
(u'Vesti' , u'http://www.b92.net/info/rss/vesti.xml' )
|
||||||
,(u'Biz' , u'http://www.b92.net/info/rss/biz.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):
|
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
|
||||||
|
@ -5,8 +5,8 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '04 December 2010, desUBIKado'
|
__copyright__ = '04 December 2010, desUBIKado'
|
||||||
__author__ = 'desUBIKado'
|
__author__ = 'desUBIKado'
|
||||||
__description__ = 'Daily newspaper from Aragon'
|
__description__ = 'Daily newspaper from Aragon'
|
||||||
__version__ = 'v0.05'
|
__version__ = 'v0.07'
|
||||||
__date__ = '07, December 2010'
|
__date__ = '06, February 2011'
|
||||||
'''
|
'''
|
||||||
elperiodicodearagon.com
|
elperiodicodearagon.com
|
||||||
'''
|
'''
|
||||||
@ -38,7 +38,8 @@ class elperiodicodearagon(BasicNewsRecipe):
|
|||||||
,'publisher' : publisher
|
,'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'Internacional', u'http://elperiodicodearagon.com/RSS/4.xml'),
|
||||||
(u'Espa\xf1a', u'http://elperiodicodearagon.com/RSS/3.xml'),
|
(u'Espa\xf1a', u'http://elperiodicodearagon.com/RSS/3.xml'),
|
||||||
(u'Econom\xeda', u'http://elperiodicodearagon.com/RSS/5.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'Opini\xf3n', u'http://elperiodicodearagon.com/RSS/103.xml'),
|
||||||
(u'Escenarios', u'http://elperiodicodearagon.com/RSS/105.xml'),
|
(u'Escenarios', u'http://elperiodicodearagon.com/RSS/105.xml'),
|
||||||
(u'Sociedad', u'http://elperiodicodearagon.com/RSS/104.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 = '''
|
extra_css = '''
|
||||||
h3{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:xx-large;}
|
h3 {font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:30px;}
|
||||||
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
h2 {font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:18px;}
|
||||||
dd{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
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']
|
remove_attributes = ['height','width']
|
||||||
@ -82,6 +86,7 @@ class elperiodicodearagon(BasicNewsRecipe):
|
|||||||
dict(name='a', attrs={'class':'AvisoComentario'}),
|
dict(name='a', attrs={'class':'AvisoComentario'}),
|
||||||
dict(name='div', attrs={'class':'CajaAvisoComentario'}),
|
dict(name='div', attrs={'class':'CajaAvisoComentario'}),
|
||||||
dict(name='div', attrs={'class':'navegaNoticias'}),
|
dict(name='div', attrs={'class':'navegaNoticias'}),
|
||||||
|
dict(name='div', attrs={'class':'Mensaje'}),
|
||||||
dict(name='div', attrs={'id':'PaginadorDiCom'}),
|
dict(name='div', attrs={'id':'PaginadorDiCom'}),
|
||||||
dict(name='div', attrs={'id':'CajaAccesoCuentaUsuario'}),
|
dict(name='div', attrs={'id':'CajaAccesoCuentaUsuario'}),
|
||||||
dict(name='div', attrs={'id':'CintilloComentario'}),
|
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> </p>', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||||
(re.compile(r'<p id="">', re.DOTALL|re.IGNORECASE), lambda match: '<p>')
|
(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
|
||||||
|
67
resources/recipes/jakarta_post.recipe
Normal file
67
resources/recipes/jakarta_post.recipe
Normal 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'),
|
||||||
|
]
|
77
resources/recipes/kompas.recipe
Normal file
77
resources/recipes/kompas.recipe
Normal 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'),
|
||||||
|
]
|
61
resources/recipes/njuz_net.recipe
Normal file
61
resources/recipes/njuz_net.recipe
Normal 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
|
||||||
|
|
@ -27,7 +27,7 @@ class TazDigiabo(BasicNewsRecipe):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def build_index(self):
|
def build_index(self):
|
||||||
domain = "http://www.taz.de"
|
domain = "http://dl.taz.de"
|
||||||
|
|
||||||
url = domain + "/epub/"
|
url = domain + "/epub/"
|
||||||
|
|
||||||
|
33
resources/recipes/tedneward.recipe
Normal file
33
resources/recipes/tedneward.recipe
Normal 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')]
|
||||||
|
|
104
resources/recipes/weblogs_sl.recipe
Normal file
104
resources/recipes/weblogs_sl.recipe
Normal 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
|
@ -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.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK
|
||||||
from calibre.devices.edge.driver import EDGE
|
from calibre.devices.edge.driver import EDGE
|
||||||
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, \
|
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.sne.driver import SNE
|
||||||
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \
|
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \
|
||||||
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, Q600, LUMIREAD, ALURATEK_COLOR, \
|
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, Q600, LUMIREAD, ALURATEK_COLOR, \
|
||||||
@ -605,9 +605,8 @@ plugins += [
|
|||||||
ELONEX,
|
ELONEX,
|
||||||
TECLAST_K3,
|
TECLAST_K3,
|
||||||
NEWSMY,
|
NEWSMY,
|
||||||
PICO, SUNSTECH_EB700, ARCHOS7O,
|
PICO, SUNSTECH_EB700, ARCHOS7O, SOVOS, STASH,
|
||||||
IPAPYRUS,
|
IPAPYRUS,
|
||||||
SOVOS,
|
|
||||||
EDGE,
|
EDGE,
|
||||||
SNE,
|
SNE,
|
||||||
ALEX,
|
ALEX,
|
||||||
|
@ -65,6 +65,9 @@ class ANDROID(USBMS):
|
|||||||
# Huawei
|
# Huawei
|
||||||
0x45e : { 0x00e1 : [0x007], },
|
0x45e : { 0x00e1 : [0x007], },
|
||||||
|
|
||||||
|
# T-Mobile
|
||||||
|
0x0408 : { 0x03ba : [0x0109], },
|
||||||
|
|
||||||
}
|
}
|
||||||
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
|
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
|
||||||
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
||||||
@ -74,13 +77,13 @@ class ANDROID(USBMS):
|
|||||||
|
|
||||||
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
|
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
|
||||||
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS',
|
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS',
|
||||||
'TELECHIP', 'HUAWEI', ]
|
'TELECHIP', 'HUAWEI', 'T-MOBILE', ]
|
||||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
|
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
|
||||||
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
|
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
|
||||||
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
|
'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',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
'A70S', 'A101IT']
|
'A70S', 'A101IT']
|
||||||
|
@ -15,7 +15,7 @@ from calibre.ebooks.pdb.header import PdbHeaderReader
|
|||||||
|
|
||||||
class APNXBuilder(object):
|
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):
|
def write_apnx(self, mobi_file_path, apnx_path):
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import os
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from struct import unpack
|
from struct import unpack
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import datetime, os, re, sys, json, hashlib
|
|||||||
from calibre.devices.kindle.apnx import APNXBuilder
|
from calibre.devices.kindle.apnx import APNXBuilder
|
||||||
from calibre.devices.kindle.bookmark import Bookmark
|
from calibre.devices.kindle.bookmark import Bookmark
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
from calibre.ebooks.oeb.base import OPF
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Notes on collections:
|
Notes on collections:
|
||||||
@ -221,7 +220,9 @@ class KINDLE2(KINDLE):
|
|||||||
try:
|
try:
|
||||||
apnx_builder.write_apnx(filepath, apnx_path)
|
apnx_builder.write_apnx(filepath, apnx_path)
|
||||||
except:
|
except:
|
||||||
pass
|
print 'Failed to generate APNX'
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
class KINDLE_DX(KINDLE2):
|
class KINDLE_DX(KINDLE2):
|
||||||
|
@ -92,3 +92,15 @@ class SUNSTECH_EB700(TECLAST_K3):
|
|||||||
VENDOR_NAME = 'SUNEB700'
|
VENDOR_NAME = 'SUNEB700'
|
||||||
WINDOWS_MAIN_MEM = 'USB-MSC'
|
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'
|
||||||
|
|
||||||
|
@ -342,11 +342,9 @@ class HeuristicProcessor(object):
|
|||||||
return content
|
return content
|
||||||
|
|
||||||
def txt_process(self, match):
|
def txt_process(self, match):
|
||||||
from calibre.ebooks.txt.processor import convert_basic, preserve_spaces, \
|
from calibre.ebooks.txt.processor import convert_basic, separate_paragraphs_single_line
|
||||||
separate_paragraphs_single_line
|
|
||||||
content = match.group('text')
|
content = match.group('text')
|
||||||
content = separate_paragraphs_single_line(content)
|
content = separate_paragraphs_single_line(content)
|
||||||
content = preserve_spaces(content)
|
|
||||||
content = convert_basic(content, epub_split_size_kb=0)
|
content = convert_basic(content, epub_split_size_kb=0)
|
||||||
return content
|
return content
|
||||||
|
|
||||||
@ -356,6 +354,8 @@ class HeuristicProcessor(object):
|
|||||||
self.log.debug("Running Text Processing")
|
self.log.debug("Running Text Processing")
|
||||||
outerhtml = re.compile(r'.*?(?<=<pre>)(?P<text>.*?)</pre>', re.IGNORECASE|re.DOTALL)
|
outerhtml = re.compile(r'.*?(?<=<pre>)(?P<text>.*?)</pre>', re.IGNORECASE|re.DOTALL)
|
||||||
html = outerhtml.sub(self.txt_process, html)
|
html = outerhtml.sub(self.txt_process, html)
|
||||||
|
from calibre.ebooks.conversion.preprocess import convert_entities
|
||||||
|
html = re.sub(r'&(\S+?);', convert_entities, html)
|
||||||
else:
|
else:
|
||||||
# Add markup naively
|
# Add markup naively
|
||||||
# TODO - find out if there are cases where there are more than one <pre> tag or
|
# TODO - find out if there are cases where there are more than one <pre> tag or
|
||||||
|
@ -216,8 +216,8 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
encryption = self.encrypt_fonts(encrypted_fonts, tdir, uuid)
|
encryption = self.encrypt_fonts(encrypted_fonts, tdir, uuid)
|
||||||
|
|
||||||
from calibre.ebooks.epub import initialize_container
|
from calibre.ebooks.epub import initialize_container
|
||||||
epub = initialize_container(output_path, os.path.basename(opf),
|
with initialize_container(output_path, os.path.basename(opf),
|
||||||
extra_entries=extra_entries)
|
extra_entries=extra_entries) as epub:
|
||||||
epub.add_dir(tdir)
|
epub.add_dir(tdir)
|
||||||
if encryption is not None:
|
if encryption is not None:
|
||||||
epub.writestr('META-INF/encryption.xml', encryption)
|
epub.writestr('META-INF/encryption.xml', encryption)
|
||||||
@ -225,12 +225,13 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
epub.writestr('META-INF/metadata.xml',
|
epub.writestr('META-INF/metadata.xml',
|
||||||
metadata_xml.encode('utf-8'))
|
metadata_xml.encode('utf-8'))
|
||||||
if opts.extract_to is not None:
|
if opts.extract_to is not None:
|
||||||
|
from calibre.utils.zipfile import ZipFile
|
||||||
if os.path.exists(opts.extract_to):
|
if os.path.exists(opts.extract_to):
|
||||||
shutil.rmtree(opts.extract_to)
|
shutil.rmtree(opts.extract_to)
|
||||||
os.mkdir(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)
|
self.log.info('EPUB extracted to', opts.extract_to)
|
||||||
epub.close()
|
|
||||||
|
|
||||||
def encrypt_fonts(self, uris, tdir, uuid): # {{{
|
def encrypt_fonts(self, uris, tdir, uuid): # {{{
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
|
@ -37,13 +37,12 @@ class LITInput(InputFormatPlugin):
|
|||||||
body = body[0]
|
body = body[0]
|
||||||
if len(body) == 1 and body[0].tag == XHTML('pre'):
|
if len(body) == 1 and body[0].tag == XHTML('pre'):
|
||||||
pre = body[0]
|
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
|
separate_paragraphs_single_line
|
||||||
from calibre.ebooks.chardet import xml_to_unicode
|
from calibre.ebooks.chardet import xml_to_unicode
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
import copy
|
import copy
|
||||||
html = separate_paragraphs_single_line(pre.text)
|
html = separate_paragraphs_single_line(pre.text)
|
||||||
html = preserve_spaces(html)
|
|
||||||
html = convert_basic(html).replace('<html>',
|
html = convert_basic(html).replace('<html>',
|
||||||
'<html xmlns="%s">'%XHTML_NS)
|
'<html xmlns="%s">'%XHTML_NS)
|
||||||
html = xml_to_unicode(html, strip_encoding_pats=True,
|
html = xml_to_unicode(html, strip_encoding_pats=True,
|
||||||
|
@ -129,9 +129,6 @@ class Metadata(object):
|
|||||||
val = NULL_VALUES.get(field, None)
|
val = NULL_VALUES.get(field, None)
|
||||||
_data[field] = val
|
_data[field] = val
|
||||||
elif field in _data['user_metadata'].iterkeys():
|
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]['#value#'] = val
|
||||||
_data['user_metadata'][field]['#extra#'] = extra
|
_data['user_metadata'][field]['#extra#'] = extra
|
||||||
else:
|
else:
|
||||||
|
@ -39,6 +39,13 @@ def asfloat(value):
|
|||||||
return 0.0
|
return 0.0
|
||||||
return float(value)
|
return float(value)
|
||||||
|
|
||||||
|
def isspace(text):
|
||||||
|
if not text:
|
||||||
|
return True
|
||||||
|
if u'\xa0' in text:
|
||||||
|
return False
|
||||||
|
return text.isspace()
|
||||||
|
|
||||||
class BlockState(object):
|
class BlockState(object):
|
||||||
def __init__(self, body):
|
def __init__(self, body):
|
||||||
self.body = body
|
self.body = body
|
||||||
@ -438,7 +445,7 @@ class MobiMLizer(object):
|
|||||||
if elem.text:
|
if elem.text:
|
||||||
if istate.preserve:
|
if istate.preserve:
|
||||||
text = elem.text
|
text = elem.text
|
||||||
elif len(elem) > 0 and elem.text.isspace():
|
elif len(elem) > 0 and isspace(elem.text):
|
||||||
text = None
|
text = None
|
||||||
else:
|
else:
|
||||||
text = COLLAPSE.sub(' ', elem.text)
|
text = COLLAPSE.sub(' ', elem.text)
|
||||||
@ -481,7 +488,7 @@ class MobiMLizer(object):
|
|||||||
if child.tail:
|
if child.tail:
|
||||||
if istate.preserve:
|
if istate.preserve:
|
||||||
tail = child.tail
|
tail = child.tail
|
||||||
elif bstate.para is None and child.tail.isspace():
|
elif bstate.para is None and isspace(child.tail):
|
||||||
tail = None
|
tail = None
|
||||||
else:
|
else:
|
||||||
tail = COLLAPSE.sub(' ', child.tail)
|
tail = COLLAPSE.sub(' ', child.tail)
|
||||||
|
@ -103,6 +103,8 @@ class OEBReader(object):
|
|||||||
data = self.oeb.container.read(None)
|
data = self.oeb.container.read(None)
|
||||||
data = self.oeb.decode(data)
|
data = self.oeb.decode(data)
|
||||||
data = XMLDECL_RE.sub('', data)
|
data = XMLDECL_RE.sub('', data)
|
||||||
|
data = data.replace('http://openebook.org/namespaces/oeb-package/1.0',
|
||||||
|
OPF1_NS)
|
||||||
try:
|
try:
|
||||||
opf = etree.fromstring(data)
|
opf = etree.fromstring(data)
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
|
@ -237,6 +237,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
return
|
return
|
||||||
self.stats.rename(location, newloc)
|
self.stats.rename(location, newloc)
|
||||||
self.build_menus()
|
self.build_menus()
|
||||||
|
self.gui.iactions['Copy To Library'].build_menus()
|
||||||
|
|
||||||
def delete_requested(self, name, location):
|
def delete_requested(self, name, location):
|
||||||
loc = location.replace('/', os.sep)
|
loc = location.replace('/', os.sep)
|
||||||
@ -253,6 +254,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
pass
|
pass
|
||||||
self.stats.remove(location)
|
self.stats.remove(location)
|
||||||
self.build_menus()
|
self.build_menus()
|
||||||
|
self.gui.iactions['Copy To Library'].build_menus()
|
||||||
|
|
||||||
def backup_status(self, location):
|
def backup_status(self, location):
|
||||||
dirty_text = 'no'
|
dirty_text = 'no'
|
||||||
@ -329,6 +331,7 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
' libraries.')%loc, show=True)
|
' libraries.')%loc, show=True)
|
||||||
self.stats.remove(location)
|
self.stats.remove(location)
|
||||||
self.build_menus()
|
self.build_menus()
|
||||||
|
self.gui.iactions['Copy To Library'].build_menus()
|
||||||
return
|
return
|
||||||
|
|
||||||
prefs['library_path'] = loc
|
prefs['library_path'] = loc
|
||||||
@ -371,9 +374,20 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
if not self.change_library_allowed():
|
if not self.change_library_allowed():
|
||||||
return
|
return
|
||||||
from calibre.gui2.dialogs.choose_library import ChooseLibrary
|
from calibre.gui2.dialogs.choose_library import ChooseLibrary
|
||||||
|
self.gui.library_view.save_state()
|
||||||
db = self.gui.library_view.model().db
|
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_()
|
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):
|
def change_library_allowed(self):
|
||||||
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
||||||
|
@ -11,7 +11,7 @@ from PyQt4.Qt import QLineEdit, QAbstractListModel, Qt, \
|
|||||||
|
|
||||||
from calibre.utils.icu import sort_key, lower
|
from calibre.utils.icu import sort_key, lower
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.gui2.widgets import EnComboBox
|
from calibre.gui2.widgets import EnComboBox, LineEditECM
|
||||||
|
|
||||||
class CompleteModel(QAbstractListModel):
|
class CompleteModel(QAbstractListModel):
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ class CompleteModel(QAbstractListModel):
|
|||||||
return NONE
|
return NONE
|
||||||
|
|
||||||
|
|
||||||
class MultiCompleteLineEdit(QLineEdit):
|
class MultiCompleteLineEdit(QLineEdit, LineEditECM):
|
||||||
'''
|
'''
|
||||||
A line edit that completes on multiple items separated by a
|
A line edit that completes on multiple items separated by a
|
||||||
separator. Use the :meth:`update_items_cache` to set the list of
|
separator. Use the :meth:`update_items_cache` to set the list of
|
||||||
|
@ -1026,6 +1026,20 @@ class DeviceMixin(object): # {{{
|
|||||||
self.location_manager.free[1] : 'carda',
|
self.location_manager.free[1] : 'carda',
|
||||||
self.location_manager.free[2] : 'cardb' }
|
self.location_manager.free[2] : 'cardb' }
|
||||||
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
|
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,
|
self.upload_books(files, names, metadata,
|
||||||
on_card=on_card,
|
on_card=on_card,
|
||||||
memory=[files, remove])
|
memory=[files, remove])
|
||||||
|
@ -71,6 +71,8 @@ class ChooseLibrary(QDialog, Ui_Dialog):
|
|||||||
prefs['library_path'] = loc
|
prefs['library_path'] = loc
|
||||||
self.callback(loc, copy_structure=self.copy_structure.isChecked())
|
self.callback(loc, copy_structure=self.copy_structure.isChecked())
|
||||||
else:
|
else:
|
||||||
|
self.db.prefs.disable_setting = True
|
||||||
|
self.library_renamed = True
|
||||||
move_library(self.db.library_path, loc, self.parent(),
|
move_library(self.db.library_path, loc, self.parent(),
|
||||||
self.callback)
|
self.callback)
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ class PluginModel(QAbstractItemModel, SearchQueryParser): # {{{
|
|||||||
return self.index(ans[0], 0, QModelIndex()) if ans[1] < 0 else \
|
return self.index(ans[0], 0, QModelIndex()) if ans[1] < 0 else \
|
||||||
self.index(ans[1], 0, self.index(ans[0], 0, QModelIndex()))
|
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):
|
if not self.hasIndex(row, column, parent):
|
||||||
return QModelIndex()
|
return QModelIndex()
|
||||||
|
|
||||||
@ -165,8 +165,6 @@ class PluginModel(QAbstractItemModel, SearchQueryParser): # {{{
|
|||||||
def flags(self, index):
|
def flags(self, index):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return 0
|
return 0
|
||||||
if index.internalId() == 0:
|
|
||||||
return Qt.ItemIsEnabled
|
|
||||||
flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
|
flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
|
||||||
return flags
|
return flags
|
||||||
|
|
||||||
|
@ -5,37 +5,316 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import textwrap
|
||||||
|
|
||||||
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, AbortCommit
|
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, AbortCommit
|
||||||
from calibre.gui2.preferences.tweaks_ui import Ui_Form
|
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.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):
|
class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||||
|
|
||||||
def genesis(self, gui):
|
def genesis(self, gui):
|
||||||
self.gui = 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):
|
def changed(self, *args):
|
||||||
self.changed_signal.emit()
|
self.changed_signal.emit()
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
deft, curt = read_raw_tweaks()
|
self.tweaks = Tweaks()
|
||||||
self.current_tweaks.blockSignals(True)
|
self.tweaks_view.setModel(self.tweaks)
|
||||||
self.current_tweaks.setPlainText(curt.decode('utf-8'))
|
|
||||||
self.current_tweaks.blockSignals(False)
|
|
||||||
|
|
||||||
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):
|
def restore_defaults(self):
|
||||||
ConfigWidgetBase.restore_defaults(self)
|
ConfigWidgetBase.restore_defaults(self)
|
||||||
deft, curt = read_raw_tweaks()
|
self.tweaks.restore_to_defaults()
|
||||||
self.current_tweaks.setPlainText(deft.decode('utf-8'))
|
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):
|
def commit(self):
|
||||||
raw = unicode(self.current_tweaks.toPlainText()).encode('utf-8')
|
raw = self.tweaks.to_string()
|
||||||
try:
|
try:
|
||||||
exec raw
|
exec raw
|
||||||
except:
|
except:
|
||||||
@ -54,5 +333,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from PyQt4.Qt import QApplication
|
from PyQt4.Qt import QApplication
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
|
#Tweaks()
|
||||||
|
#test_widget
|
||||||
test_widget('Advanced', 'Tweaks')
|
test_widget('Advanced', 'Tweaks')
|
||||||
|
|
||||||
|
@ -7,17 +7,19 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>660</width>
|
<width>660</width>
|
||||||
<height>351</height>
|
<height>531</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</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>
|
<item>
|
||||||
<widget class="QLabel" name="label_18">
|
<widget class="QLabel" name="label_18">
|
||||||
<property name="text">
|
<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 <b>after a restart</b> of calibre.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -25,13 +27,53 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_6">
|
<widget class="QListView" name="tweaks_view">
|
||||||
<property name="title">
|
<property name="sizePolicy">
|
||||||
<string>All available tweaks</string>
|
<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>&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>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_11">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QPlainTextEdit" name="default_tweaks">
|
|
||||||
<property name="readOnly">
|
<property name="readOnly">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
@ -40,14 +82,38 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="1" column="1">
|
||||||
<widget class="QGroupBox" name="groupBox_7">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&Current tweaks</string>
|
<string>Edit tweak</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_10">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QPlainTextEdit" name="current_tweaks"/>
|
<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 &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>&Apply</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -440,16 +440,17 @@ class Document(QWebPage): # {{{
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def height(self):
|
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()
|
q = self.mainFrame().contentsSize().height()
|
||||||
if q == j:
|
if q < 0:
|
||||||
return j
|
# Don't know if this is still needed, but it can't hurt
|
||||||
if min(j, q) <= 0:
|
j = self.javascript('document.body.offsetHeight', 'int')
|
||||||
return max(j, q)
|
if j >= 0:
|
||||||
window_height = self.window_height
|
q = j
|
||||||
if j == window_height:
|
return q
|
||||||
return j if q < 1.2*j else q
|
|
||||||
return j
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width(self):
|
def width(self):
|
||||||
|
@ -20,7 +20,7 @@ from calibre.gui2.filename_pattern_ui import Ui_Form
|
|||||||
from calibre import fit_image
|
from calibre import fit_image
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
from calibre.ebooks.metadata.meta import metadata_from_filename
|
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
|
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
|
||||||
|
|
||||||
history = XMLConfig('history')
|
history = XMLConfig('history')
|
||||||
@ -932,7 +932,7 @@ class SplitterHandle(QSplitterHandle):
|
|||||||
|
|
||||||
def paintEvent(self, ev):
|
def paintEvent(self, ev):
|
||||||
QSplitterHandle.paintEvent(self, ev)
|
QSplitterHandle.paintEvent(self, ev)
|
||||||
if self.highlight:
|
if self.highlight and tweaks['draw_hidden_section_indicators']:
|
||||||
painter = QPainter(self)
|
painter = QPainter(self)
|
||||||
painter.setClipRect(ev.rect())
|
painter.setClipRect(ev.rect())
|
||||||
painter.fillRect(self.rect(), Qt.yellow)
|
painter.fillRect(self.rect(), Qt.yellow)
|
||||||
|
@ -787,6 +787,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
mi.id = id
|
mi.id = id
|
||||||
for key, meta in self.field_metadata.custom_iteritems():
|
for key, meta in self.field_metadata.custom_iteritems():
|
||||||
mi.set_user_metadata(key, meta)
|
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'],
|
mi.set(key, val=self.get_custom(idx, label=meta['label'],
|
||||||
index_is_id=index_is_id),
|
index_is_id=index_is_id),
|
||||||
extra=self.get_custom_extra(idx, label=meta['label'],
|
extra=self.get_custom_extra(idx, label=meta['label'],
|
||||||
|
@ -561,9 +561,10 @@ format, whether input or output are available in the conversion dialog under the
|
|||||||
Convert Microsoft Word documents
|
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
|
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
|
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
|
generating the Table of Contents much simpler. It is called BookCreator and is available for free
|
||||||
|
@ -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
|
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>`_.
|
`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?
|
How do I use |app| with my iPad/iPhone/iTouch?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -375,13 +370,6 @@ Content From The Web
|
|||||||
:depth: 1
|
:depth: 1
|
||||||
:local:
|
: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?
|
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`.
|
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`.
|
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?
|
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.
|
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.
|
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?
|
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?
|
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:
|
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>`_.
|
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?
|
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.
|
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.
|
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
@ -107,6 +107,7 @@ _extra_lang_codes = {
|
|||||||
'en_CZ' : _('English (Czechoslovakia)'),
|
'en_CZ' : _('English (Czechoslovakia)'),
|
||||||
'en_PK' : _('English (Pakistan)'),
|
'en_PK' : _('English (Pakistan)'),
|
||||||
'en_HR' : _('English (Croatia)'),
|
'en_HR' : _('English (Croatia)'),
|
||||||
|
'en_ID' : _('English (Indonesia)'),
|
||||||
'en_IL' : _('English (Israel)'),
|
'en_IL' : _('English (Israel)'),
|
||||||
'en_SG' : _('English (Singapore)'),
|
'en_SG' : _('English (Singapore)'),
|
||||||
'en_YE' : _('English (Yemen)'),
|
'en_YE' : _('English (Yemen)'),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user