Pull from trunk
247
Changelog.yaml
@ -4,6 +4,253 @@
|
|||||||
# for important features/bug fixes.
|
# for important features/bug fixes.
|
||||||
# Also, each release can have new and improved recipes.
|
# Also, each release can have new and improved recipes.
|
||||||
|
|
||||||
|
- version: 0.7.13
|
||||||
|
date: 2010-08-06
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Add a button to the edit metadata dialog to generate a cover based on the book metadata"
|
||||||
|
tickets: [5959]
|
||||||
|
|
||||||
|
- title: "When using series or title in a save template to generate a file path, remove leading prepositions. This behavior can be controlled via a tweak."
|
||||||
|
|
||||||
|
- title: "News download: When downloading news for the Kindle, do not add date to the title, to allow the Kindle's periodical archiving to work."
|
||||||
|
tickets: [6411]
|
||||||
|
|
||||||
|
- title: "Content Server OPDS feeds: Grouping of items by first alphabet is now case-insensitive."
|
||||||
|
|
||||||
|
- title: "Do not allow the user to use save to disk to save files into the calibre library"
|
||||||
|
tickets: [6392]
|
||||||
|
|
||||||
|
- title: "Switch to a new C based API for using ImageMagick. More robust and a minor speedup when doing image manipulations"
|
||||||
|
|
||||||
|
- title: "Move cover downloading to a plugin based API. You can now add new cover sources to calibre using plugins."
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Content server OPDS feeds: Handle the case when the author field is blank"
|
||||||
|
tickets: [6371]
|
||||||
|
|
||||||
|
- title: "TXT Input: Strip out illegal chars from txt files."
|
||||||
|
tickets: [6335]
|
||||||
|
|
||||||
|
- title: "Save to disk/send to device templates: Always render {series_index} as an empty string when the book has no series."
|
||||||
|
tickets: [6409]
|
||||||
|
|
||||||
|
- title: "PD Novel driver: Remove covers when deleting books"
|
||||||
|
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "Snopes"
|
||||||
|
author: Startson17
|
||||||
|
|
||||||
|
- title: "dr.dk and Balkan Insight"
|
||||||
|
author: Darko Miletic
|
||||||
|
|
||||||
|
- title: Folha de Sao Paulo
|
||||||
|
author: Saverio Palmieri Neto
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Honolulu Star Advertiser
|
||||||
|
- Nature News
|
||||||
|
- Associated Press
|
||||||
|
- Scientific American
|
||||||
|
- New Scientist
|
||||||
|
|
||||||
|
- version: 0.7.12
|
||||||
|
date: 2010-07-30
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Fix a typo that caused a harmless error message after setting preferences"
|
||||||
|
|
||||||
|
- title: "Linux build system: No longer search for poppler-qt4 libraries as they are not needed"
|
||||||
|
|
||||||
|
- version: 0.7.11
|
||||||
|
date: 2010-07-30
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "EPUB metadata: When setting metadata in an EPUB file, if it has a well defined image based cover, update it"
|
||||||
|
|
||||||
|
- title: "Support for Droid X, Samsung Vibrant and Promedia ebook reader"
|
||||||
|
|
||||||
|
- title: "Add entry to Connect/share menu to conveniently stop and start the Content Server"
|
||||||
|
|
||||||
|
- title: "News download: Make the navbars on the section index pages more useful, adding links to net and previous section"
|
||||||
|
|
||||||
|
- title: "Add a button to reset confirm dialogs to Preferences->General"
|
||||||
|
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Fix crash in edit metadata dialog if you click OK before cover download completes"
|
||||||
|
tickets: [6337]
|
||||||
|
|
||||||
|
- title: "Kobo driver: Show a warning when the user tries to export/view .kobo files. Also add support for the new sofroot vendor id"
|
||||||
|
|
||||||
|
- title: "Update check. Do not be fooled by a redirecting proxy when checking for new version"
|
||||||
|
tickets: [6325]
|
||||||
|
|
||||||
|
- title: "Add book count to tooltip of library button in toolbar"
|
||||||
|
tickets: [6340]
|
||||||
|
|
||||||
|
- title: "Content server: When serving OPDS feeds send the correct content-type header."
|
||||||
|
tickets: [6329]
|
||||||
|
|
||||||
|
- title: "PDF Output: Don't insert blank pages before every individual HTML file in the ebook."
|
||||||
|
tickets: [6315]
|
||||||
|
|
||||||
|
- title: "Fix saving of cover when path to book folder contains non ascii characters"
|
||||||
|
tickets: [6328]
|
||||||
|
|
||||||
|
- title: "Fix regression that broke showing send to actions for multiple email accounts"
|
||||||
|
|
||||||
|
- title: "Fix bug in handlling of hexadecimal entities"
|
||||||
|
tickets: [6305]
|
||||||
|
|
||||||
|
- title: "SONY driver: More fixes to handle broken media.xml files"
|
||||||
|
tickets: [6296]
|
||||||
|
|
||||||
|
- title: "Linux installer: Fix rendering of viewer icon and restrict all icons to 128x128 since GNOME can't handle large icons"
|
||||||
|
|
||||||
|
- title: "RTF Input: Fix handling of hard linebreaks"
|
||||||
|
tickets: [6208]
|
||||||
|
|
||||||
|
- title: "RTF Output: Fix regression that broke rendering of bold and italic text"
|
||||||
|
tickets: [6098]
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "Draw and Cook"
|
||||||
|
author: Startson17
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- La Nacion
|
||||||
|
- Vecernje Novosti
|
||||||
|
- Der Tagesspiegel
|
||||||
|
- Die Zeit Nachrichten
|
||||||
|
- Toms Hardware (DE)
|
||||||
|
- Welt Online
|
||||||
|
|
||||||
|
- version: 0.7.10
|
||||||
|
date: 2010-07-23
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Allow user customization of static resources such as icons and templates"
|
||||||
|
type: major
|
||||||
|
description: >
|
||||||
|
"You can now change the icons used in the User Interface and other static resources. Details on how to
|
||||||
|
do this are at: http://calibre-ebook.com/user_manual/customize.html#overriding-icons-templates-etcetera"
|
||||||
|
|
||||||
|
- title: "Split the 'Send to device' button into two buttons, 'Connect/share' and 'Send to device'. The new 'Send to device' button will now only be available when a device is connected."
|
||||||
|
|
||||||
|
- title: "Store column layout, saved searches and user categories seprately per calibre library. This makes it possible to easily switch between libraries with different custom column setups"
|
||||||
|
|
||||||
|
- title: "See the last modofied date for each format in the edit metadata dialog via a tooltip"
|
||||||
|
tickets: [6252]
|
||||||
|
|
||||||
|
- title: "PD Novel driver: Add support for uploading cover thumbnails to device"
|
||||||
|
|
||||||
|
- title: "More sophisticated metadata extraction from HTML files"
|
||||||
|
tickets: [6223]
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
|
||||||
|
- title: "Fix problems with a few windows installs caused by the upgrade to Qt 4.6.3 in the previous release. These would manifest as a not working Add Books button, or deletes not actually deleting files, etc."
|
||||||
|
|
||||||
|
- title: "Restore configurability of toolbar, which was temporarily removed in 0.7.9. You can once again set icon size via Preferences->Interface"
|
||||||
|
|
||||||
|
- title: "Fix regression in iTunes driver in 0.7.9 when sending series info"
|
||||||
|
|
||||||
|
- title: "Search: Fix parsing of search terms that contain a word that starts with 'and' or 'or' and is not the first word"
|
||||||
|
|
||||||
|
- title: "When merging records also merge metadata in custom columns"
|
||||||
|
tickets: [6120]
|
||||||
|
|
||||||
|
- title: "When scrolling to show a particular row, handle the case when the first column is a custom column"
|
||||||
|
tickets: [6176]
|
||||||
|
|
||||||
|
- title: "Fix SD card detection for The Augen Book"
|
||||||
|
tickets: [6224]
|
||||||
|
|
||||||
|
- title: "CHM Input: Fix a couple of bugs that could cause crashes"
|
||||||
|
tickets: [6240]
|
||||||
|
|
||||||
|
- title: "Conversion pipeline: Handle zero width elements with non zero indents gracefully"
|
||||||
|
tickets: [6230]
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "daum.net"
|
||||||
|
author: trustin
|
||||||
|
|
||||||
|
- title: "MIT Technology Review, Alternet, Waco Tribune Herald and Orlando Sentinel"
|
||||||
|
author: rty
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- The BBC
|
||||||
|
- heise
|
||||||
|
|
||||||
|
- version: 0.7.9
|
||||||
|
date: 2010-07-17
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "New unified toolbar"
|
||||||
|
type: major
|
||||||
|
description: >
|
||||||
|
"A new unified toolbar combines the old toolbar and device display, to save space. Now when a device is connected, buttons
|
||||||
|
are created in the unified toolbar for the device and its storage cards. Click the arrow next to the button to eject the device."
|
||||||
|
|
||||||
|
- title: "Device drivers: Add option to allow calibre to automatically manage metadata on the device in Preferences->Add/Save->Sending to device"
|
||||||
|
|
||||||
|
- title: "BibTeX output for catalogs. The list of books in calibre can now also be output as a .bib file"
|
||||||
|
|
||||||
|
- title: "A new toolbar button to choose/create different calibre libraries. Be careful using it if you also use custom columns."
|
||||||
|
|
||||||
|
- title: "Support for the MiBuk"
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "MOBI metadata: Replace HTML entities in the title read from the MOBI file"
|
||||||
|
|
||||||
|
- title: "Conversion pipeline: Handle elements with percentage sizes that are children of zero size parents correctly."
|
||||||
|
tickets: [6155]
|
||||||
|
|
||||||
|
- title: "Fix regression that made LRF conversion less robust"
|
||||||
|
tickets: [6180]
|
||||||
|
|
||||||
|
- title: "FB2 Input: Handle embedded images correctly, so that EPUB generated from FB2 works with Adobe Digital Editions."
|
||||||
|
tickets: [6183]
|
||||||
|
|
||||||
|
- title: "Fix regression that prevented old news from being deleted in the calibre library if calibre is never kept running for more than an hour"
|
||||||
|
|
||||||
|
- title: "RTF Input: Fix handling of text align and superscript/subscripts"
|
||||||
|
tickets: [3644,5060]
|
||||||
|
|
||||||
|
- title: "Fix long series or publisher names causing convert dialog to become too wide"
|
||||||
|
|
||||||
|
- title: "SONY driver: Fix handling of invalid XML databases with null bytes"
|
||||||
|
tickets: [6165]
|
||||||
|
|
||||||
|
- title: "iTunes driver: Better series_index sorting"
|
||||||
|
|
||||||
|
- title: "Improved editing of dates for custom columns"
|
||||||
|
|
||||||
|
- title: "Linux USB scanner: Don't fail to start calibre if SYFS is not present. Instead simply fail to detect devices"
|
||||||
|
tickets: [6156]
|
||||||
|
|
||||||
|
- title: "Android driver: Show books on device if Aldiko is being used"
|
||||||
|
tickets: [6100]
|
||||||
|
|
||||||
|
- title: "Upgrade to Qt 4.6.3 in all binary builds to ensure proper rendering of the new toolbar icons"
|
||||||
|
|
||||||
|
- title: "Fix handling of entities in epub files by the epub-fix command"
|
||||||
|
tickets: [6136]
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "EL Pain Impresso"
|
||||||
|
author: Darko Miletic
|
||||||
|
|
||||||
|
- title: "MIT Technology Review, Alternet, Waco Tribune Herald and Orlando Sentinel"
|
||||||
|
author: rty
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Google Reader
|
||||||
|
|
||||||
|
|
||||||
- version: 0.7.8
|
- version: 0.7.8
|
||||||
date: 2010-07-09
|
date: 2010-07-09
|
||||||
|
|
||||||
|
83
resources/content_server/mobile.css
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/* CSS for the mobile version of the content server webpage */
|
||||||
|
|
||||||
|
.navigation table.buttons {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation .button {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button a, .button:visited a {
|
||||||
|
padding: 0.5em;
|
||||||
|
font-size: 1.25em;
|
||||||
|
border: 1px solid black;
|
||||||
|
text-color: black;
|
||||||
|
background-color: #ddd;
|
||||||
|
border-top: 1px solid ThreeDLightShadow;
|
||||||
|
border-right: 1px solid ButtonShadow;
|
||||||
|
border-bottom: 1px solid ButtonShadow;
|
||||||
|
border-left: 1 px solid ThreeDLightShadow;
|
||||||
|
-moz-border-radius: 0.25em;
|
||||||
|
-webkit-border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover a {
|
||||||
|
border-top: 1px solid #666;
|
||||||
|
border-right: 1px solid #CCC;
|
||||||
|
border-bottom: 1 px solid #CCC;
|
||||||
|
border-left: 1 px solid #666;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
div.navigation {
|
||||||
|
padding-bottom: 1em;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search_box {
|
||||||
|
border: 1px solid #393;
|
||||||
|
-moz-border-radius: 0.5em;
|
||||||
|
-webkit-border-radius: 0.5em;
|
||||||
|
padding: 1em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
#listing td {
|
||||||
|
padding: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing td.thumbnail {
|
||||||
|
height: 60px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing tr:nth-child(even) {
|
||||||
|
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing .button a{
|
||||||
|
display: inline-block;
|
||||||
|
width: 2.5em;
|
||||||
|
padding-left: 0em;
|
||||||
|
padding-right: 0em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#spacer {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -72,4 +72,11 @@ gui_pubdate_display_format = '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 how title and series names are formatted when saving to disk/sending
|
||||||
|
# to device. If set to library_order, leading articles such as The and A will
|
||||||
|
# be put at the end
|
||||||
|
# If set to 'strictly_alphabetic', the titles will be sorted without processing
|
||||||
|
# For example, with library_order, "The Client" will become "Client, The". With
|
||||||
|
# strictly_alphabetic, it would remain "The Client".
|
||||||
|
save_template_title_series_sorting = 'library_order'
|
||||||
|
|
||||||
|
5123
resources/images/connect_share.svg
Normal file
After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 5.3 KiB |
1009
resources/images/dictionary.svg
Normal file
After Width: | Height: | Size: 48 KiB |
@ -1,24 +1,31 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
xmlns:cc="http://web.resource.org/cc/"
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
width="128"
|
width="128"
|
||||||
height="128"
|
height="128"
|
||||||
id="svg1307"
|
id="svg1307"
|
||||||
sodipodi:version="0.32"
|
sodipodi:version="0.32"
|
||||||
inkscape:version="0.43"
|
inkscape:version="0.47 r22583"
|
||||||
version="1.0"
|
version="1.0"
|
||||||
sodipodi:docbase="/home/pinheiro/Documents/pics/new oxygen/svg"
|
sodipodi:docname="donate.svg">
|
||||||
sodipodi:docname="love.svg">
|
|
||||||
<defs
|
<defs
|
||||||
id="defs1309">
|
id="defs1309">
|
||||||
|
<inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 64 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="128 : 64 : 1"
|
||||||
|
inkscape:persp3d-origin="64 : 42.666667 : 1"
|
||||||
|
id="perspective44" />
|
||||||
<linearGradient
|
<linearGradient
|
||||||
inkscape:collect="always"
|
inkscape:collect="always"
|
||||||
id="linearGradient2231">
|
id="linearGradient2231">
|
||||||
@ -180,8 +187,8 @@
|
|||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="7.851329"
|
inkscape:zoom="7.851329"
|
||||||
inkscape:cx="92.691163"
|
inkscape:cx="60.937831"
|
||||||
inkscape:cy="92.473338"
|
inkscape:cy="61.488995"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
@ -189,10 +196,11 @@
|
|||||||
guidetolerance="0.1px"
|
guidetolerance="0.1px"
|
||||||
showguides="true"
|
showguides="true"
|
||||||
inkscape:guide-bbox="true"
|
inkscape:guide-bbox="true"
|
||||||
inkscape:window-width="1106"
|
inkscape:window-width="1680"
|
||||||
inkscape:window-height="958"
|
inkscape:window-height="997"
|
||||||
inkscape:window-x="597"
|
inkscape:window-x="-4"
|
||||||
inkscape:window-y="25">
|
inkscape:window-y="30"
|
||||||
|
inkscape:window-maximized="1">
|
||||||
<sodipodi:guide
|
<sodipodi:guide
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
position="32.487481"
|
position="32.487481"
|
||||||
@ -245,26 +253,19 @@
|
|||||||
id="path2276"
|
id="path2276"
|
||||||
d="M 50.892799,3.2812959 L 50.892799,0.48658747 L 50.892799,3.2812959 z "
|
d="M 50.892799,3.2812959 L 50.892799,0.48658747 L 50.892799,3.2812959 z "
|
||||||
style="fill:#ffffff;fill-opacity:0.75688076;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" />
|
style="fill:#ffffff;fill-opacity:0.75688076;fill-rule:nonzero;stroke:none;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" />
|
||||||
<path
|
|
||||||
sodipodi:type="arc"
|
|
||||||
style="opacity:0.38139535;fill:url(#radialGradient3297);fill-opacity:1.0;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
|
|
||||||
id="path3289"
|
|
||||||
sodipodi:cx="63.912209"
|
|
||||||
sodipodi:cy="115.70919"
|
|
||||||
sodipodi:rx="63.912209"
|
|
||||||
sodipodi:ry="12.641975"
|
|
||||||
d="M 127.82442 115.70919 A 63.912209 12.641975 0 1 1 0,115.70919 A 63.912209 12.641975 0 1 1 127.82442 115.70919 z"
|
|
||||||
transform="matrix(1,0,0,0.416667,0,74.87151)" />
|
|
||||||
<path
|
<path
|
||||||
style="fill:url(#radialGradient2335);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
style="fill:url(#radialGradient2335);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
d="M 35.325021,6.2016208 C 32.278871,6.2210338 29.045555,6.6687791 25.645673,7.6089386 C 5.9380713,13.058619 0.404709,29.342113 5.3805953,48.506873 C 12.126047,74.487157 36.855395,101.02725 64.150803,115.92895 L 64.150803,116.02417 C 64.162016,116.00826 64.173539,115.99248 64.184766,115.97656 C 64.195995,115.99248 64.207516,116.00826 64.218732,116.02417 L 64.218732,115.92895 C 90.473794,101.59521 116.24349,74.487157 122.98895,48.506873 C 127.96481,29.342113 122.43148,13.058619 102.72386,7.6089386 C 83.422254,2.2715258 69.549778,12.840101 64.184766,27.183808 C 59.764775,15.366673 49.572303,6.1108179 35.325021,6.2016208 z "
|
d="M 35.325021,6.2016208 C 32.278871,6.2210338 29.045555,6.6687791 25.645673,7.6089386 C 5.9380713,13.058619 0.404709,29.342113 5.3805953,48.506873 C 12.126047,74.487157 36.855395,101.02725 64.150803,115.92895 L 64.150803,116.02417 C 64.162016,116.00826 64.173539,115.99248 64.184766,115.97656 C 64.195995,115.99248 64.207516,116.00826 64.218732,116.02417 L 64.218732,115.92895 C 90.473794,101.59521 116.24349,74.487157 122.98895,48.506873 C 127.96481,29.342113 122.43148,13.058619 102.72386,7.6089386 C 83.422254,2.2715258 69.549778,12.840101 64.184766,27.183808 C 59.764775,15.366673 49.572303,6.1108179 35.325021,6.2016208 z "
|
||||||
id="path2245"
|
id="path2245"
|
||||||
sodipodi:nodetypes="cssccsccsscc" />
|
sodipodi:nodetypes="cssccsccsscc" />
|
||||||
<path
|
<g
|
||||||
id="path2369"
|
id="g2850">
|
||||||
d="M 35.325021,6.2016208 C 32.278871,6.2210338 29.045555,6.6687791 25.645673,7.6089386 C 5.9380713,13.058619 0.404709,29.342113 5.3805953,48.506873 C 12.126047,74.487157 37.113186,101.16799 64.150803,115.92895 L 64.150803,116.02417 C 64.162016,116.00826 64.173539,115.99248 64.184766,115.97656 C 64.195995,115.99248 64.207516,116.00826 64.218732,116.02417 L 64.218732,115.92895 C 90.398445,101.63635 116.24349,74.487157 122.98895,48.506873 C 127.96481,29.342113 122.43148,13.058619 102.72386,7.6089386 C 83.422254,2.2715258 69.549778,12.840101 64.184766,27.183808 C 59.764775,15.366673 49.572303,6.1108179 35.325021,6.2016208 z "
|
<path
|
||||||
style="opacity:0.4713115;fill:url(#linearGradient2379);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
sodipodi:nodetypes="cssccsccsscc"
|
||||||
sodipodi:nodetypes="cssccsccsscc" />
|
style="opacity:0.4713115;fill:url(#linearGradient2379);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 35.325021,6.2016208 C 32.278871,6.2210338 29.045555,6.6687791 25.645673,7.6089386 C 5.9380713,13.058619 0.404709,29.342113 5.3805953,48.506873 C 12.126047,74.487157 37.113186,101.16799 64.150803,115.92895 L 64.150803,116.02417 C 64.162016,116.00826 64.173539,115.99248 64.184766,115.97656 C 64.195995,115.99248 64.207516,116.00826 64.218732,116.02417 L 64.218732,115.92895 C 90.398445,101.63635 116.24349,74.487157 122.98895,48.506873 C 127.96481,29.342113 122.43148,13.058619 102.72386,7.6089386 C 83.422254,2.2715258 69.549778,12.840101 64.184766,27.183808 C 59.764775,15.366673 49.572303,6.1108179 35.325021,6.2016208 z "
|
||||||
|
id="path2369" />
|
||||||
|
</g>
|
||||||
<path
|
<path
|
||||||
style="opacity:0.1762295;fill:url(#linearGradient2331);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.29999995;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="opacity:0.1762295;fill:url(#linearGradient2331);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.29999995;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="M 34.451605,6.2067207 C 31.659392,6.2976073 28.7301,6.7682297 25.648957,7.6202497 C 7.7889432,12.559022 1.5815371,26.389172 4.2759909,43.204304 C 27.13595,75.72273 65.297627,95.42612 91.41193,91.971053 C 105.43169,77.948778 119.04939,63.70497 122.99185,48.520401 C 127.96773,29.355639 122.42255,13.069929 102.71494,7.6202497 C 83.413331,2.2828362 69.546961,12.850845 64.181949,27.194552 C 59.761957,15.377418 49.555176,6.1159177 35.307894,6.2067207 C 35.022317,6.2085406 34.740456,6.1973187 34.451605,6.2067207 z "
|
d="M 34.451605,6.2067207 C 31.659392,6.2976073 28.7301,6.7682297 25.648957,7.6202497 C 7.7889432,12.559022 1.5815371,26.389172 4.2759909,43.204304 C 27.13595,75.72273 65.297627,95.42612 91.41193,91.971053 C 105.43169,77.948778 119.04939,63.70497 122.99185,48.520401 C 127.96773,29.355639 122.42255,13.069929 102.71494,7.6202497 C 83.413331,2.2828362 69.546961,12.850845 64.181949,27.194552 C 59.761957,15.377418 49.555176,6.1159177 35.307894,6.2067207 C 35.022317,6.2085406 34.740456,6.1973187 34.451605,6.2067207 z "
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -1,234 +1,176 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://web.resource.org/cc/"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
version="1.0"
|
version="1.0"
|
||||||
id="Livello_1"
|
|
||||||
width="128"
|
width="128"
|
||||||
height="128"
|
height="128"
|
||||||
viewBox="0 0 139 139"
|
viewBox="0 0 139 139"
|
||||||
overflow="visible"
|
id="Livello_1"
|
||||||
enable-background="new 0 0 139 139"
|
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
sodipodi:version="0.32"
|
style="overflow:visible"><defs
|
||||||
inkscape:version="0.45+devel"
|
|
||||||
sodipodi:docname="system-help.svgz"
|
|
||||||
inkscape:output_extension="org.inkscape.output.svgz.inkscape"
|
|
||||||
style="overflow:visible"><metadata
|
|
||||||
id="metadata3164"><rdf:RDF><cc:Work
|
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
|
||||||
id="defs3162"><filter
|
id="defs3162"><filter
|
||||||
inkscape:collect="always"
|
|
||||||
x="-0.132641"
|
x="-0.132641"
|
||||||
width="1.265282"
|
|
||||||
y="-0.34752154"
|
y="-0.34752154"
|
||||||
|
width="1.265282"
|
||||||
height="1.6950431"
|
height="1.6950431"
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
id="filter3547"><feGaussianBlur
|
id="filter3547"><feGaussianBlur
|
||||||
inkscape:collect="always"
|
id="feGaussianBlur3549"
|
||||||
stdDeviation="2.7512044"
|
stdDeviation="2.7512044" /></filter><filter
|
||||||
id="feGaussianBlur3549" /></filter><filter
|
color-interpolation-filters="sRGB"
|
||||||
inkscape:collect="always"
|
|
||||||
id="filter5097"><feGaussianBlur
|
id="filter5097"><feGaussianBlur
|
||||||
inkscape:collect="always"
|
id="feGaussianBlur5099"
|
||||||
stdDeviation="2.32"
|
stdDeviation="2.32" /></filter><filter
|
||||||
id="feGaussianBlur5099" /></filter><filter
|
|
||||||
inkscape:collect="always"
|
|
||||||
x="-0.143268"
|
x="-0.143268"
|
||||||
width="1.286536"
|
|
||||||
y="-0.072184406"
|
y="-0.072184406"
|
||||||
|
width="1.286536"
|
||||||
height="1.1443688"
|
height="1.1443688"
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
id="filter5125"><feGaussianBlur
|
id="filter5125"><feGaussianBlur
|
||||||
inkscape:collect="always"
|
id="feGaussianBlur5127"
|
||||||
stdDeviation="1.91024"
|
stdDeviation="1.91024" /></filter></defs>
|
||||||
id="feGaussianBlur5127" /></filter></defs><sodipodi:namedview
|
|
||||||
inkscape:window-height="697"
|
|
||||||
inkscape:window-width="1024"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
guidetolerance="10.0"
|
|
||||||
gridtolerance="10.0"
|
|
||||||
objecttolerance="10.0"
|
|
||||||
borderopacity="1.0"
|
|
||||||
bordercolor="#666666"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
id="base"
|
|
||||||
inkscape:zoom="2.9352518"
|
|
||||||
inkscape:cx="99.496726"
|
|
||||||
inkscape:cy="69.329657"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:current-layer="Livello_1"
|
|
||||||
height="128px"
|
|
||||||
width="128px" />
|
|
||||||
<filter
|
<filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
id="AI_Sfocatura_4">
|
id="AI_Sfocatura_4">
|
||||||
<feGaussianBlur
|
<feGaussianBlur
|
||||||
stdDeviation="4"
|
id="feGaussianBlur3096"
|
||||||
id="feGaussianBlur3096" />
|
stdDeviation="4" />
|
||||||
</filter>
|
</filter>
|
||||||
<filter
|
<filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
id="AI_Sfocatura_2">
|
id="AI_Sfocatura_2">
|
||||||
<feGaussianBlur
|
<feGaussianBlur
|
||||||
stdDeviation="2"
|
id="feGaussianBlur3099"
|
||||||
id="feGaussianBlur3099" />
|
stdDeviation="2" />
|
||||||
</filter>
|
</filter>
|
||||||
<radialGradient
|
<radialGradient
|
||||||
id="XMLID_12_"
|
|
||||||
cx="69.600098"
|
cx="69.600098"
|
||||||
cy="69.576698"
|
cy="69.576698"
|
||||||
r="58"
|
r="58"
|
||||||
gradientTransform="matrix(1,0,0,-0.1823,0,134.8566)"
|
id="XMLID_12_"
|
||||||
gradientUnits="userSpaceOnUse">
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1,0,0,-0.1823,0,134.8566)">
|
||||||
<stop
|
<stop
|
||||||
offset="0"
|
id="stop3102"
|
||||||
style="stop-color:#000000"
|
style="stop-color:#000000;stop-opacity:1"
|
||||||
id="stop3102" />
|
offset="0" />
|
||||||
<stop
|
<stop
|
||||||
offset="1"
|
id="stop3104"
|
||||||
style="stop-color:#000000;stop-opacity:0;"
|
style="stop-color:#000000;stop-opacity:0"
|
||||||
id="stop3104" />
|
offset="1" />
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
<circle
|
<circle
|
||||||
sodipodi:ry="58"
|
cx="69.599998"
|
||||||
sodipodi:rx="58"
|
|
||||||
sodipodi:cy="69.599998"
|
|
||||||
sodipodi:cx="69.599998"
|
|
||||||
style="opacity:0.7;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5097)"
|
|
||||||
id="circle5091"
|
|
||||||
r="58"
|
|
||||||
cy="69.599998"
|
cy="69.599998"
|
||||||
cx="69.599998"
|
r="58"
|
||||||
transform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)" /><ellipse
|
transform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)"
|
||||||
cx="69.599998"
|
id="circle5091"
|
||||||
cy="122.173"
|
style="opacity:0.7;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5097)" />
|
||||||
rx="58"
|
|
||||||
ry="10.573"
|
|
||||||
id="ellipse3106"
|
|
||||||
style="opacity:0.6;fill:url(#XMLID_12_)"
|
|
||||||
sodipodi:cx="69.599998"
|
|
||||||
sodipodi:cy="122.173"
|
|
||||||
sodipodi:rx="58"
|
|
||||||
sodipodi:ry="10.573"
|
|
||||||
transform="translate(-9.9998474e-2,1.9102535)" />
|
|
||||||
|
|
||||||
<radialGradient
|
<radialGradient
|
||||||
id="XMLID_13_"
|
|
||||||
cx="69.600098"
|
cx="69.600098"
|
||||||
cy="69.600098"
|
cy="69.600098"
|
||||||
r="58"
|
r="58"
|
||||||
|
id="XMLID_13_"
|
||||||
gradientUnits="userSpaceOnUse">
|
gradientUnits="userSpaceOnUse">
|
||||||
<stop
|
<stop
|
||||||
offset="0.6154"
|
id="stop3113"
|
||||||
style="stop-color:#EEEEEE"
|
style="stop-color:#eeeeee;stop-opacity:1"
|
||||||
id="stop3113" />
|
offset="0.61540002" />
|
||||||
<stop
|
<stop
|
||||||
offset="0.8225"
|
id="stop3115"
|
||||||
style="stop-color:#DDDDDD"
|
style="stop-color:#dddddd;stop-opacity:1"
|
||||||
id="stop3115" />
|
offset="0.82249999" />
|
||||||
<stop
|
<stop
|
||||||
offset="1"
|
id="stop3117"
|
||||||
style="stop-color:#FFFFFF"
|
style="stop-color:#ffffff;stop-opacity:1"
|
||||||
id="stop3117" />
|
offset="1" />
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
<circle
|
<circle
|
||||||
cx="69.599998"
|
cx="69.599998"
|
||||||
cy="69.599998"
|
cy="69.599998"
|
||||||
r="58"
|
r="58"
|
||||||
|
transform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)"
|
||||||
id="circle3119"
|
id="circle3119"
|
||||||
style="fill:url(#XMLID_13_)"
|
style="fill:url(#XMLID_13_)" />
|
||||||
sodipodi:cx="69.599998"
|
|
||||||
sodipodi:cy="69.599998"
|
|
||||||
sodipodi:rx="58"
|
|
||||||
sodipodi:ry="58"
|
|
||||||
transform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)" />
|
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="XMLID_14_"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="27.6001"
|
x1="27.6001"
|
||||||
y1="69.600098"
|
y1="69.600098"
|
||||||
x2="111.6001"
|
x2="111.6001"
|
||||||
y2="69.600098"
|
y2="69.600098"
|
||||||
|
id="XMLID_14_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
gradientTransform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)">
|
gradientTransform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)">
|
||||||
<stop
|
<stop
|
||||||
offset="0"
|
id="stop3122"
|
||||||
style="stop-color:#2A94EC"
|
style="stop-color:#2a94ec;stop-opacity:1"
|
||||||
id="stop3122" />
|
offset="0" />
|
||||||
<stop
|
<stop
|
||||||
offset="1"
|
id="stop3124"
|
||||||
style="stop-color:#0057AE"
|
style="stop-color:#0057ae;stop-opacity:1"
|
||||||
id="stop3124" />
|
offset="1" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<path
|
<path
|
||||||
d="M 26.062502,67.328127 C 26.062502,92.477355 46.522651,112.9375 71.671877,112.9375 C 96.821104,112.9375 117.28125,92.477355 117.28125,67.328127 C 117.28125,42.178901 96.821104,21.718753 71.671877,21.718753 C 46.522651,21.718753 26.062502,42.178901 26.062502,67.328127 z"
|
d="m 26.062502,67.328127 c 0,25.149228 20.460149,45.609373 45.609375,45.609373 25.149227,0 45.609373,-20.460145 45.609373,-45.609373 0,-25.149226 -20.460146,-45.609374 -45.609373,-45.609374 -25.149226,0 -45.609375,20.460148 -45.609375,45.609374 z"
|
||||||
id="path3126"
|
id="path3126"
|
||||||
style="fill:url(#XMLID_14_)" />
|
style="fill:url(#XMLID_14_)" />
|
||||||
<g
|
<g
|
||||||
|
transform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)"
|
||||||
id="circle22111"
|
id="circle22111"
|
||||||
cy="92"
|
style="opacity:0.3;filter:url(#filter3547)">
|
||||||
rx="36"
|
|
||||||
ry="36"
|
|
||||||
cx="343.99899"
|
|
||||||
enable-background="new "
|
|
||||||
style="opacity:0.3;filter:url(#filter3547)"
|
|
||||||
transform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)">
|
|
||||||
<path
|
<path
|
||||||
d="M 77.041,104.759 C 63.767,106.115 50.122,103.11 46.565,98.042 C 43.007,92.976 50.885,87.768 64.16,86.41 C 77.434,85.054 91.079,88.058 94.637,93.126 C 98.193,98.194 90.315,103.401 77.041,104.759 z"
|
d="M 77.041,104.759 C 63.767,106.115 50.122,103.11 46.565,98.042 43.007,92.976 50.885,87.768 64.16,86.41 c 13.274,-1.356 26.919,1.648 30.477,6.716 3.556,5.068 -4.322,10.275 -17.596,11.633 z"
|
||||||
id="path3129"
|
id="path3129"
|
||||||
style="fill:#a8dde0" />
|
style="fill:#a8dde0" />
|
||||||
</g>
|
</g>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="circle16776_1_"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x1="135.5601"
|
x1="135.5601"
|
||||||
y1="417.66461"
|
y1="417.66461"
|
||||||
x2="161.87621"
|
x2="161.87621"
|
||||||
y2="417.66461"
|
y2="417.66461"
|
||||||
|
id="circle16776_1_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
gradientTransform="matrix(0,1.7280523,1.7280523,0,-650.07477,-218.71693)">
|
gradientTransform="matrix(0,1.7280523,1.7280523,0,-650.07477,-218.71693)">
|
||||||
<stop
|
<stop
|
||||||
offset="0"
|
id="stop3132"
|
||||||
style="stop-color:#FFFFFF"
|
style="stop-color:#ffffff;stop-opacity:1"
|
||||||
id="stop3132" />
|
offset="0" />
|
||||||
<stop
|
<stop
|
||||||
offset="1"
|
id="stop3134"
|
||||||
style="stop-color:#ffffff;stop-opacity:0;"
|
style="stop-color:#ffffff;stop-opacity:0"
|
||||||
id="stop3134" />
|
offset="1" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<path
|
<path
|
||||||
|
d="m 71.671877,24.06655 c -21.383195,0 -39.252297,14.70468 -43.558039,34.283047 8.584336,7.792687 24.872313,-3.99082 43.558039,-3.99082 18.685727,0 34.974783,11.783507 43.558033,3.99082 C 110.92417,38.77123 93.056158,24.06655 71.671877,24.06655 z"
|
||||||
id="circle16776"
|
id="circle16776"
|
||||||
enable-background="new "
|
|
||||||
d="M 71.671877,24.06655 C 50.288682,24.06655 32.41958,38.77123 28.113838,58.349597 C 36.698174,66.142284 52.986151,54.358777 71.671877,54.358777 C 90.357604,54.358777 106.64666,66.142284 115.22991,58.349597 C 110.92417,38.77123 93.056158,24.06655 71.671877,24.06655 z"
|
|
||||||
style="opacity:0.8;fill:url(#circle16776_1_)" />
|
style="opacity:0.8;fill:url(#circle16776_1_)" />
|
||||||
<g
|
<g
|
||||||
id="g3137"
|
transform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)"
|
||||||
transform="matrix(1.0859375,0,0,1.0859375,-3.9093733,-8.2531233)">
|
id="g3137">
|
||||||
<defs
|
<defs
|
||||||
id="defs3139"><path
|
id="defs3139"><path
|
||||||
id="XMLID_10_"
|
d="m 27.6,69.6 c 0,23.159 18.841,42 42,42 23.159,0 42,-18.841 42,-42 0,-23.159 -18.841,-42 -42,-42 -23.159,0 -42,18.841 -42,42 z"
|
||||||
d="M 27.6,69.6 C 27.6,92.759 46.441,111.6 69.6,111.6 C 92.759,111.6 111.6,92.759 111.6,69.6 C 111.6,46.441 92.759,27.6 69.6,27.6 C 46.441,27.6 27.6,46.441 27.6,69.6 z" /></defs>
|
id="XMLID_10_" /></defs>
|
||||||
<clipPath
|
<clipPath
|
||||||
id="XMLID_6_">
|
id="XMLID_6_">
|
||||||
<use
|
<use
|
||||||
xlink:href="#XMLID_10_"
|
|
||||||
id="use3143"
|
id="use3143"
|
||||||
x="0"
|
x="0"
|
||||||
y="0"
|
y="0"
|
||||||
width="139"
|
width="139"
|
||||||
height="139" />
|
height="139"
|
||||||
|
xlink:href="#XMLID_10_" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<g
|
<g
|
||||||
clip-path="url(#XMLID_6_)"
|
clip-path="url(#XMLID_6_)"
|
||||||
id="g3145"
|
id="g3145"
|
||||||
style="filter:url(#AI_Sfocatura_2)">
|
style="filter:url(#AI_Sfocatura_2)">
|
||||||
<path
|
<path
|
||||||
d="M 27.6,69.6 C 27.6,92.759 46.441,111.6 69.6,111.6 C 92.759,111.6 111.6,92.759 111.6,69.6 C 111.6,46.441 92.759,27.6 69.6,27.6 C 46.441,27.6 27.6,46.441 27.6,69.6 z"
|
d="m 27.6,69.6 c 0,23.159 18.841,42 42,42 23.159,0 42,-18.841 42,-42 0,-23.159 -18.841,-42 -42,-42 -23.159,0 -42,18.841 -42,42 z"
|
||||||
id="path3147"
|
id="path3147"
|
||||||
style="fill:none;stroke:#00316e;stroke-width:2" />
|
style="fill:none;stroke:#00316e;stroke-width:2" />
|
||||||
</g>
|
</g>
|
||||||
@ -240,30 +182,22 @@
|
|||||||
transform="matrix(1.0859375,0,0,1.1113796,-3.201342,-9.3177223)"
|
transform="matrix(1.0859375,0,0,1.1113796,-3.201342,-9.3177223)"
|
||||||
id="g5119"
|
id="g5119"
|
||||||
style="fill:#00316e;filter:url(#filter5125)"><path
|
style="fill:#00316e;filter:url(#filter5125)"><path
|
||||||
style="fill:#00316e"
|
d="m 63.37,80.089 -0.178,-2.343 c -0.18,-4.598 1.248,-9.284 5.259,-14.062 2.853,-3.424 5.169,-6.398 5.169,-9.463 0,-3.064 -2.049,-5.227 -6.418,-5.318 -3.029,0 -6.506,0.992 -8.913,2.614 l -2.941,-9.733 c 3.208,-1.894 8.467,-3.696 14.885,-3.696 11.677,0 17.115,6.58 17.115,13.97 0,6.939 -4.279,11.357 -7.667,15.231 -3.209,3.605 -4.635,7.121 -4.546,11.177 l 0,1.622 -11.765,0 0,0.001 z"
|
||||||
d="M 63.37,80.089 L 63.192,77.746 C 63.012,73.148 64.44,68.462 68.451,63.684 C 71.304,60.26 73.62,57.286 73.62,54.221 C 73.62,51.157 71.571,48.994 67.202,48.903 C 64.173,48.903 60.696,49.895 58.289,51.517 L 55.348,41.784 C 58.556,39.89 63.815,38.088 70.233,38.088 C 81.91,38.088 87.348,44.668 87.348,52.058 C 87.348,58.997 83.069,63.415 79.681,67.289 C 76.472,70.894 75.046,74.41 75.135,78.466 L 75.135,80.088 L 63.37,80.088 L 63.37,80.089 z"
|
id="path5121"
|
||||||
id="path5121" /><circle
|
style="fill:#00316e" /><circle
|
||||||
style="fill:#00316e"
|
|
||||||
sodipodi:ry="8"
|
|
||||||
sodipodi:rx="8"
|
|
||||||
sodipodi:cy="93.599998"
|
|
||||||
sodipodi:cx="69.599998"
|
|
||||||
cx="69.599998"
|
cx="69.599998"
|
||||||
cy="93.599998"
|
cy="93.599998"
|
||||||
r="8"
|
r="8"
|
||||||
id="circle5123" /></g><g
|
id="circle5123"
|
||||||
id="g5101"
|
style="fill:#00316e" /></g><g
|
||||||
transform="matrix(1.0859375,0,0,1.0859375,-3.201342,-8.2531233)"><path
|
transform="matrix(1.0859375,0,0,1.0859375,-3.201342,-8.2531233)"
|
||||||
|
id="g5101"><path
|
||||||
|
d="m 63.37,80.089 -0.178,-2.343 c -0.18,-4.598 1.248,-9.284 5.259,-14.062 2.853,-3.424 5.169,-6.398 5.169,-9.463 0,-3.064 -2.049,-5.227 -6.418,-5.318 -3.029,0 -6.506,0.992 -8.913,2.614 l -2.941,-9.733 c 3.208,-1.894 8.467,-3.696 14.885,-3.696 11.677,0 17.115,6.58 17.115,13.97 0,6.939 -4.279,11.357 -7.667,15.231 -3.209,3.605 -4.635,7.121 -4.546,11.177 l 0,1.622 -11.765,0 0,0.001 z"
|
||||||
id="path3157"
|
id="path3157"
|
||||||
d="M 63.37,80.089 L 63.192,77.746 C 63.012,73.148 64.44,68.462 68.451,63.684 C 71.304,60.26 73.62,57.286 73.62,54.221 C 73.62,51.157 71.571,48.994 67.202,48.903 C 64.173,48.903 60.696,49.895 58.289,51.517 L 55.348,41.784 C 58.556,39.89 63.815,38.088 70.233,38.088 C 81.91,38.088 87.348,44.668 87.348,52.058 C 87.348,58.997 83.069,63.415 79.681,67.289 C 76.472,70.894 75.046,74.41 75.135,78.466 L 75.135,80.088 L 63.37,80.088 L 63.37,80.089 z"
|
|
||||||
style="fill:#ffffff" /><circle
|
style="fill:#ffffff" /><circle
|
||||||
id="circle3159"
|
|
||||||
r="8"
|
|
||||||
cy="93.599998"
|
|
||||||
cx="69.599998"
|
cx="69.599998"
|
||||||
sodipodi:cx="69.599998"
|
cy="93.599998"
|
||||||
sodipodi:cy="93.599998"
|
r="8"
|
||||||
sodipodi:rx="8"
|
id="circle3159"
|
||||||
sodipodi:ry="8"
|
|
||||||
style="fill:#ffffff" /></g>
|
style="fill:#ffffff" /></g>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 6.3 KiB |
BIN
resources/images/lt.png
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 28 KiB |
@ -1,8 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
xmlns:cc="http://web.resource.org/cc/"
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -10,106 +11,573 @@
|
|||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
version="1.0"
|
version="1.0"
|
||||||
width="48"
|
width="128"
|
||||||
height="48"
|
height="128"
|
||||||
id="svg2160"
|
id="svg4486"
|
||||||
sodipodi:version="0.32"
|
inkscape:version="0.47 r22583"
|
||||||
inkscape:version="0.45.1"
|
sodipodi:docname="epub.svg">
|
||||||
sodipodi:docname="mobi.svg"
|
|
||||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
|
||||||
sodipodi:docbase="/home/kovid/temp">
|
|
||||||
<sodipodi:namedview
|
|
||||||
inkscape:window-height="674"
|
|
||||||
inkscape:window-width="791"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
guidetolerance="10.0"
|
|
||||||
gridtolerance="10.0"
|
|
||||||
objecttolerance="10.0"
|
|
||||||
borderopacity="1.0"
|
|
||||||
bordercolor="#666666"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
id="base"
|
|
||||||
inkscape:zoom="9.6458333"
|
|
||||||
inkscape:cx="24"
|
|
||||||
inkscape:cy="24"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="31"
|
|
||||||
inkscape:current-layer="svg2160" />
|
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata7">
|
id="metadata52">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
<cc:Work
|
<cc:Work
|
||||||
rdf:about="">
|
rdf:about="">
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1366"
|
||||||
|
inkscape:window-height="692"
|
||||||
|
id="namedview50"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:zoom="2.3256144"
|
||||||
|
inkscape:cx="73.759964"
|
||||||
|
inkscape:cy="13.023094"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4486" />
|
||||||
<defs
|
<defs
|
||||||
id="defs2162" />
|
id="defs4488">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3672">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#2b2b2b;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3674" />
|
||||||
|
<stop
|
||||||
|
id="stop3685"
|
||||||
|
offset="0.5"
|
||||||
|
style="stop-color:#242424;stop-opacity:1;" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#363636;stop-opacity:1;"
|
||||||
|
offset="0.75"
|
||||||
|
id="stop3689" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#2b2b2b;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3676" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 64 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="128 : 64 : 1"
|
||||||
|
inkscape:persp3d-origin="64 : 42.666667 : 1"
|
||||||
|
id="perspective54" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient5048">
|
||||||
|
<stop
|
||||||
|
id="stop5050"
|
||||||
|
style="stop-color:#000000;stop-opacity:0"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop5056"
|
||||||
|
style="stop-color:#000000;stop-opacity:1"
|
||||||
|
offset="0.5" />
|
||||||
|
<stop
|
||||||
|
id="stop5052"
|
||||||
|
style="stop-color:#000000;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient5060">
|
||||||
|
<stop
|
||||||
|
id="stop5062"
|
||||||
|
style="stop-color:#000000;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop5064"
|
||||||
|
style="stop-color:#000000;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3104">
|
||||||
|
<stop
|
||||||
|
id="stop3106"
|
||||||
|
style="stop-color:#aaaaaa;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3108"
|
||||||
|
style="stop-color:#c8c8c8;stop-opacity:1"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3600">
|
||||||
|
<stop
|
||||||
|
id="stop3602"
|
||||||
|
style="stop-color:#f4f4f4;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3604"
|
||||||
|
style="stop-color:#dbdbdb;stop-opacity:1"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.55859"
|
||||||
|
id="XMLID_8_"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
id="stop41"
|
||||||
|
style="stop-color:#b7b8b9;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop47"
|
||||||
|
style="stop-color:#ececec;stop-opacity:1"
|
||||||
|
offset="0.18851049" />
|
||||||
|
<stop
|
||||||
|
id="stop49"
|
||||||
|
style="stop-color:#fafafa;stop-opacity:0"
|
||||||
|
offset="0.25718147" />
|
||||||
|
<stop
|
||||||
|
id="stop51"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0"
|
||||||
|
offset="0.30111277" />
|
||||||
|
<stop
|
||||||
|
id="stop53"
|
||||||
|
style="stop-color:#fafafa;stop-opacity:0"
|
||||||
|
offset="0.53130001" />
|
||||||
|
<stop
|
||||||
|
id="stop55"
|
||||||
|
style="stop-color:#ebecec;stop-opacity:0"
|
||||||
|
offset="0.84490001" />
|
||||||
|
<stop
|
||||||
|
id="stop57"
|
||||||
|
style="stop-color:#e1e2e3;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</radialGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3211">
|
||||||
|
<stop
|
||||||
|
id="stop3213"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3215"
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient8589">
|
||||||
|
<stop
|
||||||
|
id="stop8591"
|
||||||
|
style="stop-color:#fefefe;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop8593"
|
||||||
|
style="stop-color:#cbcbcb;stop-opacity:1"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<filter
|
||||||
|
x="-0.14846256"
|
||||||
|
y="-0.16434373"
|
||||||
|
width="1.2969251"
|
||||||
|
height="1.3286875"
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
id="filter3212">
|
||||||
|
<feGaussianBlur
|
||||||
|
stdDeviation="0.77391625"
|
||||||
|
id="feGaussianBlur3214" />
|
||||||
|
</filter>
|
||||||
|
<linearGradient
|
||||||
|
x1="32.892288"
|
||||||
|
y1="8.0590115"
|
||||||
|
x2="36.358372"
|
||||||
|
y2="5.4565363"
|
||||||
|
id="linearGradient2455"
|
||||||
|
xlink:href="#linearGradient8589"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(2.6605294,0,0,2.7751643,0.7455334,-3.5662351)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="24"
|
||||||
|
y1="1.9999999"
|
||||||
|
x2="24"
|
||||||
|
y2="46.01725"
|
||||||
|
id="linearGradient2459"
|
||||||
|
xlink:href="#linearGradient3211"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(2.7575715,0,0,2.6744155,-2.1817183,-4.1859717)" />
|
||||||
|
<radialGradient
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.55859"
|
||||||
|
id="radialGradient2462"
|
||||||
|
xlink:href="#XMLID_8_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.9787237,0,0,-1.0535153,1.3617012,127.48164)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="25.132275"
|
||||||
|
y1="0.98520643"
|
||||||
|
x2="25.132275"
|
||||||
|
y2="47.013336"
|
||||||
|
id="linearGradient2465"
|
||||||
|
xlink:href="#linearGradient3600"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(2.657139,0,0,2.5422197,0.2286615,-4.9132741)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="-51.786404"
|
||||||
|
y1="50.786446"
|
||||||
|
x2="-51.786404"
|
||||||
|
y2="2.9062471"
|
||||||
|
id="linearGradient2467"
|
||||||
|
xlink:href="#linearGradient3104"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(2.1456312,0,0,2.3791388,158.0899,-7.7465258)" />
|
||||||
|
<linearGradient
|
||||||
|
x1="302.85715"
|
||||||
|
y1="366.64789"
|
||||||
|
x2="302.85715"
|
||||||
|
y2="609.50507"
|
||||||
|
id="linearGradient2483"
|
||||||
|
xlink:href="#linearGradient5048"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.1725148,0,0,0.03920058,0.6482238,98.773724)" />
|
||||||
|
<radialGradient
|
||||||
|
cx="605.71429"
|
||||||
|
cy="486.64789"
|
||||||
|
r="117.14286"
|
||||||
|
fx="605.71429"
|
||||||
|
fy="486.64789"
|
||||||
|
id="radialGradient2485"
|
||||||
|
xlink:href="#linearGradient5060"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-0.05903807,0,0,0.03920058,56.929907,98.773804)" />
|
||||||
|
<radialGradient
|
||||||
|
cx="605.71429"
|
||||||
|
cy="486.64789"
|
||||||
|
r="117.14286"
|
||||||
|
fx="605.71429"
|
||||||
|
fy="486.64789"
|
||||||
|
id="radialGradient2487"
|
||||||
|
xlink:href="#linearGradient5060"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.05903808,0,0,0.03920058,69.07008,98.773804)" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective2878"
|
||||||
|
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||||
|
inkscape:vp_z="1 : 0.5 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 0.5 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient7012-661-145-733-759-865-745-661-970-94"
|
||||||
|
id="linearGradient2989"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.7993221,0,0,1.0036506,40.855793,-1.5607197)"
|
||||||
|
x1="-22.539846"
|
||||||
|
y1="11.109024"
|
||||||
|
x2="-22.539846"
|
||||||
|
y2="46.263954" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient7012-661-145-733-759-865-745-661-970-94">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#f0c178;stop-opacity:1"
|
||||||
|
id="stop3618" />
|
||||||
|
<stop
|
||||||
|
offset="0.5"
|
||||||
|
style="stop-color:#e18941;stop-opacity:1"
|
||||||
|
id="stop3270" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#ec4f18;stop-opacity:1"
|
||||||
|
id="stop3620" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3390-178-986-453"
|
||||||
|
id="linearGradient2991"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.9110051,0,0,0.9769973,0.6027366,-0.94793564)"
|
||||||
|
x1="9.4919996"
|
||||||
|
y1="46.314064"
|
||||||
|
x2="9.4919996"
|
||||||
|
y2="1.7164899" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3390-178-986-453">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#c92e13;stop-opacity:1;"
|
||||||
|
id="stop3624" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#dea176;stop-opacity:1;"
|
||||||
|
id="stop3626" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
y2="46.263954"
|
||||||
|
x2="-22.539846"
|
||||||
|
y1="11.109024"
|
||||||
|
x1="-22.539846"
|
||||||
|
gradientTransform="matrix(1.2084176,0,0,2.666214,69.448297,-3.8858475)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient2892"
|
||||||
|
xlink:href="#linearGradient7012-661-145-733-759-865-745-661-970-94"
|
||||||
|
inkscape:collect="always" />
|
||||||
|
<linearGradient
|
||||||
|
y2="1.7164899"
|
||||||
|
x2="9.4919996"
|
||||||
|
y1="46.314064"
|
||||||
|
x1="9.4919996"
|
||||||
|
gradientTransform="matrix(1.3772603,0,0,2.595409,8.5935979,-2.257977)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient2894"
|
||||||
|
xlink:href="#linearGradient3390-178-986-453"
|
||||||
|
inkscape:collect="always" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective3666"
|
||||||
|
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||||
|
inkscape:vp_z="1 : 0.5 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 0.5 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
<linearGradient
|
||||||
|
x1="302.85715"
|
||||||
|
y1="366.64789"
|
||||||
|
x2="302.85715"
|
||||||
|
y2="609.50507"
|
||||||
|
id="linearGradient19619"
|
||||||
|
xlink:href="#linearGradient5048-1"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.08449704,0,0,0.01235294,-6.5396456,38.470822)" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient5048-1">
|
||||||
|
<stop
|
||||||
|
id="stop5050-2"
|
||||||
|
style="stop-color:black;stop-opacity:0"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop5056-0"
|
||||||
|
style="stop-color:black;stop-opacity:1"
|
||||||
|
offset="0.5" />
|
||||||
|
<stop
|
||||||
|
id="stop5052-7"
|
||||||
|
style="stop-color:black;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
cx="605.71429"
|
||||||
|
cy="486.64789"
|
||||||
|
r="117.14286"
|
||||||
|
fx="605.71429"
|
||||||
|
fy="486.64789"
|
||||||
|
id="radialGradient19616"
|
||||||
|
xlink:href="#linearGradient5060-3"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-0.0289166,0,0,0.01235294,21.026894,38.470848)" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient5060-3">
|
||||||
|
<stop
|
||||||
|
id="stop5062-1"
|
||||||
|
style="stop-color:black;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop5064-1"
|
||||||
|
style="stop-color:black;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient
|
||||||
|
cx="605.71429"
|
||||||
|
cy="486.64789"
|
||||||
|
r="117.14286"
|
||||||
|
fx="605.71429"
|
||||||
|
fy="486.64789"
|
||||||
|
id="radialGradient19613"
|
||||||
|
xlink:href="#linearGradient5060-3"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.02891661,0,0,0.01235294,26.973101,38.470848)" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3680">
|
||||||
|
<stop
|
||||||
|
id="stop3682"
|
||||||
|
style="stop-color:black;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3684"
|
||||||
|
style="stop-color:black;stop-opacity:0"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
x1="108.26451"
|
||||||
|
y1="110.28094"
|
||||||
|
x2="25.817675"
|
||||||
|
y2="14.029031"
|
||||||
|
id="linearGradient19610"
|
||||||
|
xlink:href="#linearGradient259-942"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.5066363,0,0,0.3512482,-58.338079,-49.085986)" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient259-942">
|
||||||
|
<stop
|
||||||
|
id="stop3802"
|
||||||
|
style="stop-color:white;stop-opacity:1"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop3804"
|
||||||
|
style="stop-color:#e0e0e0;stop-opacity:1"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3650"
|
||||||
|
id="linearGradient3656"
|
||||||
|
x1="-44.02877"
|
||||||
|
y1="-26.590452"
|
||||||
|
x2="-4.1013746"
|
||||||
|
y2="-26.590452"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient3650">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#73d216;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3652" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#8ae234;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3654" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3672"
|
||||||
|
id="linearGradient3682"
|
||||||
|
x1="32.254131"
|
||||||
|
y1="57.967407"
|
||||||
|
x2="98.357651"
|
||||||
|
y2="57.967407"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(19.439951,41.709408)" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective2970"
|
||||||
|
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||||
|
inkscape:vp_z="1 : 0.5 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 0.5 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective2984"
|
||||||
|
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||||
|
inkscape:vp_z="1 : 0.5 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 0.5 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective3890"
|
||||||
|
inkscape:persp3d-origin="150 : 23.333333 : 1"
|
||||||
|
inkscape:vp_z="300 : 35 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 35 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective3915"
|
||||||
|
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||||
|
inkscape:vp_z="1 : 0.5 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 0.5 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective4057"
|
||||||
|
inkscape:persp3d-origin="150 : 23.333333 : 1"
|
||||||
|
inkscape:vp_z="300 : 35 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 35 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective4082"
|
||||||
|
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||||
|
inkscape:vp_z="1 : 0.5 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 0.5 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
</defs>
|
||||||
<g
|
<g
|
||||||
id="layer1">
|
transform="matrix(1.0408163,0,0,0.6302428,-1.5714269,43.690218)"
|
||||||
<image
|
id="g2478">
|
||||||
id="image2226"
|
<rect
|
||||||
width="48"
|
width="83.299995"
|
||||||
y="0"
|
height="9.5201406"
|
||||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI
|
x="21.349998"
|
||||||
WXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAwAAAAMADO7oxXAAALJUlEQVRo3u2ae3DTVRbHP3k1
|
y="113.14653"
|
||||||
v6ZJk4a+W1NalAJSy4qtvKS4iCKOYtcHvvCxDuuMozu62vVRdGcRfOzsODoq7o4jyoyy49aFqYoO
|
id="rect2879"
|
||||||
7rJjaREsKi2F0sC2CNImado82ib5/fJo9o+0vzS2pe2i4B97/rq553fu7/u9597zu+fcKB56+OFo
|
style="opacity:0.3;fill:url(#linearGradient2483);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||||
bnY2fr8fKRgkIIpIosiwaAUBAK/HA8A0s1nW6fV6RsrGTZsUnGNRPL9pU/T3TzyZ0OnxuEe0PUiS
|
<path
|
||||||
hBiIkero6MDb50UMBJCCQex2OwFRRK1UyjY+vx+320NamglRFDGaTLz+2ms/CTn1WJ0mU9qYbYDS
|
d="m 21.35,113.14694 c 0,0 0,9.51962 0,9.51962 -3.040314,0.0179 -7.35,-2.13287 -7.35,-4.76043 0,-2.62755 3.392762,-4.75919 7.35,-4.75919 z"
|
||||||
efPGHWyY+GPbtuNtP8ZTa29HCkp8unMn66uroz+Fh9RnP0RcPm5u5s26Iyj1M3nghhky2dbWVg63
|
id="path2881"
|
||||||
tPzY2M+ewMleD0fajnGk8yQ72rpR6meSlrsU6/F/smzWHT8J4EkR2NnUwvYvGznaI57RWKmfiV6n
|
style="opacity:0.3;fill:url(#radialGradient2485);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||||
J90wl5yCKGIogj8cpaLAiEGrPX8ENn20h7WrbuayEX2u/hAAPilMMDRIIDSIFB5EDEUIhOPgHQMe
|
<path
|
||||||
VllSzwl4AOVkHposeF84QtD5NeuuvurnQ2Aq4AfEfioKjOcM/IQEpgLeGx7E2dPOQ1cv+3kQmDL4
|
d="m 104.64999,113.14694 c 0,0 0,9.51962 0,9.51962 3.04032,0.0179 7.35001,-2.13287 7.35001,-4.76043 0,-2.62755 -3.39277,-4.75919 -7.35001,-4.75919 z"
|
||||||
cJQbLIMUppsn/fKflMBUwfedbuaxFVeeU/BnJDAl8JFB7pkJuRnxr7YoipMCcNYE/H7/mIqpgKdj
|
id="path2883"
|
||||||
d8LsnyvwcAYPTBq8388Lyy9khiX350VgUuCBctURfrv6+kRbSTr/BCYDPs19hDfuWDnaNvAz8MBE
|
style="opacity:0.3;fill:url(#radialGradient2487);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||||
4LG18PK1hcy5IG+Uba+r9/wTmAj8O5UzuHXBglF2nqHM7bwTONOyeadyBmsrlo1p5/V4zymBcfOB
|
|
||||||
8Wb+hpmMCx6g025HSNJM+OL11dVRKRiUfw977q233ppS1jYugVHgxTCkzWbrKRtsfpu3H7x/TLtu
|
|
||||||
uw2LxTKmrqqqKhoQRfqkKKecMU8lZZoJdrvQAnapi0WLFkUBcnPz+PDDmgnJjEtgFPhhSc5h6ylg
|
|
||||||
89tsvv/OBBuPx4MYCAAgDFUzACwWC3V1dSiSjcxetoQ8IJixEJ8UJq3vAMyBJFsns5iF7cFaQnXT
|
|
||||||
cLZFKCsrixYVXcQHH2wbl8i4e2BM8AkkTPx9//6E7tY2KwAqTTwbe/fdd/j6wAHmXHsdV66pRF+0
|
|
||||||
FE3uLMwGDSlaNe7UMvlZ2+JacvauRlPRS+4DPVTeeCP9Of9hxYoV0SkTGBf8CBKP7mhM6DpubUOt
|
|
||||||
UsneePyx3wGx2lJawWwGUmYP/c7E1R+SSTiS5+PTGcjZuxrb4lrMu68BwLV8F8svWMOcq/QsWrFo
|
|
||||||
TBLjEzgT+CHxCDNkL9hsNux2u6x78smnmFtSAsCMOSUUqsKsKtSywDQAQJJzX8JYUr9IS84qnI13
|
|
||||||
oRUEzLuvQWfVYltci0W9hMsrBXl/TN4DE0lyDl8caQdg/4jltGP7hyxcuACLxYKqsBCtUsWg1yPr
|
|
||||||
NREnIW8fAx178ElhkjRKfBddS7SnHk/KQnw6A67luwDI2bsaSRTJcC/HVB5mzZo7EkicHQGQKxf7
|
|
||||||
v/oKgPYTJ0gWBMzz5tHd3U1+RhYhQYc0GKHl22843Pk9IVUGutnl6IuWkqJVk6KNxRJF+hUkqxX0
|
|
||||||
/eI9efxDGgNWQwau5bu4NGUFfn//j+iBIdm7dy92W2z5dPa0U1xcTF4kIuu7jx/h+/YTlFw6n7l5
|
|
||||||
FwCxfSCJ3UAs9xiWS07Xjhp/n/8iAHQ6HWk5BVRVVcleOGsClbMyqampAWIbN2NWFEEQ6FSpEEUR
|
|
||||||
x6EmLBYLupRkXO5e9nviBeFQV5vcDoRiUc9qyEC752YAPnG/gFW0cCwi8NEXf+Rd1SLasmcmHFeU
|
|
||||||
I7+GU5aAjYHT3TQ1NQFgt9spmXYdmspdRE6cAKAgP3bYi0aCCeABHMnzRw3pSVlIUsgvk2gdTCNV
|
|
||||||
peRYRGCh7jhrjCF84fhhUR04i+RjevQk1sZ4KDWka2Ifsr9VIAgCzp5eGrocZGdl0OnoIY9Pcer0
|
|
||||||
6IuW4uoPkaRR4h6ITaAUHsQdCGPy7aPDYKZDWYZXjBUWKnXNmAKfoSnvhb2r0RjjS04pjUFgdrrA
|
|
||||||
hBKwcYmqg4aGehTKWOyfe+F8hj2qqdyFRqUk5O0jcuIEeVnpGG6vw9DrlCseAFmBb+TlM1KKBg9w
|
|
||||||
Z0Y1v1lWRYGxBk1FfNb7uuLPqQEkSUSrjYNeYdaxtbkOcivGBU/DmzR+d3RMteH2OrldfHe93HZu
|
|
||||||
Kad/WgYQLxpYKYWh2R+WAmMN/mKJsbP1RFECuN3uhM7bbrudu1V26KqLgR0JvKsOGt5E/XU9fp+P
|
|
||||||
oqIiysvLWHPrrfg0hnFf5NxSji0QxZ1allDxEEMRxFAsYomhiAxeZ51ccVgN4LA7yM7OSVBs2fwq
|
|
||||||
S999h+0fb+PL7xspzb2MFJWSJeVlZC68k+IXn2XhkqUJNk0vv4EroiF/6LcUDOLcUo5LrcWVfyVZ
|
|
||||||
gW9wDAGH2LoPhGMR0RWY+Muvs2pRqhLPn2qAg00Hx7x5uffe+7j33vtG9dfVN1C/t4Gc/AuYPr1Q
|
|
||||||
7p97URG1fdMx/yWEbXEtLD7JaetmjMkaGF4uQ0CHc24AX8ALpNAlhviSP7DI+keMPSvxF9eis2o5
|
|
||||||
6b2Fot0u8rLS6cdHOBD/GCuzMjNpam4eBdLjcVNX30BdfUNC/2uvv85gfx+P5inY8kDicdqkSyYQ
|
|
||||||
jnJIY8DZeBc6q5YZjn9j7w/iDYRwB8JEe+pxB8IEwlFcgbA8811iCGc4SuOAhg2nN1ATuZg/736J
|
|
||||||
Dac3ANBhMOPU6Wk9cpjs7Ow4gY2bNim8Hg92uy0B/MbnniNJMcjsDY/zp5delHXdtk6WuFpwb91B
|
|
||||||
yqGTCQTL5pdi8sUOaR3KMow98YrF8Gx3KucghiIycF84gissYFQr6YsMysf44fh/jyF2NaVIv4Ik
|
|
||||||
Wyd2uz0ha1NCLPlobW2VX7bxued45u5fcem+f6A63Y9vTxxkkuTDvXUHqtOxM0lpycVxD5jSWGHW
|
|
||||||
AaDFx2fayzGrQph8++TZlkghO9iIFh9afJQovyUp2ENSsEceJ1WlpFLXTIZaQYeyDMm4CK1aSVtb
|
|
||||||
W8LsywRSdDpaRlzCGRVhhM9rGNjyr1FLK9TytQw+eOdNo24xV65cSb4ocrn/W1xhAVdEQ1G/Cy0+
|
|
||||||
IBbfi/udzLvsaeZd9jQZ5e+xZOnzFAunmKkSWaCJpZpW0YJRrcScrCY7VUvk0PtoBWFUzqwGeOXV
|
|
||||||
VxVr166Vd0aXL8jAlnj8LrzlJrn967++z9sffEBmVhbP/GCDezxuTn53EoXXweeqLErSv2U3Myk2
|
|
||||||
nOIKbwN6QYNtcS0uwLz7GsT8L2TbfFHkWESAiECqCvQ6PSnJWuYH9yEdF/E5bLw+RsIvd6xbty76
|
|
||||||
yCOPUFw8i+amJrZXrycadqH/5Q388CL8h4APNh2kqblZvs03mkwkCwI1g5lUFBjZ2l/Cs/nPAuAv
|
|
||||||
jpcddVatHPNfsW0kGz92dJTqk8gzaCjVHKa9tRWFwzZutSIhqB48eJDi4lmUzptH6c5PRj1st9uw
|
|
||||||
Hm/n6OFDtB07lgDYlJo66ja+qqoqum3/UcovFIk4rkda+qEMeiSJYbHoNRSqtWQHG8ELrQfqY7f8
|
|
||||||
Zyi1yIr11dVRR3c3TzzxBNOnF2K323DYHRxtO0rb0aM4urvlqvM0sxm9Xj+p/0asr66OnnJ6sSUL
|
|
||||||
5AREpi+OJf5eux5NRa9MIOK4HkdfD6HMfHwH95GmVpOVmTnhOxKU69atiw6DFAQBURQRBGFSA02G
|
|
||||||
iKevD6/HQ7u9HU2GREawiIhSIkmbRCSgIC3NhFYQMKWmnpc/jvxf/hf5L875Zqc0KDqlAAAAAElF
|
|
||||||
TkSuQmCC
|
|
||||||
"
|
|
||||||
x="0"
|
|
||||||
height="48" />
|
|
||||||
</g>
|
</g>
|
||||||
|
<path
|
||||||
|
d="m 17.499961,1.499962 c 21.311039,0 42.622077,0 63.933118,0 3.738416,1.2619859 23.822451,15.639336 29.066961,25.594247 0,30.468614 0,60.937223 0,91.405831 -31.000026,0 -62.000051,0 -93.000079,0 0,-39.000023 0,-78.000052 0,-117.000078 z"
|
||||||
|
id="path4160"
|
||||||
|
style="fill:url(#linearGradient2465);fill-opacity:1;stroke:url(#linearGradient2467);stroke-width:0.99992192;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline" />
|
||||||
|
<path
|
||||||
|
d="M 18.978723,118 C 18.439449,118 18,117.52697 18,116.94648 L 18,3.1668773 C 18,2.5853392 18.439449,2.1133645 18.978723,2.1133645 39.22709,2.4045657 61.66587,1.6777678 81.889633,2.1858707 L 109.71323,26.088148 110,116.94648 C 110,117.52697 109.56154,118 109.02128,118 l -90.042557,0 z"
|
||||||
|
id="path4191"
|
||||||
|
style="fill:url(#radialGradient2462);fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 109.50003,26.517935 c 0,29.94778 0,61.034321 0,90.982105 -30.333353,0 -60.666712,0 -91.00007,0 0,-38.333364 0,-76.666721 0,-115.0000784 20.852737,0 42.202796,0 63.055535,0"
|
||||||
|
id="path2435"
|
||||||
|
style="opacity:0.6;fill:none;stroke:url(#linearGradient2459);stroke-width:0.99992174;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline" />
|
||||||
|
<path
|
||||||
|
d="m 28.617256,0.92125832 c 4.282522,0 2.15325,8.48317128 2.15325,8.48317128 0,0 10.357642,-1.8023467 10.357642,2.8187444 0,-2.6097233 -11.302304,-10.7285956 -12.510892,-11.30191568 z"
|
||||||
|
transform="matrix(2.6666667,0,0,2.6666667,0.3087257,-0.6174513)"
|
||||||
|
id="path12038"
|
||||||
|
style="opacity:0.4;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline;filter:url(#filter3212)" />
|
||||||
|
<path
|
||||||
|
d="m 76.62141,1.8392376 c 8.497314,0 6.228693,20.4316764 6.228693,20.4316764 0,0 27.133687,-2.616144 27.133687,9.706766 0,-3.002504 0.2291,-5.152653 -0.35674,-6.089581 C 105.41822,19.156956 87.239007,4.052365 80.67425,2.0726634 80.182991,1.9245177 79.093706,1.8392376 76.62141,1.8392376 z"
|
||||||
|
id="path4474"
|
||||||
|
style="fill:url(#linearGradient2455);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline" />
|
||||||
|
<path
|
||||||
|
style="fill:url(#linearGradient2892);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2894);stroke-width:1.00930357;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:block;overflow:visible"
|
||||||
|
id="path4530"
|
||||||
|
d="m 25.816692,118.46233 c -2.652245,0 -5.304508,0 -7.956769,0 -0.64465,-1.3837 -0.154446,-4.13089 -0.307623,-6.08978 0,-36.699903 0,-73.399803 0,-110.0996953 l 0.08995,-0.4752429 0.217716,-0.1962367 0,0 c 2.76046,0 5.088125,0 7.848554,0" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||||
|
x="38.310501"
|
||||||
|
y="64.757271"
|
||||||
|
id="text2892"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan2894" /></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
|
||||||
|
x="35.259502"
|
||||||
|
y="109.51025"
|
||||||
|
id="text3772"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3774"
|
||||||
|
x="35.259502"
|
||||||
|
y="109.51025"
|
||||||
|
style="font-size:28px;fill:#000000;fill-opacity:1">mobi</tspan></text>
|
||||||
|
<path
|
||||||
|
id="path3858"
|
||||||
|
d="m 100.22334,68.053513 c -8.716894,6.432161 -21.357191,9.853809 -32.243032,9.853809 -15.251419,0 -28.989193,-5.636741 -39.38419,-15.021522 -0.815557,-0.738364 -0.08726,-1.746902 0.89191,-1.173831 11.217267,6.527812 25.085933,10.456247 39.410201,10.456247 9.664184,0 20.284886,-2.004491 30.058986,-6.151079 1.474215,-0.623415 2.709285,0.97246 1.266125,2.036376 z"
|
||||||
|
style="fill:#ff9201;fill-rule:evenodd" />
|
||||||
|
<path
|
||||||
|
id="path3860"
|
||||||
|
d="m 103.85056,63.911959 c -1.11342,-1.426386 -7.371902,-0.676274 -10.179364,-0.337298 -0.853315,0.09817 -0.984206,-0.641874 -0.217315,-1.184739 4.990672,-3.505554 13.168059,-2.491141 14.119539,-1.318987 0.95736,1.187256 -0.25087,9.384779 -4.92858,13.293915 -0.71907,0.604117 -1.40373,0.286117 -1.08573,-0.510142 1.05133,-2.631263 3.40906,-8.515524 2.29145,-9.942749 z"
|
||||||
|
style="fill:#ff9201;fill-rule:evenodd" />
|
||||||
|
<path
|
||||||
|
id="path4047"
|
||||||
|
d="m 69.299213,48.156661 c 0,2.157 0.052,3.954 -1.035,5.874 -0.88,1.561 -2.279,2.517 -3.833,2.517 -2.121,0 -3.366,-1.62 -3.366,-4.015 0,-4.714 4.232,-5.571 8.233,-5.571 v 1.195 z m 5.579,13.496 c -0.365,0.332 -0.896,0.352 -1.307,0.132 -1.838,-1.528 -2.167,-2.231 -3.174,-3.69 -3.035,3.094 -5.188,4.023 -9.123,4.023 -4.663,0 -8.284,-2.876 -8.284,-8.629 0,-4.49 2.433,-7.544 5.899,-9.045 3.005,-1.317 7.202,-1.556 10.41,-1.914 v -0.723 c 0,-1.313 0.104,-2.875 -0.671,-4.012 -0.674,-1.021 -1.968,-1.437 -3.106,-1.437 -2.111,0 -3.99,1.078 -4.45,3.321 -0.097,0.498 -0.46,0.991 -0.962,1.017 l -5.364,-0.581 c -0.456,-0.102 -0.958,-0.463 -0.828,-1.155 1.233,-6.511 7.109,-8.475 12.378,-8.475 2.693,0 6.215,0.719 8.335,2.757 2.693,2.515 2.432,5.869 2.432,9.524 v 8.623 c 0,2.596 1.081,3.732 2.091,5.128 0.354,0.503 0.434,1.103 -0.018,1.473 -1.131,0.949 -3.139,2.693 -4.244,3.676 l -0.014,-0.013 z"
|
||||||
|
style="fill-rule:evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 22 KiB |
63
resources/images/mimetypes/odt.svg
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" width="128" height="128" id="svg2176">
|
||||||
|
<defs id="defs2178">
|
||||||
|
<linearGradient x1="406.065" y1="290.50299" x2="406.065" y2="276.29501" id="linearGradient4819" xlink:href="#linearGradient7431" gradientUnits="userSpaceOnUse" gradientTransform="matrix(4.80814, 0, 0, 2.87475, -1569.44, -758.786)" spreadMethod="pad"/>
|
||||||
|
<linearGradient x1="68.374298" y1="-410.099" x2="67.912201" y2="-478.508" id="linearGradient4817" xlink:href="#linearGradient11367" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.58223, 0, 0, -0.727268, 275.522, -213.417)" spreadMethod="pad"/>
|
||||||
|
<linearGradient x1="436.48801" y1="-278.91299" x2="436.51199" y2="-299.88699" id="linearGradient4815" xlink:href="#linearGradient11377" gradientUnits="userSpaceOnUse" gradientTransform="matrix(4.59378, 0, 0, 0.359494, -1920.95, 434.897)" spreadMethod="pad"/>
|
||||||
|
<linearGradient id="linearGradient11377">
|
||||||
|
<stop id="stop11379" style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="0"/>
|
||||||
|
<stop id="stop11385" style="stop-color: rgb(255, 255, 255); stop-opacity: 0.575472;" offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient11367">
|
||||||
|
<stop id="stop11369" style="stop-color: rgb(0, 0, 0); stop-opacity: 0;" offset="0"/>
|
||||||
|
<stop id="stop11371" style="stop-color: rgb(0, 0, 0); stop-opacity: 0.254717;" offset="0.72131097"/>
|
||||||
|
<stop id="stop18428" style="stop-color: rgb(0, 0, 0); stop-opacity: 0.12549;" offset="0.91000003"/>
|
||||||
|
<stop id="stop11375" style="stop-color: rgb(0, 0, 0); stop-opacity: 0;" offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient7431">
|
||||||
|
<stop id="stop7433" style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="0"/>
|
||||||
|
<stop id="stop7439" style="stop-color: rgb(255, 255, 255); stop-opacity: 0.858491;" offset="0.72000003"/>
|
||||||
|
<stop id="stop8224" style="stop-color: rgb(255, 255, 255); stop-opacity: 0.707547;" offset="0.89999998"/>
|
||||||
|
<stop id="stop7435" style="stop-color: rgb(255, 255, 255); stop-opacity: 0.320755;" offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="filter3659">
|
||||||
|
<feGaussianBlur inkscape:collect="always" stdDeviation="0.25192676" id="feGaussianBlur3661"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<g id="layer1">
|
||||||
|
<g transform="matrix(1.1475, 0, 0, 1.1475, -368.661, -33.5075)" id="g4500">
|
||||||
|
<path d="M 326.964,34.4298 L 423.481,34.4298 C 437.856,66.0223 403.767,104.222 423.481,135.402 L 326.964,135.402 L 326.964,34.4298 z" id="path21978" style="fill: rgb(95, 123, 141); fill-opacity: 1; fill-rule: evenodd; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<path d="M 326.964,34.4298 L 353.143,34.4298 C 367.518,66.0223 333.429,104.222 353.143,135.402 L 326.964,135.402 L 326.964,34.4298 z" id="path21980" style="fill: rgb(29, 70, 89); fill-opacity: 1; fill-rule: evenodd; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<rect width="101.089" height="7.9108801" x="34.429798" y="326.96399" transform="matrix(0, 1, 1, 0, 0, 0)" id="rect21982" style="fill: url("#linearGradient4815") rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<g transform="matrix(3.17412, 0, 0, 3.17412, 1038.99, -354.131)" id="g21984">
|
||||||
|
<path d="M -218.445,122.416 C -213.917,132.369 -224.656,144.405 -218.445,154.228 L -216.32,154.228 C -222.531,144.405 -211.792,132.369 -216.32,122.416 L -218.445,122.416 z" id="path21986" style="fill: rgb(0, 0, 0); fill-opacity: 0.0798122; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<path d="M -217.955,122.416 C -213.426,132.369 -224.166,144.405 -217.955,154.228 L -216.32,154.228 C -222.531,144.405 -211.792,132.369 -216.32,122.416 L -217.955,122.416 z" id="path21988" style="fill: rgb(0, 0, 0); fill-opacity: 0.131455; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<path d="M -217.403,122.416 C -212.875,132.369 -223.614,144.405 -217.403,154.228 L -216.32,154.228 C -222.531,144.405 -211.792,132.369 -216.32,122.416 L -217.403,122.416 z" id="path21990" style="fill: rgb(0, 0, 0); fill-opacity: 0.197183; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<path d="M -216.852,122.416 C -212.323,132.369 -223.063,144.405 -216.852,154.228 L -216.32,154.228 C -222.531,144.405 -211.792,132.369 -216.32,122.416 L -216.852,122.416 z" id="path21992" style="fill: rgb(0, 0, 0); fill-opacity: 0.267606; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
</g>
|
||||||
|
<path d="M 326.964,135.402 L 422.488,135.402 C 412.274,118.171 416.819,101.345 421.306,83.5374 L 326.964,83.2034 L 326.964,135.402 z" id="path21994" style="fill: url("#linearGradient4817") rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<g transform="matrix(2.41517, 0, 0, 2.41517, 876.456, -197.189)" id="g21996">
|
||||||
|
<path d="M 602.125,190.59375 C 599.45874,190.67075 596.74504,191.16798 594.53125,192.78125 C 591.5146,192.34561 588.3664,192.55749 585.5,193.6875 C 583.62824,194.43267 582.15635,195.77855 580.96875,197.34375 C 580.95544,197.36301 580.94492,197.38405 580.9375,197.40625 C 580.92091,197.43509 580.91029,197.46697 580.90625,197.5 C 580.91029,197.53303 580.92091,197.56491 580.9375,197.59375 C 580.94492,197.61595 580.95544,197.63699 580.96875,197.65625 C 580.97822,197.66757 580.98868,197.67803 581,197.6875 C 581.02605,197.71524 581.05813,197.73662 581.09375,197.75 C 581.12472,197.75595 581.15653,197.75595 581.1875,197.75 C 581.20825,197.75263 581.22925,197.75263 581.25,197.75 C 584.80749,196.49944 588.39295,195.15225 592.15625,195.5 C 593.28385,195.58867 594.35616,196.00271 595.46875,196.375 C 595.50974,196.38565 595.55276,196.38565 595.59375,196.375 C 595.62678,196.37096 595.65866,196.36034 595.6875,196.34375 C 595.7097,196.33633 595.73074,196.32581 595.75,196.3125 C 598.71379,193.45164 603.00891,192.72955 606.96875,191.90625 C 606.98007,191.89678 606.99053,191.88632 607,191.875 C 607.19563,191.80037 607.32956,191.73576 607.4375,191.625 C 607.49147,191.56962 607.55414,191.50784 607.5625,191.40625 C 607.57086,191.30466 607.51945,191.21518 607.46875,191.15625 C 607.36735,191.03839 607.25573,190.98239 607.125,190.9375 C 606.99427,190.89261 606.8215,190.87546 606.65625,190.84375 C 605.99526,190.71692 605.12704,190.6454 604.8125,190.625 C 604.80209,190.62434 604.79166,190.62434 604.78125,190.625 C 603.91011,190.58739 603.02603,190.56773 602.125,190.59375 z" transform="translate(-806.724, -92.8004)" id="path21998" style="fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: evenodd; stroke: none; stroke-width: 1pt; stroke-linecap: butt; stroke-linejoin: miter; stroke-opacity: 1;"/>
|
||||||
|
<path d="M -224.344,103.835 C -219.295,101.9 -214.705,101.331 -211.263,102.86 C -208.45,100.119 -202.237,98.6242 -200.227,98.6199 C -207.528,97.8352 -210.552,99.4967 -212,100.582 C -216.698,100.015 -221.096,100.522 -224.344,103.834" id="path22000" style="fill: rgb(255, 255, 255); fill-opacity: 1; stroke: none;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(2.41517, 0, 0, 2.41517, 876.456, -197.189)" id="g22002">
|
||||||
|
<path d="M 596.25,27.53125 C 587.9033,27.701471 579.93436,30.011449 573.84375,35.03125 C 565.49276,31.223728 554.44432,30.751141 544.375,32.28125 C 538.97209,33.102263 533.8987,34.480363 529.6875,36.34375 C 525.4884,38.201779 521.99675,40.484175 520.1875,43.8125 C 519.57732,44.883163 519.7128,46.206199 520.46875,47.15625 C 521.22471,48.106278 522.5049,48.470375 523.65625,48.125 C 544.63433,42.131263 561.86554,43.038041 573.6875,52.875 C 574.80806,53.806374 576.46471,53.801852 577.5625,52.84375 C 587.80668,43.812696 604.05857,37.910216 621.5625,38.875 C 622.98852,38.943575 624.2874,37.997938 624.625,36.625 C 624.96264,35.252044 624.25302,33.783013 622.96875,33.1875 C 614.73544,29.461107 605.28688,27.346954 596.25,27.53125 z" transform="matrix(0.292292, -0.0677077, 0.0677077, 0.292292, -381.543, 134.276)" id="path22004" style="fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: evenodd; stroke: none; stroke-width: 1pt; stroke-linecap: butt; stroke-linejoin: miter; stroke-opacity: 1;"/>
|
||||||
|
<path d="M -225.696,112.15 C -219.345,108.564 -214.201,107.915 -209.842,110.097 C -206.945,105.454 -199.625,102.766 -197.16,102.691 C -204.796,101.053 -210.086,104.587 -211.05,106.575 C -215.328,105.394 -224.104,108.305 -225.696,112.149" id="path22006" style="fill: rgb(255, 255, 255); fill-opacity: 1; stroke: none;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.14159, 0, 0, 1.14159, 265.142, -259.674)" id="g22010">
|
||||||
|
<path d="M 134.221,257.626 C 146.813,285.3 116.952,318.766 134.221,346.078 L 138.68,346.078 C 121.411,318.766 151.272,285.3 138.68,257.626 L 134.221,257.626 z" id="path22012" style="fill: rgb(0, 0, 0); fill-opacity: 0.0798122; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<path d="M 135.222,257.626 C 147.814,285.3 117.953,318.766 135.222,346.078 L 138.68,346.078 C 121.411,318.766 151.272,285.3 138.68,257.626 L 135.222,257.626 z" id="path22014" style="fill: rgb(0, 0, 0); fill-opacity: 0.131455; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<path d="M 136.393,257.626 C 148.985,285.3 119.124,318.766 136.393,346.078 L 138.68,346.078 C 121.411,318.766 151.272,285.3 138.68,257.626 L 136.393,257.626 z" id="path22016" style="fill: rgb(0, 0, 0); fill-opacity: 0.197183; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<path d="M 137.564,257.626 C 150.156,285.3 120.295,318.766 137.564,346.078 L 138.68,346.078 C 121.411,318.766 151.272,285.3 138.68,257.626 L 137.564,257.626 z" id="path22018" style="fill: rgb(0, 0, 0); fill-opacity: 0.267606; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
<path d="M 133.134,257.626 C 145.726,285.3 115.865,318.766 133.134,346.078 L 138.68,346.078 C 121.411,318.766 151.272,285.3 138.68,257.626 L 133.134,257.626 z" id="path22020" style="fill: rgb(0, 0, 0); fill-opacity: 0.0798122; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.14159, 0, 0, 1.14159, -389.722, -484.947)" id="g22103">
|
||||||
|
<path d="M 653.161,498.44 L 693.751,498.44" id="path21604" style="fill: rgb(96, 123, 142); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(255, 254, 255); stroke-width: 8; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dashoffset: 0pt; stroke-opacity: 0.698039;"/>
|
||||||
|
<path d="M 653.161,515.294 L 683.452,515.294" id="path21606" style="fill: rgb(96, 123, 142); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(255, 254, 255); stroke-width: 8; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dashoffset: 0pt; stroke-opacity: 0.698039;"/>
|
||||||
|
<path d="M 653.161,532.46 L 693.751,532.46" id="path22101" style="fill: rgb(96, 123, 142); fill-opacity: 1; fill-rule: nonzero; stroke: rgb(255, 254, 255); stroke-width: 8; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dashoffset: 0pt; stroke-opacity: 0.698039;"/>
|
||||||
|
</g>
|
||||||
|
<path d="M 326.964,34.4298 L 423.285,34.4298 C 433.146,54.4709 420.531,82.4058 417.826,102.327 L 326.964,102.327 L 326.964,34.4298 z" id="path22008" style="fill: url("#linearGradient4819") rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 19 KiB |
BIN
resources/images/news/balkaninsight.png
Normal file
After Width: | Height: | Size: 573 B |
BIN
resources/images/news/discover_magazine.png
Normal file
After Width: | Height: | Size: 1014 B |
BIN
resources/images/news/dr_dk.png
Normal file
After Width: | Height: | Size: 391 B |
BIN
resources/images/news/elpais_impreso.png
Normal file
After Width: | Height: | Size: 717 B |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 21 KiB |
60
resources/recipes/DrawAndCook.recipe
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class DrawAndCook(BasicNewsRecipe):
|
||||||
|
title = 'DrawAndCook'
|
||||||
|
__author__ = 'Starson17'
|
||||||
|
description = 'Drawings of recipes!'
|
||||||
|
language = 'en'
|
||||||
|
publisher = 'Starson17'
|
||||||
|
category = 'news, food, recipes'
|
||||||
|
use_embedded_content= False
|
||||||
|
no_stylesheets = True
|
||||||
|
oldest_article = 24
|
||||||
|
remove_javascript = True
|
||||||
|
remove_empty_feeds = True
|
||||||
|
cover_url = 'http://farm5.static.flickr.com/4043/4471139063_4dafced67f_o.jpg'
|
||||||
|
max_articles_per_feed = 30
|
||||||
|
|
||||||
|
remove_attributes = ['style', 'font']
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
feeds = []
|
||||||
|
for title, url in [
|
||||||
|
("They Draw and Cook", "http://www.theydrawandcook.com/")
|
||||||
|
]:
|
||||||
|
articles = self.make_links(url)
|
||||||
|
if articles:
|
||||||
|
feeds.append((title, articles))
|
||||||
|
print 'feeds are: ', feeds
|
||||||
|
return feeds
|
||||||
|
|
||||||
|
def make_links(self, url):
|
||||||
|
soup = self.index_to_soup(url)
|
||||||
|
title = ''
|
||||||
|
date = ''
|
||||||
|
current_articles = []
|
||||||
|
soup = self.index_to_soup(url)
|
||||||
|
recipes = soup.findAll('div', attrs={'class': 'date-outer'})
|
||||||
|
for recipe in recipes:
|
||||||
|
title = recipe.h3.a.string
|
||||||
|
page_url = recipe.h3.a['href']
|
||||||
|
current_articles.append({'title': title, 'url': page_url, 'description':'', 'date':date})
|
||||||
|
return current_articles
|
||||||
|
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='h3', attrs={'class':'post-title entry-title'})
|
||||||
|
,dict(name='div', attrs={'class':'post-body entry-content'})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [dict(name='div', attrs={'class':['separator']})
|
||||||
|
,dict(name='div', attrs={'class':['post-share-buttons']})
|
||||||
|
]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
|
img {max-width:100%; min-width:100%;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
38
resources/recipes/alternet.recipe
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Alternet(BasicNewsRecipe):
|
||||||
|
title = u'Alternet'
|
||||||
|
__author__= 'rty'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
publisher = 'alternet.org'
|
||||||
|
category = 'News, Magazine'
|
||||||
|
description = 'News magazine and online community'
|
||||||
|
feeds = [
|
||||||
|
(u'Front Page', u'http://feeds.feedblitz.com/alternet'),
|
||||||
|
(u'Breaking News', u'http://feeds.feedblitz.com/alternet_breaking_news'),
|
||||||
|
(u'Top Ten Campaigns', u'http://feeds.feedblitz.com/alternet_top_10_campaigns'),
|
||||||
|
(u'Special Coverage Areas', u'http://feeds.feedblitz.com/alternet_coverage')
|
||||||
|
]
|
||||||
|
remove_attributes = ['width', 'align','cellspacing']
|
||||||
|
remove_javascript = True
|
||||||
|
use_embedded_content = False
|
||||||
|
no_stylesheets = True
|
||||||
|
language = 'en'
|
||||||
|
encoding = 'UTF-8'
|
||||||
|
temp_files = []
|
||||||
|
articles_are_obfuscated = True
|
||||||
|
|
||||||
|
def get_article_url(self, article):
|
||||||
|
return article.get('link', None)
|
||||||
|
|
||||||
|
def get_obfuscated_article(self, url):
|
||||||
|
br = self.get_browser()
|
||||||
|
br.open(url)
|
||||||
|
response = br.follow_link(url_regex = r'/printversion/[0-9]+', nr = 0)
|
||||||
|
html = response.read()
|
||||||
|
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
|
||||||
|
self.temp_files[-1].write(html)
|
||||||
|
self.temp_files[-1].close()
|
||||||
|
return self.temp_files[-1].name
|
@ -6,31 +6,38 @@ class AssociatedPress(BasicNewsRecipe):
|
|||||||
|
|
||||||
title = u'Associated Press'
|
title = u'Associated Press'
|
||||||
description = 'Global news'
|
description = 'Global news'
|
||||||
__author__ = 'Kovid Goyal'
|
__author__ = 'Kovid Goyal and Sujata Raman'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
no_stylesheets = True
|
||||||
max_articles_per_feed = 15
|
max_articles_per_feed = 15
|
||||||
html2lrf_options = ['--force-page-break-before-tag="chapter"']
|
html2lrf_options = ['--force-page-break-before-tag="chapter"']
|
||||||
|
|
||||||
|
|
||||||
preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
|
||||||
[
|
|
||||||
(r'<HEAD>.*?</HEAD>' , lambda match : '<HEAD></HEAD>'),
|
|
||||||
(r'<body class="apple-rss-no-unread-mode" onLoad="setup(null)">.*?<!-- start Entries -->', lambda match : '<body>'),
|
|
||||||
(r'<!-- end apple-rss-content-area -->.*?</body>', lambda match : '</body>'),
|
|
||||||
(r'<script.*?>.*?</script>', lambda match : ''),
|
|
||||||
(r'<body.*?>.*?<span class="headline">', lambda match : '<body><span class="headline"><chapter>'),
|
|
||||||
(r'<tr><td><div class="body">.*?<p class="ap-story-p">', lambda match : '<p class="ap-story-p">'),
|
|
||||||
(r'<p class="ap-story-p">', lambda match : '<p>'),
|
|
||||||
(r'Learn more about our <a href="http://apdigitalnews.com/privacy.html">Privacy Policy</a>.*?</body>', lambda match : '</body>'),
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
feeds = [ ('AP Headlines', 'http://hosted.ap.org/lineups/TOPHEADS-rss_2.0.xml?SITE=ORAST&SECTION=HOME'),
|
preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
||||||
('AP US News', 'http://hosted.ap.org/lineups/USHEADS-rss_2.0.xml?SITE=CAVIC&SECTION=HOME'),
|
[
|
||||||
|
(r'<span class="entry-content">', lambda match : '<div class="entry-content">'),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
keep_only_tags = [ dict(name='div', attrs={'class':['body']}),
|
||||||
|
dict(name='div', attrs={'class':['entry-content']}),
|
||||||
|
]
|
||||||
|
remove_tags = [dict(name='table', attrs={'class':['ap-video-table','ap-htmlfragment-table','ap-htmltable-table']}),
|
||||||
|
dict(name='span', attrs={'class':['apCaption','tabletitle']}),
|
||||||
|
dict(name='td', attrs={'bgcolor':['#333333']}),
|
||||||
|
]
|
||||||
|
extra_css = '''
|
||||||
|
.headline{font-family:Verdana,Arial,Helvetica,sans-serif;font-weight:bold;}
|
||||||
|
.bline{color:#003366;}
|
||||||
|
body{font-family:Arial,Helvetica,sans-serif;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('AP Headlines', 'http://hosted.ap.org/lineups/TOPHEADS-rss_2.0.xml?SITE=ORAST&SECTION=HOME'),
|
||||||
|
('AP US News', 'http://hosted.ap.org/lineups/USHEADS-rss_2.0.xml?SITE=CAVIC&SECTION=HOME'),
|
||||||
('AP World News', 'http://hosted.ap.org/lineups/WORLDHEADS-rss_2.0.xml?SITE=SCAND&SECTION=HOME'),
|
('AP World News', 'http://hosted.ap.org/lineups/WORLDHEADS-rss_2.0.xml?SITE=SCAND&SECTION=HOME'),
|
||||||
('AP Political News', 'http://hosted.ap.org/lineups/POLITICSHEADS-rss_2.0.xml?SITE=ORMED&SECTION=HOME'),
|
('AP Political News', 'http://hosted.ap.org/lineups/POLITICSHEADS-rss_2.0.xml?SITE=ORMED&SECTION=HOME'),
|
||||||
('AP Washington State News', 'http://hosted.ap.org/lineups/WASHINGTONHEADS-rss_2.0.xml?SITE=NYPLA&SECTION=HOME'),
|
('AP Washington State News', 'http://hosted.ap.org/lineups/WASHINGTONHEADS-rss_2.0.xml?SITE=NYPLA&SECTION=HOME'),
|
||||||
@ -38,4 +45,5 @@ class AssociatedPress(BasicNewsRecipe):
|
|||||||
('AP Health News', 'http://hosted.ap.org/lineups/HEALTHHEADS-rss_2.0.xml?SITE=FLDAY&SECTION=HOME'),
|
('AP Health News', 'http://hosted.ap.org/lineups/HEALTHHEADS-rss_2.0.xml?SITE=FLDAY&SECTION=HOME'),
|
||||||
('AP Science News', 'http://hosted.ap.org/lineups/SCIENCEHEADS-rss_2.0.xml?SITE=OHCIN&SECTION=HOME'),
|
('AP Science News', 'http://hosted.ap.org/lineups/SCIENCEHEADS-rss_2.0.xml?SITE=OHCIN&SECTION=HOME'),
|
||||||
('AP Strange News', 'http://hosted.ap.org/lineups/STRANGEHEADS-rss_2.0.xml?SITE=WCNC&SECTION=HOME'),
|
('AP Strange News', 'http://hosted.ap.org/lineups/STRANGEHEADS-rss_2.0.xml?SITE=WCNC&SECTION=HOME'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
62
resources/recipes/balkaninsight.recipe
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
balkaninsight.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
import re
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class BalkanInsight(BasicNewsRecipe):
|
||||||
|
title = 'Balkan Insight'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'Get exclusive news and in depth information on business, politics, events and lifestyle in the Balkans. Free and exclusive premium content.'
|
||||||
|
publisher = 'BalkanInsight.com'
|
||||||
|
category = 'news, politics, Balcans'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = False
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'utf-8'
|
||||||
|
masthead_url = 'http://www.balkaninsight.com/templates/balkaninsight/images/aindex_02.jpg'
|
||||||
|
language = 'en'
|
||||||
|
publication_type = 'newsportal'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
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)}
|
||||||
|
.article_description,body{font-family: Arial,Verdana,Helvetica,sans1,sans-serif}
|
||||||
|
img{margin-bottom: 0.8em}
|
||||||
|
h1,h2,h3,h4{font-family: Times,Georgia,serif1,serif; color: #24569E}
|
||||||
|
.article-deck {color:#777777; font-size: small;}
|
||||||
|
.main_news_img{font-size: 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(name='div', attrs={'id':'article'})]
|
||||||
|
remove_tags = [
|
||||||
|
dict(name=['object','link','iframe'])
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Albania' , u'http://www.balkaninsight.com/?tpl=653&tpid=144' )
|
||||||
|
,(u'Bosnia' , u'http://www.balkaninsight.com/?tpl=653&tpid=145' )
|
||||||
|
,(u'Bulgaria' , u'http://www.balkaninsight.com/?tpl=653&tpid=146' )
|
||||||
|
,(u'Croatia' , u'http://www.balkaninsight.com/?tpl=653&tpid=147' )
|
||||||
|
,(u'Kosovo' , u'http://www.balkaninsight.com/?tpl=653&tpid=148' )
|
||||||
|
,(u'Macedonia' , u'http://www.balkaninsight.com/?tpl=653&tpid=149' )
|
||||||
|
,(u'Montenegro' , u'http://www.balkaninsight.com/?tpl=653&tpid=150' )
|
||||||
|
,(u'Romania' , u'http://www.balkaninsight.com/?tpl=653&tpid=151' )
|
||||||
|
,(u'Serbia' , u'http://www.balkaninsight.com/?tpl=653&tpid=152' )
|
||||||
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return self.adeify_images(soup)
|
@ -3,14 +3,13 @@ __copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
|||||||
'''
|
'''
|
||||||
news.bbc.co.uk
|
news.bbc.co.uk
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
class BBC(BasicNewsRecipe):
|
class BBC(BasicNewsRecipe):
|
||||||
title = 'The BBC'
|
title = 'BBC News'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic, Starson17'
|
||||||
description = 'Global news and current affairs from the British Broadcasting Corporation'
|
description = 'News from UK. '
|
||||||
oldest_article = 2
|
oldest_article = 2
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
@ -23,7 +22,6 @@ class BBC(BasicNewsRecipe):
|
|||||||
publication_type = 'newsportal'
|
publication_type = 'newsportal'
|
||||||
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .introduction{font-weight: bold} .story-feature{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .introduction{font-weight: bold} .story-feature{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||||
preprocess_regexps = [(re.compile(r'<!--.*?-->', re.DOTALL), lambda m: '')]
|
preprocess_regexps = [(re.compile(r'<!--.*?-->', re.DOTALL), lambda m: '')]
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comments' : description
|
'comments' : description
|
||||||
,'tags' : category
|
,'tags' : category
|
||||||
@ -33,14 +31,15 @@ class BBC(BasicNewsRecipe):
|
|||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
dict(attrs={'id' :['meta-information','story-body']})
|
dict(name='div', attrs={'class':['layout-block-a layout-block']})
|
||||||
,dict(attrs={'class':['mxb' ,'storybody' ]})
|
,dict(attrs={'class':['story-body','storybody']})
|
||||||
]
|
]
|
||||||
remove_tags = [
|
|
||||||
dict(name=['object','link','table'])
|
remove_tags = [
|
||||||
,dict(attrs={'class':['caption','caption full-width','story-actions','hidden','sharesb','audioInStoryC']})
|
dict(name='div', attrs={'class':['story-feature related narrow', 'share-help', 'embedded-hyper', \
|
||||||
|
'story-feature wide ', 'story-feature narrow']})
|
||||||
]
|
]
|
||||||
remove_tags_after = dict(attrs={'class':'sharesb'})
|
|
||||||
remove_attributes = ['width','height']
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
|
@ -8,7 +8,7 @@ from calibre.web.feeds.recipes import BasicNewsRecipe
|
|||||||
|
|
||||||
class BBC(BasicNewsRecipe):
|
class BBC(BasicNewsRecipe):
|
||||||
title = 'BBC News (fast)'
|
title = 'BBC News (fast)'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic, Starson17'
|
||||||
description = 'News from UK. A much faster version that does not download pictures'
|
description = 'News from UK. A much faster version that does not download pictures'
|
||||||
oldest_article = 2
|
oldest_article = 2
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
@ -31,14 +31,16 @@ class BBC(BasicNewsRecipe):
|
|||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
dict(attrs={'id' :['meta-information','story-body']})
|
dict(name='div', attrs={'class':['layout-block-a layout-block']})
|
||||||
,dict(attrs={'class':['mxb' ,'storybody' ]})
|
,dict(attrs={'class':['story-body','storybody']})
|
||||||
]
|
]
|
||||||
remove_tags = [
|
|
||||||
dict(name=['object','link','table','img'])
|
remove_tags = [
|
||||||
,dict(attrs={'class':['caption','caption full-width','story-actions','hidden','sharesb','audioInStoryC']})
|
dict(name='div', attrs={'class':['story-feature related narrow', 'share-help', 'embedded-hyper', \
|
||||||
|
'story-feature wide ', 'story-feature narrow']})
|
||||||
|
, dict(name=['img'])
|
||||||
]
|
]
|
||||||
remove_tags_after = dict(attrs={'class':'sharesb'})
|
|
||||||
remove_attributes = ['width','height']
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
|
112
resources/recipes/daum_net.recipe
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import re
|
||||||
|
from datetime import date, timedelta
|
||||||
|
|
||||||
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
|
class MediaDaumRecipe(BasicNewsRecipe):
|
||||||
|
title = u'\uBBF8\uB514\uC5B4 \uB2E4\uC74C \uC624\uB298\uC758 \uC8FC\uC694 \uB274\uC2A4'
|
||||||
|
description = 'Articles from media.daum.net'
|
||||||
|
__author__ = 'trustin'
|
||||||
|
language = 'ko'
|
||||||
|
max_articles = 100
|
||||||
|
|
||||||
|
timefmt = ''
|
||||||
|
masthead_url = 'http://img-media.daum-img.net/2010ci/service_news.gif'
|
||||||
|
cover_margins = (18,18,'grey99')
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_tags_before = dict(id='GS_con')
|
||||||
|
remove_tags_after = dict(id='GS_con')
|
||||||
|
remove_tags = [dict(attrs={'class':[
|
||||||
|
'bline',
|
||||||
|
'GS_vod',
|
||||||
|
]}),
|
||||||
|
dict(id=[
|
||||||
|
'GS_swf_poll',
|
||||||
|
'ad250',
|
||||||
|
]),
|
||||||
|
dict(name=['script', 'noscript', 'style', 'object'])]
|
||||||
|
preprocess_regexps = [
|
||||||
|
(re.compile(r'<\s+', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '< '),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*){3,}', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: ''),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*</div>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</div>'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*</p>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</p>'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*</td>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</td>'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*</strong>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</strong>'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*</b>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</b>'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*</em>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</em>'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*</i>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</i>'),
|
||||||
|
(re.compile(u'\(\uB05D\)[ \t\r\n]*<br[^>]*>.*</div>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</div>'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*<div', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '<div'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*<p', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '<p'),
|
||||||
|
(re.compile(r'(<br[^>]*>[ \t\r\n]*)*<table', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '<table'),
|
||||||
|
(re.compile(r'<strong>(<br[^>]*>[ \t\r\n]*)*', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '<strong>'),
|
||||||
|
(re.compile(r'<b>(<br[^>]*>[ \t\r\n]*)*', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '<b>'),
|
||||||
|
(re.compile(r'<em>(<br[^>]*>[ \t\r\n]*)*', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '<em>'),
|
||||||
|
(re.compile(r'<i>(<br[^>]*>[ \t\r\n]*)*', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '<i>'),
|
||||||
|
(re.compile(u'(<br[^>]*>[ \t\r\n]*)*(\u25B6|\u25CF|\u261E|\u24D2|\(c\))*\[[^\]]*(\u24D2|\(c\)|\uAE30\uC0AC|\uC778\uAE30[^\]]*\uB274\uC2A4)[^\]]*\].*</div>', re.DOTALL|re.IGNORECASE),
|
||||||
|
lambda match: '</div>'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
today = date.today();
|
||||||
|
articles = []
|
||||||
|
articles = self.parse_list_page(articles, today)
|
||||||
|
articles = self.parse_list_page(articles, today - timedelta(1))
|
||||||
|
return [('\uBBF8\uB514\uC5B4 \uB2E4\uC74C \uC624\uB298\uC758 \uC8FC\uC694 \uB274\uC2A4', articles)]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_list_page(self, articles, date):
|
||||||
|
if len(articles) >= self.max_articles:
|
||||||
|
return articles
|
||||||
|
|
||||||
|
for page in range(1, 10):
|
||||||
|
soup = self.index_to_soup('http://media.daum.net/primary/total/list.html?cateid=100044&date=%(date)s&page=%(page)d' % {'date': date.strftime('%Y%m%d'), 'page': page})
|
||||||
|
done = True
|
||||||
|
for item in soup.findAll('dl'):
|
||||||
|
dt = item.find('dt', { 'class': 'tit' })
|
||||||
|
dd = item.find('dd', { 'class': 'txt' })
|
||||||
|
if dt is None:
|
||||||
|
break
|
||||||
|
a = dt.find('a', href=True)
|
||||||
|
url = 'http://media.daum.net/primary/total/' + a['href']
|
||||||
|
title = self.tag_to_string(dt)
|
||||||
|
if dd is None:
|
||||||
|
description = ''
|
||||||
|
else:
|
||||||
|
description = self.tag_to_string(dd)
|
||||||
|
articles.append(dict(title=title, description=description, url=url, content=''))
|
||||||
|
done = len(articles) >= self.max_articles
|
||||||
|
if done:
|
||||||
|
break
|
||||||
|
if done:
|
||||||
|
break
|
||||||
|
return articles
|
||||||
|
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
return self.strip_anchors(soup)
|
||||||
|
|
||||||
|
def strip_anchors(self, soup):
|
||||||
|
for para in soup.findAll(True):
|
||||||
|
aTags = para.findAll('a')
|
||||||
|
for a in aTags:
|
||||||
|
if a.img is None:
|
||||||
|
a.replaceWith(a.renderContents().decode('utf-8','replace'))
|
||||||
|
return soup
|
42
resources/recipes/dr_dk.recipe
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
dr.dk
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class dr_dk(BasicNewsRecipe):
|
||||||
|
title = 'DR Nyheder'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'Myndighederne indfører nu eskorte af brandbiler og ambulancer i Ishøj af frygt for hærværk.'
|
||||||
|
publisher = 'Nyhedsbureauet DR Nyheder'
|
||||||
|
category = 'news, politics, Denmark'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
delay = 1
|
||||||
|
encoding = 'utf8'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'da'
|
||||||
|
extra_css = """ body{font-family: Verdana,Arial,sans-serif }
|
||||||
|
img{margin-bottom: 0.4em}
|
||||||
|
.txtContent,.stamp{font-size: small}
|
||||||
|
"""
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'class':'articleContent'})]
|
||||||
|
remove_attributes=['xmlns:msxsl','width','height']
|
||||||
|
|
||||||
|
feeds = [(u'All news', u'http://www.dr.dk/Nyheder/Service/feeds/Allenyheder.htm')]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return soup
|
86
resources/recipes/elpais_impreso.recipe
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
www.elpais.com/diario/
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre import strftime
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class ElPaisImpresa(BasicNewsRecipe):
|
||||||
|
title = 'El País - edicion impresa'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'el periodico global en Español'
|
||||||
|
publisher = 'EDICIONES EL PAIS, S.L.'
|
||||||
|
category = 'news, politics,Spain,actualidad,noticias,informacion,videos,fotografias,audios,graficos,nacional,internacional,deportes,economia,tecnologia,cultura,gente,television,sociedad,opinion,blogs,foros,chats,encuestas,entrevistas,participacion'
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'latin1'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'es'
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://www.elpais.com/im/tit_logo_global.gif'
|
||||||
|
index = 'http://www.elpais.com/diario/'
|
||||||
|
extra_css = ' p{text-align: justify} body{ text-align: left; font-family: Georgia,"Times New Roman",Times,serif } h2{font-family: Arial,Helvetica,sans-serif} img{margin-bottom: 0.4em} '
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Internacional' , index + u'internacional/' )
|
||||||
|
,(u'España' , index + u'espana/' )
|
||||||
|
,(u'Economia' , index + u'economia/' )
|
||||||
|
,(u'Opinion' , index + u'opinion/' )
|
||||||
|
,(u'Viñetas' , index + u'vineta/' )
|
||||||
|
,(u'Sociedad' , index + u'sociedad/' )
|
||||||
|
,(u'Cultura' , index + u'cultura/' )
|
||||||
|
,(u'Tendencias' , index + u'tendencias/' )
|
||||||
|
,(u'Gente' , index + u'gente/' )
|
||||||
|
,(u'Obituarios' , index + u'obituarios/' )
|
||||||
|
,(u'Deportes' , index + u'deportes/' )
|
||||||
|
,(u'Pantallas' , index + u'radioytv/' )
|
||||||
|
,(u'Ultima' , index + u'ultima/' )
|
||||||
|
,(u'Educacion' , index + u'educacion/' )
|
||||||
|
,(u'Saludo' , index + u'salud/' )
|
||||||
|
,(u'Ciberpais' , index + u'ciberpais/' )
|
||||||
|
,(u'EP3' , index + u'ep3/' )
|
||||||
|
,(u'Cine' , index + u'cine/' )
|
||||||
|
,(u'Babelia' , index + u'babelia/' )
|
||||||
|
,(u'El viajero' , index + u'viajero/' )
|
||||||
|
,(u'Negocios' , index + u'negocios/' )
|
||||||
|
,(u'Domingo' , index + u'domingo/' )
|
||||||
|
,(u'El Pais semanal' , index + u'eps/' )
|
||||||
|
,(u'Quadern Catalunya' , index + u'quadern-catalunya/' )
|
||||||
|
]
|
||||||
|
|
||||||
|
keep_only_tags=[dict(attrs={'class':['cabecera_noticia','contenido_noticia']})]
|
||||||
|
remove_attributes=['width','height']
|
||||||
|
remove_tags=[dict(name='link')]
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
totalfeeds = []
|
||||||
|
lfeeds = self.get_feeds()
|
||||||
|
for feedobj in lfeeds:
|
||||||
|
feedtitle, feedurl = feedobj
|
||||||
|
self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl))
|
||||||
|
articles = []
|
||||||
|
soup = self.index_to_soup(feedurl)
|
||||||
|
for item in soup.findAll('a',attrs={'class':['g19r003','g19i003','g17r003','g17i003']}):
|
||||||
|
url = 'http://www.elpais.com' + item['href'].rpartition('/')[0]
|
||||||
|
title = self.tag_to_string(item)
|
||||||
|
date = strftime(self.timefmt)
|
||||||
|
articles.append({
|
||||||
|
'title' :title
|
||||||
|
,'date' :date
|
||||||
|
,'url' :url
|
||||||
|
,'description':''
|
||||||
|
})
|
||||||
|
totalfeeds.append((feedtitle, articles))
|
||||||
|
return totalfeeds
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?print=1'
|
74
resources/recipes/folhadesaopaulo.recipe
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Saverio Palmieri Neto <saverio.palmieri at gmail.com>'
|
||||||
|
'''
|
||||||
|
folha.uol.com.br
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class FolhaOnline(BasicNewsRecipe):
|
||||||
|
title = 'Folha de Sao Paulo'
|
||||||
|
__author__ = 'Saverio Palmieri Neto'
|
||||||
|
description = 'Brazilian news from Folha de Sao Paulo Online'
|
||||||
|
publisher = 'Folha de Sao Paulo'
|
||||||
|
category = 'Brasil, news'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 1000
|
||||||
|
summary_length = 2048
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
timefmt = ' [%d %b %Y (%a)]'
|
||||||
|
encoding = 'cp1252'
|
||||||
|
cover_url = 'http://lh5.ggpht.com/_hEb7sFmuBvk/TFoiKLRS5dI/AAAAAAAAADM/kcVKggZwKnw/capa_folha.jpg'
|
||||||
|
cover_margins = (5,5,'white')
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'articleNew'})]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='script')
|
||||||
|
,dict(name='div',
|
||||||
|
attrs={'id':[
|
||||||
|
'articleButton'
|
||||||
|
,'bookmarklets'
|
||||||
|
,'ad-180x150-1'
|
||||||
|
,'contextualAdsArticle'
|
||||||
|
,'articleEnd'
|
||||||
|
,'articleComments'
|
||||||
|
]})
|
||||||
|
,dict(name='div',
|
||||||
|
attrs={'class':[
|
||||||
|
'openBox adslibraryArticle'
|
||||||
|
]})
|
||||||
|
,dict(name='a')
|
||||||
|
,dict(name='iframe')
|
||||||
|
,dict(name='link')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Em cima da hora', u'http://feeds.folha.uol.com.br/emcimadahora/rss091.xml')
|
||||||
|
,(u'Ambiente', u'http://feeds.folha.uol.com.br/ambiente/rss091.xml')
|
||||||
|
,(u'Bichos', u'http://feeds.folha.uol.com.br/bichos/rss091.xml')
|
||||||
|
,(u'Poder', u'http://feeds.folha.uol.com.br/poder/rss091.xml')
|
||||||
|
,(u'Ciencia', u'http://feeds.folha.uol.com.br/ciencia/rss091.xml')
|
||||||
|
,(u'Cotidiano', u'http://feeds.folha.uol.com.br/cotidiado/rss091.xml')
|
||||||
|
,(u'Saber', u'http://feeds.folha.uol.com.br/saber/rss091.xml')
|
||||||
|
,(u'Equilíbrio e Saúde', u'http://feeds.folha.uol.com.br/equilibrioesaude/rss091.xml')
|
||||||
|
,(u'Esporte', u'http://feeds.folha.uol.com.br/esporte/rss091.xml')
|
||||||
|
,(u'Ilustrada', u'http://feeds.folha.uol.com.br/ilustrada/rss091.xml')
|
||||||
|
,(u'Ilustríssima', u'http://feeds.folha.uol.com.br/ilustrissima/rss091.xml')
|
||||||
|
,(u'Mercado', u'http://feeds.folha.uol.com.br/mercado/rss091.xml')
|
||||||
|
,(u'Mundo', u'http://feeds.folha.uol.com.br/mundo/rss091.xml')
|
||||||
|
,(u'Tec', u'http://feeds.folha.uol.com.br/tec/rss091.xml')
|
||||||
|
,(u'Turismo', u'http://feeds.folha.uol.com.br/turismo/rss091.xml')
|
||||||
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return soup
|
||||||
|
|
||||||
|
language = 'pt'
|
@ -4,28 +4,23 @@ import re
|
|||||||
class NatureNews(BasicNewsRecipe):
|
class NatureNews(BasicNewsRecipe):
|
||||||
title = u'Nature News'
|
title = u'Nature News'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
__author__ = 'Krittika Goyal'
|
__author__ = 'Krittika Goyal, Starson17'
|
||||||
oldest_article = 31 #days
|
oldest_article = 31 #days
|
||||||
|
remove_empty_feeds = True
|
||||||
max_articles_per_feed = 50
|
max_articles_per_feed = 50
|
||||||
#encoding = 'latin1'
|
|
||||||
|
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
remove_tags_before = dict(name='h1', attrs={'class':'heading entry-title'})
|
remove_tags_before = dict(name='h1', attrs={'class':'heading entry-title'})
|
||||||
remove_tags_after = dict(name='h2', attrs={'id':'comments'})
|
remove_tags_after = dict(name='h2', attrs={'id':'comments'})
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
#dict(name='iframe'),
|
|
||||||
#dict(name='div', attrs={'class':['pt-box-title', 'pt-box-content']}),
|
|
||||||
#dict(name='div', attrs={'id':['block-td_search_160', 'block-cam_search_160']}),
|
|
||||||
dict(name='h2', attrs={'id':'comments'}),
|
dict(name='h2', attrs={'id':'comments'}),
|
||||||
dict(name='ul', attrs={'class':'toolsmenu xoxo'}),
|
dict(attrs={'alt':'Advertisement'}),
|
||||||
]
|
dict(name='div', attrs={'class':'ad'}),
|
||||||
|
]
|
||||||
|
|
||||||
preprocess_regexps = [
|
preprocess_regexps = [
|
||||||
(re.compile(r'<script.*?</script>', re.DOTALL), lambda m: '')
|
(re.compile(r'<p>ADVERTISEMENT</p>', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||||
]
|
]
|
||||||
|
|
||||||
feeds = [('Nature News', 'http://feeds.nature.com/news/rss/most_recent')]
|
feeds = [('Nature News', 'http://feeds.nature.com/news/rss/most_recent')]
|
||||||
|
|
||||||
def get_article_url(self, article):
|
|
||||||
return article.get('id')
|
|
||||||
|
|
||||||
|
@ -4,29 +4,27 @@ from calibre import __appname__
|
|||||||
|
|
||||||
class GoogleReader(BasicNewsRecipe):
|
class GoogleReader(BasicNewsRecipe):
|
||||||
title = 'Google Reader'
|
title = 'Google Reader'
|
||||||
description = 'This recipe downloads feeds you have tagged from your Google Reader account.'
|
description = 'This recipe fetches from your Google Reader account unread Starred items and unread Feeds you have placed in a folder via the manage subscriptions feature.'
|
||||||
needs_subscription = True
|
needs_subscription = True
|
||||||
__author__ = 'davec'
|
__author__ = 'davec, rollercoaster, Starson17'
|
||||||
base_url = 'http://www.google.com/reader/atom/'
|
base_url = 'http://www.google.com/reader/atom/'
|
||||||
max_articles_per_feed = 50
|
oldest_article = 365
|
||||||
|
max_articles_per_feed = 250
|
||||||
get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed
|
get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed
|
||||||
use_embedded_content = True
|
use_embedded_content = True
|
||||||
|
|
||||||
def get_browser(self):
|
def get_browser(self):
|
||||||
br = BasicNewsRecipe.get_browser()
|
br = BasicNewsRecipe.get_browser(self)
|
||||||
|
|
||||||
if self.username is not None and self.password is not None:
|
if self.username is not None and self.password is not None:
|
||||||
request = urllib.urlencode([('Email', self.username), ('Passwd', self.password),
|
request = urllib.urlencode([('Email', self.username), ('Passwd', self.password),
|
||||||
('service', 'reader'), ('source', __appname__)])
|
('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)])
|
||||||
response = br.open('https://www.google.com/accounts/ClientLogin', request)
|
response = br.open('https://www.google.com/accounts/ClientLogin', request)
|
||||||
sid = re.search('SID=(\S*)', response.read()).group(1)
|
auth = re.search('Auth=(\S*)', response.read()).group(1)
|
||||||
|
|
||||||
cookies = mechanize.CookieJar()
|
cookies = mechanize.CookieJar()
|
||||||
br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies))
|
br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies))
|
||||||
cookies.set_cookie(mechanize.Cookie(None, 'SID', sid, None, False, '.google.com', True, True, '/', True, False, None, True, '', '', None))
|
br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)]
|
||||||
return br
|
return br
|
||||||
|
|
||||||
|
|
||||||
def get_feeds(self):
|
def get_feeds(self):
|
||||||
feeds = []
|
feeds = []
|
||||||
soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list')
|
soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list')
|
||||||
|
@ -3,10 +3,10 @@ from calibre.web.feeds.recipes import BasicNewsRecipe
|
|||||||
from calibre import __appname__
|
from calibre import __appname__
|
||||||
|
|
||||||
class GoogleReaderUber(BasicNewsRecipe):
|
class GoogleReaderUber(BasicNewsRecipe):
|
||||||
title = 'Google Reader Uber'
|
title = 'Google Reader uber'
|
||||||
description = 'This recipe downloads all unread feedsfrom your Google Reader account.'
|
description = 'Fetches all feeds from your Google Reader account including the uncategorized items.'
|
||||||
needs_subscription = True
|
needs_subscription = True
|
||||||
__author__ = 'rollercoaster, davec'
|
__author__ = 'davec, rollercoaster, Starson17'
|
||||||
base_url = 'http://www.google.com/reader/atom/'
|
base_url = 'http://www.google.com/reader/atom/'
|
||||||
oldest_article = 365
|
oldest_article = 365
|
||||||
max_articles_per_feed = 250
|
max_articles_per_feed = 250
|
||||||
@ -14,20 +14,17 @@ class GoogleReaderUber(BasicNewsRecipe):
|
|||||||
use_embedded_content = True
|
use_embedded_content = True
|
||||||
|
|
||||||
def get_browser(self):
|
def get_browser(self):
|
||||||
br = BasicNewsRecipe.get_browser()
|
br = BasicNewsRecipe.get_browser(self)
|
||||||
|
|
||||||
if self.username is not None and self.password is not None:
|
if self.username is not None and self.password is not None:
|
||||||
request = urllib.urlencode([('Email', self.username), ('Passwd', self.password),
|
request = urllib.urlencode([('Email', self.username), ('Passwd', self.password),
|
||||||
('service', 'reader'), ('source', __appname__)])
|
('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)])
|
||||||
response = br.open('https://www.google.com/accounts/ClientLogin', request)
|
response = br.open('https://www.google.com/accounts/ClientLogin', request)
|
||||||
sid = re.search('SID=(\S*)', response.read()).group(1)
|
auth = re.search('Auth=(\S*)', response.read()).group(1)
|
||||||
|
|
||||||
cookies = mechanize.CookieJar()
|
cookies = mechanize.CookieJar()
|
||||||
br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies))
|
br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies))
|
||||||
cookies.set_cookie(mechanize.Cookie(None, 'SID', sid, None, False, '.google.com', True, True, '/', True, False, None, True, '', '', None))
|
br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)]
|
||||||
return br
|
return br
|
||||||
|
|
||||||
|
|
||||||
def get_feeds(self):
|
def get_feeds(self):
|
||||||
feeds = []
|
feeds = []
|
||||||
soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list')
|
soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list')
|
||||||
|
@ -19,6 +19,32 @@ class heiseDe(BasicNewsRecipe):
|
|||||||
max_articles_per_feed = 40
|
max_articles_per_feed = 40
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
.bild_links, .bild_bu_links {
|
||||||
|
float:left;
|
||||||
|
line-height:105%;
|
||||||
|
margin:12px 1.4em 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bild_rechts, .bild_bu {
|
||||||
|
float:right;
|
||||||
|
line-height:105%;
|
||||||
|
margin:12px 0 12px 1em;
|
||||||
|
text-align:right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bild_zentriert {
|
||||||
|
clear:both;
|
||||||
|
line-height:105%;
|
||||||
|
margin:.2em auto;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.bild_links, span.bild_rechts, span.bild_zentriert {
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
remove_tags = [dict(id='navi_top'),
|
remove_tags = [dict(id='navi_top'),
|
||||||
dict(id='navi_bottom'),
|
dict(id='navi_bottom'),
|
||||||
dict(id='logo'),
|
dict(id='logo'),
|
||||||
|
@ -9,17 +9,22 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
class Lanacion(BasicNewsRecipe):
|
class Lanacion(BasicNewsRecipe):
|
||||||
title = 'La Nacion'
|
title = 'La Nacion'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic'
|
||||||
description = 'Noticias de Argentina y el resto del mundo'
|
description = "lanacion.com - Informacion actualizada las 24 horas, con noticias de Argentina y del mundo"
|
||||||
publisher = 'La Nacion S.A.'
|
publisher = 'La Nacion S.A.'
|
||||||
category = 'news, politics, Argentina'
|
category = 'news, politics, Argentina'
|
||||||
oldest_article = 2
|
oldest_article = 1
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
language = 'es'
|
language = 'es'
|
||||||
encoding = 'cp1252'
|
publication_type = 'newspaper'
|
||||||
|
remove_empty_feeds = True
|
||||||
masthead_url = 'http://www.lanacion.com.ar/imgs/layout/logos/ln341x47.gif'
|
masthead_url = 'http://www.lanacion.com.ar/imgs/layout/logos/ln341x47.gif'
|
||||||
extra_css = ' h1{font-family: Georgia,serif} body{font-family: Arial,sans-serif} img{margin-top: 0.5em; margin-bottom: 0.2em} .notaEpigrafe{font-size: x-small} '
|
extra_css = """ h1{font-family: Georgia,serif}
|
||||||
|
body{font-family: Arial,sans-serif}
|
||||||
|
img{margin-top: 0.5em; margin-bottom: 0.2em}
|
||||||
|
.notaEpigrafe{font-size: x-small}
|
||||||
|
.topNota h1{font-family: Arial,sans-serif} """
|
||||||
|
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
@ -29,19 +34,19 @@ class Lanacion(BasicNewsRecipe):
|
|||||||
, 'language' : language
|
, 'language' : language
|
||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'class':'nota floatFix'})]
|
keep_only_tags = [dict(name='div', attrs={'class':['nota floatFix','topNota','nota','post']})]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div' , attrs={'class':'notaComentario floatFix noprint' })
|
dict(name='div' , attrs={'class':'notaComentario floatFix noprint' })
|
||||||
,dict(name='ul' , attrs={'class':'cajaHerramientas cajaTop noprint'})
|
,dict(name='ul' , attrs={'class':['cajaHerramientas cajaTop noprint','herramientas noprint']})
|
||||||
,dict(name='div' , attrs={'class':'cajaHerramientas noprint' })
|
,dict(name='div' , attrs={'class':'cajaHerramientas noprint' })
|
||||||
,dict(attrs={'class':['titulosMultimedia','derecha','techo color']})
|
,dict(attrs={'class':['titulosMultimedia','derecha','techo color','encuesta','izquierda compartir','floatFix']})
|
||||||
,dict(name=['iframe','embed','object'])
|
,dict(name=['iframe','embed','object','form','base','hr'])
|
||||||
]
|
]
|
||||||
remove_attributes = ['height','width']
|
remove_tags_after = dict(attrs={'class':['tags','nota-destacado']})
|
||||||
|
remove_attributes = ['height','width','visible']
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Ultimas noticias' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?origen=2' )
|
(u'Ultimas noticias' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?origen=2' )
|
||||||
,(u'Diario de hoy' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?origen=1' )
|
|
||||||
,(u'Politica' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=30' )
|
,(u'Politica' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=30' )
|
||||||
,(u'Economia' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=272' )
|
,(u'Economia' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=272' )
|
||||||
,(u'Deportes' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=131' )
|
,(u'Deportes' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=131' )
|
||||||
@ -50,8 +55,23 @@ class Lanacion(BasicNewsRecipe):
|
|||||||
,(u'Opinion' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=28' )
|
,(u'Opinion' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=28' )
|
||||||
,(u'Espectaculos' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=120' )
|
,(u'Espectaculos' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=120' )
|
||||||
,(u'Exterior' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7' )
|
,(u'Exterior' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7' )
|
||||||
,(u'Ciencia/Salud' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=498' )
|
,(u'Ciencia&Salud' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=498' )
|
||||||
,(u'Revista' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=494' )
|
,(u'Revista' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=494' )
|
||||||
|
,(u'Enfoques' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=421' )
|
||||||
|
,(u'Comercio Exterior' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=347' )
|
||||||
|
,(u'Tecnologia' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=432' )
|
||||||
|
,(u'Arquitectura' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=366' )
|
||||||
|
,(u'Turismo' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=504' )
|
||||||
|
,(u'Al volante' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=371' )
|
||||||
|
,(u'El Campo' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=337' )
|
||||||
|
,(u'Moda y Belleza' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=1312' )
|
||||||
|
,(u'Inmuebles Comerciales', u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=1363' )
|
||||||
|
,(u'Countries' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=1348' )
|
||||||
|
,(u'adnCultura' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=6734' )
|
||||||
|
,(u'The Wall Street Journal Americas', u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=6373' )
|
||||||
|
,(u'Estilo de vida' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7353' )
|
||||||
|
,(u'Management' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7380' )
|
||||||
|
,(u'Bicentenario' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7276' )
|
||||||
]
|
]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
|
@ -6,10 +6,9 @@ www.standardmedia.co.ke
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from calibre import strftime, __appname__, __version__
|
from calibre import strftime, __appname__, __version__
|
||||||
import calibre.utils.PythonMagickWand as pw
|
|
||||||
from ctypes import byref
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.constants import preferred_encoding
|
from calibre.constants import preferred_encoding
|
||||||
|
from calibre.utils.magick import Image
|
||||||
|
|
||||||
class NationKeRecipe(BasicNewsRecipe):
|
class NationKeRecipe(BasicNewsRecipe):
|
||||||
|
|
||||||
@ -95,19 +94,9 @@ class NationKeRecipe(BasicNewsRecipe):
|
|||||||
self.cover_img_path = None
|
self.cover_img_path = None
|
||||||
|
|
||||||
def prepare_cover_image(self, path_to_image, out_path):
|
def prepare_cover_image(self, path_to_image, out_path):
|
||||||
with pw.ImageMagick():
|
img = Image()
|
||||||
img = pw.NewMagickWand()
|
img.open(path_to_image)
|
||||||
if img < 0:
|
img.save(out_path)
|
||||||
raise RuntimeError('Out of memory')
|
|
||||||
if not pw.MagickReadImage(img, path_to_image):
|
|
||||||
severity = pw.ExceptionType(0)
|
|
||||||
msg = pw.MagickGetException(img, byref(severity))
|
|
||||||
raise IOError('Failed to read image from: %s: %s'
|
|
||||||
%(path_to_image, msg))
|
|
||||||
if not pw.MagickWriteImage(img, out_path):
|
|
||||||
raise RuntimeError('Failed to save image to %s'%out_path)
|
|
||||||
pw.DestroyMagickWand(img)
|
|
||||||
|
|
||||||
|
|
||||||
def default_cover(self, cover_file):
|
def default_cover(self, cover_file):
|
||||||
'''
|
'''
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008-2010, AprilHare, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2008-2010, AprilHare, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
'''
|
'''
|
||||||
@ -36,7 +35,7 @@ class NewScientist(BasicNewsRecipe):
|
|||||||
|
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div' , attrs={'class':['hldBd','adline','pnl','infotext' ]})
|
dict(name='div' , attrs={'class':['hldBd','adline','pnl','infotext' ]})
|
||||||
,dict(name='div' , attrs={'id' :['compnl','artIssueInfo','artTools','comments','blgsocial']})
|
,dict(name='div' , attrs={'id' :['compnl','artIssueInfo','artTools','comments','blgsocial','sharebtns']})
|
||||||
,dict(name='p' , attrs={'class':['marker','infotext' ]})
|
,dict(name='p' , attrs={'class':['marker','infotext' ]})
|
||||||
,dict(name='meta' , attrs={'name' :'description' })
|
,dict(name='meta' , attrs={'name' :'description' })
|
||||||
,dict(name='a' , attrs={'rel' :'tag' })
|
,dict(name='a' , attrs={'rel' :'tag' })
|
||||||
|
@ -11,7 +11,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
class Novosti(BasicNewsRecipe):
|
class Novosti(BasicNewsRecipe):
|
||||||
title = 'Vecernje Novosti'
|
title = 'Vecernje Novosti'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic'
|
||||||
description = 'Vesti'
|
description = 'U početku su bile istinske večernje novine - pokrenute u vreme Tršćanske krize, Italijansko-jugoslovenskog konflikta oko grada Trsta - ali su brzo izrasle u dnevni informativno-politički list, koji već godinama ima najveći tiraž u Srbiji.'
|
||||||
publisher = 'Kompanija Novosti'
|
publisher = 'Kompanija Novosti'
|
||||||
category = 'news, politics, Serbia'
|
category = 'news, politics, Serbia'
|
||||||
oldest_article = 2
|
oldest_article = 2
|
||||||
@ -21,24 +21,22 @@ class Novosti(BasicNewsRecipe):
|
|||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
language = 'sr'
|
language = 'sr'
|
||||||
publication_type = 'newspaper'
|
publication_type = 'newspaper'
|
||||||
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)} .article_description,body{font-family: Tahoma,Arial,Helvetica,sans1,sans-serif} '
|
extra_css = """ @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
||||||
|
.article_description,body{font-family: Arial,Helvetica,sans1,sans-serif}
|
||||||
|
.author{font-size: small}
|
||||||
|
.articleLead{font-size: large; font-weight: bold}
|
||||||
|
"""
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
, 'tags' : category
|
, 'tags' : category
|
||||||
, 'publisher' : publisher
|
, 'publisher' : publisher
|
||||||
, 'language' : language
|
, 'language' : language
|
||||||
, 'linearize_tables' : True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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='div', attrs={'class':'jednaVest'})]
|
keep_only_tags = [dict(attrs={'class':['articleTitle','author','articleLead','articleBody']})]
|
||||||
remove_tags = [dict(name='div', attrs={'class':['info','info_bottom','clip_div']})]
|
remove_tags = [dict(name=['embed','object','iframe','base'])]
|
||||||
|
|
||||||
feeds = [(u'Vesti', u'http://www.novosti.rs/php/vesti/rss.php')]
|
feeds = [(u'Vesti', u'http://www.novosti.rs/rss/rss-vesti')]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
|
||||||
for item in soup.findAll(style=True):
|
|
||||||
del item['style']
|
|
||||||
return self.adeify_images(soup)
|
|
||||||
|
@ -6,6 +6,7 @@ class AdvancedUserRecipe1257302745(BasicNewsRecipe):
|
|||||||
language = 'en'
|
language = 'en'
|
||||||
__author__ = 'onyxrev'
|
__author__ = 'onyxrev'
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
remove_tags_before = {'class':'storytitle'}
|
remove_tags_before = {'class':'storytitle'}
|
||||||
remove_tags_after = dict(name='div', attrs={'id':'storytext' })
|
remove_tags_after = dict(name='div', attrs={'id':'storytext' })
|
||||||
|
@ -14,33 +14,39 @@ class Nspm(BasicNewsRecipe):
|
|||||||
description = 'Casopis za politicku teoriju i drustvena istrazivanja'
|
description = 'Casopis za politicku teoriju i drustvena istrazivanja'
|
||||||
publisher = 'NSPM'
|
publisher = 'NSPM'
|
||||||
category = 'news, politics, Serbia'
|
category = 'news, politics, Serbia'
|
||||||
oldest_article = 2
|
oldest_article = 7
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
INDEX = 'http://www.nspm.rs/?alphabet=l'
|
INDEX = 'http://www.nspm.rs/?alphabet=l'
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
language = 'sr'
|
language = 'sr'
|
||||||
|
delay = 2
|
||||||
publication_type = 'magazine'
|
publication_type = 'magazine'
|
||||||
masthead_url = 'http://www.nspm.rs/templates/jsn_epic_pro/images/logol.jpg'
|
masthead_url = 'http://www.nspm.rs/templates/jsn_epic_pro/images/logol.jpg'
|
||||||
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: "Times New Roman", serif1, serif} .article_description{font-family: Arial, sans1, sans-serif} img{margin-top:0.5em; margin-bottom: 0.7em} .author{color: #990000; font-weight: bold} .author,.createdate{font-size: 0.9em} img{margin-top:0.5em; margin-bottom: 0.7em} '
|
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: "Times New Roman", serif1, serif}
|
||||||
|
.article_description{font-family: Arial, sans1, sans-serif}
|
||||||
|
img{margin-top:0.5em; margin-bottom: 0.7em}
|
||||||
|
.author{color: #990000; font-weight: bold}
|
||||||
|
.author,.createdate{font-size: 0.9em} """
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
, 'tags' : category
|
, 'tags' : category
|
||||||
, 'publisher' : publisher
|
, 'publisher' : publisher
|
||||||
, 'language' : language
|
, 'language' : language
|
||||||
|
, 'linearize_tables' : True
|
||||||
}
|
}
|
||||||
|
|
||||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||||
|
keep_only_tags = [dict(attrs={'id':'jsn-mainbody'})]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name=['link','object','embed','script','meta'])
|
dict(name=['link','object','embed','script','meta','base','iframe'])
|
||||||
,dict(name='td', attrs={'class':'buttonheading'})
|
,dict(attrs={'class':'buttonheading'})
|
||||||
]
|
]
|
||||||
keep_only_tags = [
|
remove_tags_after = dict(attrs={'class':'article_separator'})
|
||||||
dict(attrs={'class':['contentpagetitle','author','createdate']})
|
|
||||||
,dict(name='p')
|
|
||||||
]
|
|
||||||
remove_attributes = ['width','height']
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
def get_browser(self):
|
def get_browser(self):
|
||||||
@ -48,25 +54,18 @@ class Nspm(BasicNewsRecipe):
|
|||||||
br.open(self.INDEX)
|
br.open(self.INDEX)
|
||||||
return br
|
return br
|
||||||
|
|
||||||
feeds = [(u'Nova srpska politicka misao', u'http://www.nspm.rs/feed/rss.html')]
|
feeds = [
|
||||||
|
(u'Rubrike' , u'http://www.nspm.rs/rubrike/feed/rss.html')
|
||||||
def print_version(self, url):
|
,(u'Debate' , u'http://www.nspm.rs/debate/feed/rss.html')
|
||||||
return url.replace('.html','/stampa.html')
|
,(u'Reci i misli' , u'http://www.nspm.rs/reci-i-misli/feed/rss.html')
|
||||||
|
,(u'Samo smeh srbina spasava', u'http://www.nspm.rs/samo-smeh-srbina-spasava/feed/rss.html')
|
||||||
|
,(u'Polemike' , u'http://www.nspm.rs/polemike/feed/rss.html')
|
||||||
|
,(u'Prikazi' , u'http://www.nspm.rs/prikazi/feed/rss.html')
|
||||||
|
,(u'Prenosimo' , u'http://www.nspm.rs/prenosimo/feed/rss.html')
|
||||||
|
,(u'Hronika' , u'http://www.nspm.rs/tabela/hronika/feed/rss.html')
|
||||||
|
]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
for item in soup.body.findAll(style=True):
|
for item in soup.body.findAll(style=True):
|
||||||
del item['style']
|
del item['style']
|
||||||
att = soup.find('a',attrs={'class':'contentpagetitle'})
|
return self.adeify_images(soup)
|
||||||
if att:
|
|
||||||
att.name = 'h1';
|
|
||||||
del att['href']
|
|
||||||
att2 = soup.find('td')
|
|
||||||
if att2:
|
|
||||||
att2.name = 'p';
|
|
||||||
del att['valign']
|
|
||||||
for pt in soup.findAll('img'):
|
|
||||||
brtag = Tag(soup,'br')
|
|
||||||
brtag2 = Tag(soup,'br')
|
|
||||||
pt.append(brtag)
|
|
||||||
pt.append(brtag2)
|
|
||||||
return soup
|
|
@ -1,7 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
'''
|
'''
|
||||||
nspm.rs/nspm-in-english
|
nspm.rs/nspm-in-english
|
||||||
'''
|
'''
|
||||||
@ -11,29 +9,44 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
class Nspm_int(BasicNewsRecipe):
|
class Nspm_int(BasicNewsRecipe):
|
||||||
title = 'NSPM in English'
|
title = 'NSPM in English'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic'
|
||||||
description = 'Magazine dedicated to political theory and sociological research'
|
description = 'Magazine dedicated to political theory and sociological research'
|
||||||
oldest_article = 20
|
publisher = 'NSPM'
|
||||||
|
category = 'news, politics, Serbia'
|
||||||
|
oldest_article = 7
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
language = 'en'
|
|
||||||
|
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
INDEX = 'http://www.nspm.rs/?alphabet=l'
|
encoding = 'utf-8'
|
||||||
cover_url = 'http://nspm.rs/templates/jsn_epic_pro/images/logol.jpg'
|
language = 'en'
|
||||||
html2lrf_options = [
|
delay = 2
|
||||||
'--comment', description
|
publication_type = 'magazine'
|
||||||
, '--base-font-size', '10'
|
masthead_url = 'http://www.nspm.rs/templates/jsn_epic_pro/images/logol.jpg'
|
||||||
, '--category', 'news, politics, Serbia, english'
|
extra_css = """
|
||||||
, '--publisher', 'IIC NSPM'
|
body{font-family: "Times New Roman", serif}
|
||||||
]
|
.article_description{font-family: Arial, sans-serif}
|
||||||
|
img{margin-top:0.5em; margin-bottom: 0.7em}
|
||||||
|
.author{color: #990000; font-weight: bold}
|
||||||
|
.author,.createdate{font-size: 0.9em} """
|
||||||
|
|
||||||
def get_browser(self):
|
conversion_options = {
|
||||||
br = BasicNewsRecipe.get_browser()
|
'comment' : description
|
||||||
br.open(self.INDEX)
|
, 'tags' : category
|
||||||
return br
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
, 'linearize_tables' : True
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [dict(attrs={'id':'jsn-mainbody'})]
|
||||||
|
remove_tags = [
|
||||||
|
dict(name=['link','object','embed','script','meta','base','iframe'])
|
||||||
|
,dict(attrs={'class':'buttonheading'})
|
||||||
|
]
|
||||||
|
remove_tags_after = dict(attrs={'class':'article_separator'})
|
||||||
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':'jsn-mainbody'})]
|
feeds = [(u'Articles', u'http://www.nspm.rs/nspm-in-english/feed/rss.html')]
|
||||||
remove_tags = [dict(name='div', attrs={'id':'yvComment' })]
|
|
||||||
|
|
||||||
feeds = [ (u'NSPM in English', u'http://nspm.rs/nspm-in-english/feed/rss.html')]
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.body.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return self.adeify_images(soup)
|
38
resources/recipes/orlando_sentinel.recipe
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1279258912(BasicNewsRecipe):
|
||||||
|
title = u'Orlando Sentinel'
|
||||||
|
oldest_article = 3
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'News', u'http://feeds.feedburner.com/orlandosentinel/news'),
|
||||||
|
(u'Opinion', u'http://feeds.feedburner.com/orlandosentinel/news/opinion'),
|
||||||
|
(u'Business', u'http://feeds.feedburner.com/orlandosentinel/business'),
|
||||||
|
(u'Technology', u'http://feeds.feedburner.com/orlandosentinel/technology'),
|
||||||
|
(u'Space and Science', u'http://feeds.feedburner.com/orlandosentinel/news/space'),
|
||||||
|
(u'Entertainment', u'http://feeds.feedburner.com/orlandosentinel/entertainment'),
|
||||||
|
(u'Life and Family', u'http://feeds.feedburner.com/orlandosentinel/features/lifestyle'),
|
||||||
|
]
|
||||||
|
__author__ = 'rty'
|
||||||
|
pubisher = 'OrlandoSentinel.com'
|
||||||
|
description = 'Orlando, Florida, Newspaper'
|
||||||
|
category = 'News, Orlando, Florida'
|
||||||
|
|
||||||
|
|
||||||
|
remove_javascript = True
|
||||||
|
use_embedded_content = False
|
||||||
|
no_stylesheets = True
|
||||||
|
language = 'en'
|
||||||
|
encoding = 'utf-8'
|
||||||
|
conversion_options = {'linearize_tables':True}
|
||||||
|
masthead_url = 'http://www.orlandosentinel.com/media/graphic/2009-07/46844851.gif'
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'class':'story'})
|
||||||
|
]
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'class':['articlerail','tools','comment-group','clearfix']}),
|
||||||
|
]
|
||||||
|
remove_tags_after = [
|
||||||
|
dict(name='p', attrs={'class':'copyright'}),
|
||||||
|
]
|
@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
'''
|
'''
|
||||||
sp.rian.ru
|
sp.rian.ru
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class Ria_eng(BasicNewsRecipe):
|
class Ria_esp(BasicNewsRecipe):
|
||||||
title = 'Ria Novosti'
|
title = 'Ria Novosti'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic'
|
||||||
description = 'Noticias desde Russia en Castellano'
|
description = 'Noticias desde Russia en Castellano'
|
||||||
@ -28,14 +28,10 @@ class Ria_eng(BasicNewsRecipe):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'class':'articletxt'})]
|
keep_only_tags = [dict(name='div', attrs={'class':['mainnewsrubric','titleblock','mainnewstxt']})]
|
||||||
remove_tags = [dict(name=['object','link','iframe','base'])]
|
remove_tags = [dict(name=['object','link','iframe','base'])]
|
||||||
remove_tags_after = dict(name='div',attrs={'class':'text'})
|
|
||||||
|
|
||||||
|
|
||||||
feeds = [(u'Noticias', u'http://sp.rian.ru/export/rss2/index.xml')]
|
feeds = [(u'Noticias', u'http://rss.feedsportal.com/c/860/fe.ed/sp.rian.ru/export/rss2/index.xml')]
|
||||||
|
|
||||||
def print_version(self, url):
|
|
||||||
return url.replace('.html','-print.html')
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class ScientificAmerican(BasicNewsRecipe):
|
|||||||
description = u'Popular science. Monthly magazine.'
|
description = u'Popular science. Monthly magazine.'
|
||||||
__author__ = 'Kovid Goyal and Sujata Raman'
|
__author__ = 'Kovid Goyal and Sujata Raman'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
remove_javascript = True
|
||||||
oldest_article = 30
|
oldest_article = 30
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
@ -31,11 +31,13 @@ class ScientificAmerican(BasicNewsRecipe):
|
|||||||
remove_tags_after = dict(id=['article'])
|
remove_tags_after = dict(id=['article'])
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(id=['sharetools', 'reddit']),
|
dict(id=['sharetools', 'reddit']),
|
||||||
dict(name='script'),
|
#dict(name='script'),
|
||||||
{'class':['float_left', 'atools']},
|
{'class':['float_left', 'atools']},
|
||||||
{"class": re.compile(r'also-in-this')},
|
{"class": re.compile(r'also-in-this')},
|
||||||
dict(name='a',title = ["Get the Rest of the Article","Subscribe","Buy this Issue"]),
|
dict(name='a',title = ["Get the Rest of the Article","Subscribe","Buy this Issue"]),
|
||||||
dict(name = 'img',alt = ["Graphic - Get the Rest of the Article"]),
|
dict(name = 'img',alt = ["Graphic - Get the Rest of the Article"]),
|
||||||
|
dict(name='div', attrs={'class':['commentbox']}),
|
||||||
|
dict(name='h2', attrs={'class':['discuss_h2']}),
|
||||||
]
|
]
|
||||||
|
|
||||||
html2lrf_options = ['--base-font-size', '8']
|
html2lrf_options = ['--base-font-size', '8']
|
||||||
@ -110,3 +112,10 @@ class ScientificAmerican(BasicNewsRecipe):
|
|||||||
div.extract()
|
div.extract()
|
||||||
|
|
||||||
return soup
|
return soup
|
||||||
|
|
||||||
|
preprocess_regexps = [
|
||||||
|
(re.compile(r'Already a Digital subscriber.*Now</a>', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||||
|
(re.compile(r'If your institution has site license access, enter.*here</a>.', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||||
|
(re.compile(r'to subscribe to our.*;.*\}', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||||
|
(re.compile(r'\)\(jQuery\);.*-->', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||||
|
]
|
||||||
|
46
resources/recipes/snopes.recipe
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Starson17'
|
||||||
|
'''
|
||||||
|
snopes.com
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Snopes(BasicNewsRecipe):
|
||||||
|
title = 'Snopes'
|
||||||
|
__author__ = 'Starson17'
|
||||||
|
description = 'Urban Legends'
|
||||||
|
oldest_article = 21
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'utf8'
|
||||||
|
publisher = 'Snopes'
|
||||||
|
category = 'news, '
|
||||||
|
language = 'en'
|
||||||
|
publication_type = 'newsportal'
|
||||||
|
remove_javascript = True
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comments' : description
|
||||||
|
,'tags' : category
|
||||||
|
,'language' : language
|
||||||
|
,'publisher' : publisher
|
||||||
|
,'linearize_tables': True
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='h1'),
|
||||||
|
dict(name='div', attrs={'class':['article_text']}),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('Snopes', 'http://www.snopes.com/info/whatsnew.xml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Trebuchet MS,Bookman Old Style,Arial;color:#75b570}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:medium;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
'''
|
@ -6,11 +6,10 @@ www.standardmedia.co.ke
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from calibre import strftime, __appname__, __version__
|
from calibre import strftime, __appname__, __version__
|
||||||
import calibre.utils.PythonMagickWand as pw
|
|
||||||
from ctypes import byref
|
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.constants import preferred_encoding
|
from calibre.constants import preferred_encoding
|
||||||
|
from calibre.utils.magick import Image
|
||||||
|
|
||||||
class StandardMediaKeRecipe(BasicNewsRecipe):
|
class StandardMediaKeRecipe(BasicNewsRecipe):
|
||||||
|
|
||||||
@ -88,19 +87,9 @@ class StandardMediaKeRecipe(BasicNewsRecipe):
|
|||||||
self.cover_img_path = None
|
self.cover_img_path = None
|
||||||
|
|
||||||
def prepare_cover_image(self, path_to_image, out_path):
|
def prepare_cover_image(self, path_to_image, out_path):
|
||||||
with pw.ImageMagick():
|
img = Image()
|
||||||
img = pw.NewMagickWand()
|
img.open(path_to_image)
|
||||||
if img < 0:
|
img.save(out_path)
|
||||||
raise RuntimeError('Out of memory')
|
|
||||||
if not pw.MagickReadImage(img, path_to_image):
|
|
||||||
severity = pw.ExceptionType(0)
|
|
||||||
msg = pw.MagickGetException(img, byref(severity))
|
|
||||||
raise IOError('Failed to read image from: %s: %s'
|
|
||||||
%(path_to_image, msg))
|
|
||||||
if not pw.MagickWriteImage(img, out_path):
|
|
||||||
raise RuntimeError('Failed to save image to %s'%out_path)
|
|
||||||
pw.DestroyMagickWand(img)
|
|
||||||
|
|
||||||
|
|
||||||
def default_cover(self, cover_file):
|
def default_cover(self, cover_file):
|
||||||
'''
|
'''
|
||||||
|
@ -30,10 +30,12 @@ class Starbulletin(BasicNewsRecipe):
|
|||||||
}
|
}
|
||||||
|
|
||||||
remove_tags_before = dict(attrs={'id':'storyTitle'})
|
remove_tags_before = dict(attrs={'id':'storyTitle'})
|
||||||
remove_tags_after = dict(name='div', attrs={'class':'storytext'})
|
remove_tags_after = dict(name='div',attrs={'class':'storytext'})
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name=['object','link'])
|
dict(name=['object','link','script','span'])
|
||||||
,dict(attrs={'class':'insideStoryImage'})
|
,dict(attrs={'class':'insideStoryImage'})
|
||||||
|
,dict(attrs={'name':'fb_share'})
|
||||||
|
,dict(name='div',attrs={'class':'storytext'})
|
||||||
]
|
]
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
|
@ -10,7 +10,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
|
|
||||||
class TagesspiegelRSS(BasicNewsRecipe):
|
class TagesspiegelRSS(BasicNewsRecipe):
|
||||||
title = u'Der Tagesspiegel'
|
title = u'Der Tagesspiegel'
|
||||||
__author__ = 'ipaschke'
|
__author__ = 'Ingo Paschke'
|
||||||
language = 'de'
|
language = 'de'
|
||||||
oldest_article = 7
|
oldest_article = 7
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
@ -26,7 +26,7 @@ class TagesspiegelRSS(BasicNewsRecipe):
|
|||||||
.quote .cite{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:xx-small}
|
.quote .cite{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:xx-small}
|
||||||
.hcf-inline-left{float:left;margin-right:15px;position:relative;}
|
.hcf-inline-left{float:left;margin-right:15px;position:relative;}
|
||||||
.hcf-inline-right{float:right;margin-right:15px;position:relative;}
|
.hcf-inline-right{float:right;margin-right:15px;position:relative;}
|
||||||
.hcf-smart-box{font-family: Arial, Helvetica, sans-serif; font-size: xx-small; margin: 0px 15px 8px 0px; width: 300px;}
|
.hcf-smart-box{font-family: Arial, Helvetica, sans-serif; font-size: xx-small; margin: 0px 15px 8px 0px; width: 300px;}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
@ -39,25 +39,30 @@ class TagesspiegelRSS(BasicNewsRecipe):
|
|||||||
dict(name='link'), dict(name='iframe'),dict(name='style'),dict(name='meta'),dict(name='button'),
|
dict(name='link'), dict(name='iframe'),dict(name='style'),dict(name='meta'),dict(name='button'),
|
||||||
dict(name='div', attrs={'class':["hcf-jump-to-comments","hcf-clear","hcf-magnify hcf-media-control"] }),
|
dict(name='div', attrs={'class':["hcf-jump-to-comments","hcf-clear","hcf-magnify hcf-media-control"] }),
|
||||||
dict(name='span', attrs={'class':["hcf-mainsearch",] }),
|
dict(name='span', attrs={'class':["hcf-mainsearch",] }),
|
||||||
dict(name='ul', attrs={'class':["hcf-tools"] }),
|
dict(name='ul', attrs={'class':["hcf-tools"]}),
|
||||||
|
dict(name='ul', attrs={'class': re.compile('hcf-services')})
|
||||||
]
|
]
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
soup = self.index_to_soup('http://www.tagesspiegel.de/zeitung/')
|
soup = self.index_to_soup('http://www.tagesspiegel.de/zeitung/')
|
||||||
|
|
||||||
def feed_title(div):
|
def feed_title(div):
|
||||||
return ''.join(div.findAll(text=True, recursive=False)).strip()
|
return ''.join(div.findAll(text=True, recursive=False)).strip() if div is not None else None
|
||||||
|
|
||||||
articles = {}
|
articles = {}
|
||||||
key = None
|
key = None
|
||||||
ans = []
|
ans = []
|
||||||
|
maincol = soup.find('div', attrs={'class':re.compile('hcf-main-col')})
|
||||||
|
|
||||||
for div in soup.findAll(True, attrs={'class':['hcf-teaser', 'hcf-header', 'story headline']}):
|
for div in maincol.findAll(True, attrs={'class':['hcf-teaser', 'hcf-header', 'story headline']}):
|
||||||
|
|
||||||
if div['class'] == 'hcf-header':
|
if div['class'] == 'hcf-header':
|
||||||
key = string.capwords(feed_title(div.em.a))
|
try:
|
||||||
articles[key] = []
|
key = string.capwords(feed_title(div.em.a))
|
||||||
ans.append(key)
|
articles[key] = []
|
||||||
|
ans.append(key)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
elif div['class'] == 'hcf-teaser' and getattr(div.contents[0],'name','') == 'h2':
|
elif div['class'] == 'hcf-teaser' and getattr(div.contents[0],'name','') == 'h2':
|
||||||
a = div.find('a', href=True)
|
a = div.find('a', href=True)
|
||||||
@ -83,4 +88,3 @@ class TagesspiegelRSS(BasicNewsRecipe):
|
|||||||
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
||||||
|
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
44
resources/recipes/technology_review.recipe
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import string
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class TechnologyReview(BasicNewsRecipe):
|
||||||
|
title = u'Technology Review'
|
||||||
|
__author__ = 'rty'
|
||||||
|
description = 'MIT Technology Magazine'
|
||||||
|
publisher = 'Technology Review Inc.'
|
||||||
|
category = 'Technology, Innovation, R&D'
|
||||||
|
oldest_article = 14
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
No_stylesheets = True
|
||||||
|
extra_css = """
|
||||||
|
.ArticleBody {font: normal; text-align: justify}
|
||||||
|
.headline {font: bold x-large}
|
||||||
|
.subheadline {font: italic large}
|
||||||
|
"""
|
||||||
|
feeds = [
|
||||||
|
(u'Computing', u'http://feeds.technologyreview.com/technology_review_Computing'),
|
||||||
|
(u'Web', u'http://feeds.technologyreview.com/technology_review_Web'),
|
||||||
|
(u'Communications', u'http://feeds.technologyreview.com/technology_review_Communications'),
|
||||||
|
(u'Energy', u'http://feeds.technologyreview.com/technology_review_Energy'),
|
||||||
|
(u'Materials', u'http://feeds.technologyreview.com/technology_review_Materials'),
|
||||||
|
(u'Biomedicine', u'http://feeds.technologyreview.com/technology_review_Biotech'),
|
||||||
|
(u'Business', u'http://feeds.technologyreview.com/technology_review_Biztech')
|
||||||
|
]
|
||||||
|
remove_attributes = ['width', 'align','cellspacing']
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'id':['CloseLink','footerAdDiv','copyright']}),
|
||||||
|
]
|
||||||
|
remove_tags_after = [dict(name='div', attrs={'id':'copyright'})]
|
||||||
|
|
||||||
|
def get_article_url(self, article):
|
||||||
|
return article.get('guid', article.get('id', None))
|
||||||
|
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
baseurl='http://www.technologyreview.com/printer_friendly_article.aspx?id='
|
||||||
|
split1 = string.split(url,"/")
|
||||||
|
xxx=split1 [4]
|
||||||
|
split2= string.split(xxx,"/")
|
||||||
|
s = baseurl + split2[0]
|
||||||
|
return s
|
@ -50,12 +50,14 @@ class cdnet(BasicNewsRecipe):
|
|||||||
dict(name='div', attrs={'class':'greyBoxR clearfix'}),
|
dict(name='div', attrs={'class':'greyBoxR clearfix'}),
|
||||||
dict(name='div', attrs={'class':'greyBoxL clearfix'}),
|
dict(name='div', attrs={'class':'greyBoxL clearfix'}),
|
||||||
dict(name='div', attrs={'class':'greyBox clearfix'}),
|
dict(name='div', attrs={'class':'greyBox clearfix'}),
|
||||||
|
dict(name='div', attrs={'class':'labelized'}),
|
||||||
dict(id='')]
|
dict(id='')]
|
||||||
#remove_tags_before = [dict(id='header-news-title')]
|
#remove_tags_before = [dict(id='header-news-title')]
|
||||||
remove_tags_after = [dict(name='div', attrs={'class':'btmGreyTables'})]
|
remove_tags_after = [dict(name='div', attrs={'class':'labelized'})]
|
||||||
#remove_tags_after = [dict(name='div', attrs={'class':'intelliTXT'})]
|
#remove_tags_after = [dict(name='div', attrs={'class':'intelliTXT'})]
|
||||||
|
|
||||||
feeds = [ ('tomshardware', 'http://www.tomshardware.com/de/feeds/rss2/tom-s-hardware-de,12-1.xml') ]
|
feeds = [ ('tomshardware', 'http://www.tomshardware.com/de/feeds/rss2/tom-s-hardware-de,12-1.xml') ]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class Vijesti(BasicNewsRecipe):
|
|||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':'mainnews'})]
|
keep_only_tags = [dict(name='div', attrs={'id':'mainnews'})]
|
||||||
|
|
||||||
remove_tags = [dict(name=['object','link','embed'])]
|
remove_tags = [dict(name=['object','link','embed','form'])]
|
||||||
|
|
||||||
feeds = [(u'Sve vijesti', u'http://www.vijesti.me/rss.php' )]
|
feeds = [(u'Sve vijesti', u'http://www.vijesti.me/rss.php' )]
|
||||||
|
|
||||||
|
34
resources/recipes/waco_tribune.recipe
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1278773519(BasicNewsRecipe):
|
||||||
|
title = u'Waco Tribune Herald'
|
||||||
|
__author__ = 'rty'
|
||||||
|
pubisher = 'A Robinson Media Company'
|
||||||
|
description = 'Waco, Texas, Newspaper'
|
||||||
|
category = 'News, Texas, Waco'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'News', u'http://www.wacotrib.com/news/index.rss2'),
|
||||||
|
(u'Sports', u'http://www.wacotrib.com/sports/index.rss2'),
|
||||||
|
(u'AccessWaco', u'http://www.wacotrib.com/accesswaco/index.rss2'),
|
||||||
|
(u'Opinions', u'http://www.wacotrib.com/opinion/index.rss2')
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_javascript = True
|
||||||
|
use_embedded_content = False
|
||||||
|
no_stylesheets = True
|
||||||
|
language = 'en'
|
||||||
|
encoding = 'utf-8'
|
||||||
|
conversion_options = {'linearize_tables':True}
|
||||||
|
masthead_url = 'http://media.wacotrib.com/designimages/wacotrib_logo.jpg'
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'class':'twoColumn left'}),
|
||||||
|
]
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'class':'right blueLinks'}),
|
||||||
|
]
|
||||||
|
remove_tags_after = [
|
||||||
|
dict(name='div', attrs={'class':'dottedRule'}),
|
||||||
|
]
|
@ -22,7 +22,7 @@ class weltDe(BasicNewsRecipe):
|
|||||||
remove_stylesheets = True
|
remove_stylesheets = True
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
html2epub_options = 'linearize_tables = True\nbase_font_size2=10'
|
html2epub_options = 'base_font_size=10'
|
||||||
BasicNewsRecipe.summary_length = 100
|
BasicNewsRecipe.summary_length = 100
|
||||||
|
|
||||||
|
|
||||||
@ -83,10 +83,9 @@ class weltDe(BasicNewsRecipe):
|
|||||||
dict(name='div', attrs={'class':'articleOptions clear'}),
|
dict(name='div', attrs={'class':'articleOptions clear'}),
|
||||||
dict(name='div', attrs={'class':'noPrint galleryIndex'}),
|
dict(name='div', attrs={'class':'noPrint galleryIndex'}),
|
||||||
dict(name='div', attrs={'class':'inlineBox inlineTagCloud'}),
|
dict(name='div', attrs={'class':'inlineBox inlineTagCloud'}),
|
||||||
|
dict(name='div', attrs={'class':'clear module imageGalleryBig bgColor1'}),
|
||||||
dict(name='div', attrs={'class':'clear module writeComment bgColor1'}),
|
dict(name='div', attrs={'class':'clear module writeComment bgColor1'}),
|
||||||
dict(name='div', attrs={'class':'clear module textGallery bgColor1'}),
|
dict(name='div', attrs={'class':'clear module textGallery bgColor1'}),
|
||||||
dict(name='div', attrs={'class':'clear module socialMedia bgColor1'}),
|
|
||||||
dict(name='div', attrs={'class':'clear module continuativeLinks'}),
|
|
||||||
dict(name='div', attrs={'class':'moreArtH3'}),
|
dict(name='div', attrs={'class':'moreArtH3'}),
|
||||||
dict(name='div', attrs={'class':'jqmWindow'}),
|
dict(name='div', attrs={'class':'jqmWindow'}),
|
||||||
dict(name='div', attrs={'class':'clear gap4'}),
|
dict(name='div', attrs={'class':'clear gap4'}),
|
||||||
@ -99,7 +98,7 @@ class weltDe(BasicNewsRecipe):
|
|||||||
dict(name='div', attrs={'class':'headLineH3'}),
|
dict(name='div', attrs={'class':'headLineH3'}),
|
||||||
dict(name='div', attrs={'class':'print'}),
|
dict(name='div', attrs={'class':'print'}),
|
||||||
dict(name='div', attrs={'class':'clear menu'}),
|
dict(name='div', attrs={'class':'clear menu'}),
|
||||||
dict(name='div', attrs={'class':'clear galleryContent'}),
|
dict(name='div', attrs={'class':'themenalarm'}),
|
||||||
dict(name='p', attrs={'class':'jump'}),
|
dict(name='p', attrs={'class':'jump'}),
|
||||||
dict(name='a', attrs={'class':'commentLink'}),
|
dict(name='a', attrs={'class':'commentLink'}),
|
||||||
dict(name='h2', attrs={'class':'jumpHeading'}),
|
dict(name='h2', attrs={'class':'jumpHeading'}),
|
||||||
@ -110,7 +109,7 @@ class weltDe(BasicNewsRecipe):
|
|||||||
dict(name='table', attrs={'class':'textGallery'}),
|
dict(name='table', attrs={'class':'textGallery'}),
|
||||||
dict(name='li', attrs={'class':'active'})]
|
dict(name='li', attrs={'class':'active'})]
|
||||||
|
|
||||||
remove_tags_after = [dict(name='div', attrs={'class':'clear departmentLine'})]
|
remove_tags_after = [dict(name='div', attrs={'class':'themenalarm'})]
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
h2{font-family:Arial,Helvetica,sans-serif; font-size: x-small; color: #003399;}
|
h2{font-family:Arial,Helvetica,sans-serif; font-size: x-small; color: #003399;}
|
||||||
@ -122,6 +121,7 @@ class weltDe(BasicNewsRecipe):
|
|||||||
.photo {font-family:Arial,Helvetica,sans-serif; font-size: x-small; color: #666666;} '''
|
.photo {font-family:Arial,Helvetica,sans-serif; font-size: x-small; color: #666666;} '''
|
||||||
|
|
||||||
feeds = [ ('Politik', 'http://welt.de/politik/?service=Rss'),
|
feeds = [ ('Politik', 'http://welt.de/politik/?service=Rss'),
|
||||||
|
('Deutsche Dinge', 'http://www.welt.de/deutsche-dinge/?service=Rss'),
|
||||||
('Wirtschaft', 'http://welt.de/wirtschaft/?service=Rss'),
|
('Wirtschaft', 'http://welt.de/wirtschaft/?service=Rss'),
|
||||||
('Finanzen', 'http://welt.de/finanzen/?service=Rss'),
|
('Finanzen', 'http://welt.de/finanzen/?service=Rss'),
|
||||||
('Sport', 'http://welt.de/sport/?service=Rss'),
|
('Sport', 'http://welt.de/sport/?service=Rss'),
|
||||||
@ -137,3 +137,4 @@ class weltDe(BasicNewsRecipe):
|
|||||||
def print_version(self, url):
|
def print_version(self, url):
|
||||||
return url.replace ('.html', '.html?print=true')
|
return url.replace ('.html', '.html?print=true')
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,88 +6,105 @@ Fetch Die Zeit.
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ebooks.BeautifulSoup import Tag
|
||||||
|
|
||||||
class ZeitDe(BasicNewsRecipe):
|
class ZeitDe(BasicNewsRecipe):
|
||||||
|
|
||||||
title = 'Die Zeit Nachrichten'
|
title = 'ZEIT Online'
|
||||||
description = 'Die Zeit - Online Nachrichten'
|
description = 'ZEIT Online'
|
||||||
language = 'de'
|
language = 'de'
|
||||||
lang = 'de_DE'
|
lang = 'de_DE'
|
||||||
|
|
||||||
__author__ = 'Martin Pitt and Sujata Raman'
|
__author__ = 'Martin Pitt, Sujata Raman and Ingo Paschke'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
max_articles_per_feed = 40
|
max_articles_per_feed = 40
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
no_javascript = True
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
|
('Seite 1', 'http://newsfeed.zeit.de/index_xml'),
|
||||||
('Politik', 'http://newsfeed.zeit.de/politik/index'),
|
('Politik', 'http://newsfeed.zeit.de/politik/index'),
|
||||||
('Wirtschaft', 'http://newsfeed.zeit.de/wirtschaft/index'),
|
('Wirtschaft', 'http://newsfeed.zeit.de/wirtschaft/index'),
|
||||||
('Meinung', 'http://newsfeed.zeit.de/meinung/index'),
|
('Meinung', 'http://newsfeed.zeit.de/meinung/index'),
|
||||||
('Gesellschaft', 'http://newsfeed.zeit.de/gesellschaft/index'),
|
('Gesellschaft', 'http://newsfeed.zeit.de/gesellschaft/index'),
|
||||||
('Kultur', 'http://newsfeed.zeit.de/kultur/index'),
|
('Kultur', 'http://newsfeed.zeit.de/kultur/index'),
|
||||||
('Wissen', 'http://newsfeed.zeit.de/wissen/index'),
|
('Wissen', 'http://newsfeed.zeit.de/wissen/index'),
|
||||||
|
('Digital', 'http://newsfeed.zeit.de/digital/index'),
|
||||||
|
('Studium', 'http://newsfeed.zeit.de/studium/index'),
|
||||||
|
('Karriere', 'http://newsfeed.zeit.de/karriere/index'),
|
||||||
|
('Lebensart', 'http://newsfeed.zeit.de/lebensart/index'),
|
||||||
|
('Reisen', 'http://newsfeed.zeit.de/reisen/index'),
|
||||||
|
('Auto', 'http://newsfeed.zeit.de/auto/index'),
|
||||||
|
('Sport', 'http://newsfeed.zeit.de/sport/index'),
|
||||||
]
|
]
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
.supertitle{color:#990000; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
|
.supertitle{color:#990000; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
|
||||||
.excerpt{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:large;}
|
.excerpt{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:small;}
|
||||||
.title{font-family:Arial,Helvetica,sans-serif;font-size:large}
|
.title{font-family:Arial,Helvetica,sans-serif;font-size:large;clear:right;}
|
||||||
.caption{color:#666666; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
|
.caption{color:#666666; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
|
||||||
.copyright{color:#666666; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
|
.copyright{color:#666666; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
|
||||||
.article{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small}
|
.article{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small}
|
||||||
|
.quote{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small}
|
||||||
|
.quote .cite{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:xx-small}
|
||||||
.headline iconportrait_inline{font-family:Arial,Helvetica,sans-serif;font-size:x-small}
|
.headline iconportrait_inline{font-family:Arial,Helvetica,sans-serif;font-size:x-small}
|
||||||
|
.inline{float:left;margin-top:0;margin-right:15px;position:relative;width:180px; }
|
||||||
|
img.inline{float:none}
|
||||||
|
.intertitle{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small;font-weight:700}
|
||||||
|
.ebinfobox{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:xx-small;list-style-type:none;float:right;margin-top:0;border-left-style:solid;border-left-width:1px;padding-left:10px;}
|
||||||
|
.infobox {border-style: solid; border-width: 1px;padding:8px;}
|
||||||
|
.infobox dt {font-weight:700;}
|
||||||
'''
|
'''
|
||||||
#filter_regexps = [r'ad.de.doubleclick.net/']
|
#filter_regexps = [r'ad.de.doubleclick.net/']
|
||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
dict(name='div', attrs={'class':["article"]}) ,
|
dict(name='div', attrs={'class':["article"]}) ,
|
||||||
|
dict(name='ul', attrs={'class':["tools"]}) ,
|
||||||
]
|
]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='link'), dict(name='iframe'),dict(name='style'),
|
dict(name='link'), dict(name='iframe'),dict(name='style'),dict(name='meta'),
|
||||||
dict(name='div', attrs={'class':["pagination block","pagenav","inline link"] }),
|
dict(name='div', attrs={'class':["pagination block","pagenav","inline link", "copyright"] }),
|
||||||
dict(name='div', attrs={'id':["place_5","place_4"]})
|
dict(name='p', attrs={'class':["ressortbacklink", "copyright"] }),
|
||||||
|
dict(name='div', attrs={'id':["place_5","place_4","comments"]})
|
||||||
]
|
]
|
||||||
|
|
||||||
|
remove_attributes = ['style', 'font']
|
||||||
|
|
||||||
def get_article_url(self, article):
|
def get_article_url(self, article):
|
||||||
|
ans = article.get('link',None)
|
||||||
ans = article.get('guid',None)
|
ans += "?page=all"
|
||||||
|
|
||||||
try:
|
|
||||||
self.log('Looking for full story link in', ans)
|
|
||||||
soup = self.index_to_soup(ans)
|
|
||||||
x = soup.find(text="Auf einer Seite lesen")
|
|
||||||
|
|
||||||
if x is not None:
|
|
||||||
|
|
||||||
a = x.parent
|
|
||||||
if a and a.has_key('href'):
|
|
||||||
ans = a['href']
|
|
||||||
self.log('Found full story link', ans)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if 'video' in ans or 'quiz' in ans :
|
if 'video' in ans or 'quiz' in ans :
|
||||||
|
|
||||||
ans = None
|
ans = None
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
def get_cover_url(self):
|
||||||
|
try:
|
||||||
|
inhalt = self.index_to_soup('http://www.zeit.de/inhalt')
|
||||||
|
return inhalt.find('div', attrs={'class':'singlearchive clearfix'}).img['src'].replace('icon_','')
|
||||||
|
except:
|
||||||
|
return 'http://images.zeit.de/bilder/titelseiten_zeit/1946/001_001.jpg'
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
soup.html['xml:lang'] = self.lang
|
soup.html['xml:lang'] = self.lang
|
||||||
soup.html['lang'] = self.lang
|
soup.html['lang'] = self.lang
|
||||||
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
|
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
|
||||||
soup.head.insert(0,mtag)
|
soup.head.insert(0,mtag)
|
||||||
|
title = soup.find('h2', attrs={'class':'title'})
|
||||||
|
if title is None:
|
||||||
|
print "no title"
|
||||||
|
return soup
|
||||||
|
info = Tag(soup,'ul',[('class','ebinfobox')])
|
||||||
|
tools = soup.find('ul', attrs={'class':'tools'})
|
||||||
|
#author = tools.find('li','author first')
|
||||||
|
for tag in ['author first', 'date', 'date first', 'author', 'source']:
|
||||||
|
line = tools.find('li', tag)
|
||||||
|
if line:
|
||||||
|
info.insert(0,line)
|
||||||
|
title.parent.insert(0,info)
|
||||||
|
tools.extract()
|
||||||
return soup
|
return soup
|
||||||
|
|
||||||
|
|
||||||
#def print_version(self,url):
|
|
||||||
# return url.replace('http://www.zeit.de/', 'http://images.zeit.de/text/').replace('?from=rss', '')
|
|
||||||
|
|
||||||
|
@ -111,7 +111,6 @@
|
|||||||
or (@shadow = 'true')
|
or (@shadow = 'true')
|
||||||
or (@hidden = 'true')
|
or (@hidden = 'true')
|
||||||
or (@outline = 'true')
|
or (@outline = 'true')
|
||||||
|
|
||||||
">
|
">
|
||||||
<emph rend = "paragraph-emph">
|
<emph rend = "paragraph-emph">
|
||||||
<xsl:apply-templates/>
|
<xsl:apply-templates/>
|
||||||
@ -263,12 +262,23 @@
|
|||||||
</xsl:if>
|
</xsl:if>
|
||||||
<xsl:if test="@line-spacing">
|
<xsl:if test="@line-spacing">
|
||||||
<xsl:text>line-height:</xsl:text>
|
<xsl:text>line-height:</xsl:text>
|
||||||
<xsl:value-of select="@line-height"/>
|
<xsl:value-of select="@line-spacing"/>
|
||||||
<xsl:text>pt;</xsl:text>
|
<xsl:text>pt;</xsl:text>
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
|
<xsl:if test="(@align = 'just')">
|
||||||
|
<xsl:text>text-align: justify;</xsl:text>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:if test="(@align = 'cent')">
|
||||||
|
<xsl:text>text-align: center;</xsl:text>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:if test="(@align = 'left')">
|
||||||
|
<xsl:text>text-align: left;</xsl:text>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:if test="(@align = 'right')">
|
||||||
|
<xsl:text>text-align: right;</xsl:text>
|
||||||
|
</xsl:if>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
|
|
||||||
<xsl:template match="rtf:inline">
|
<xsl:template match="rtf:inline">
|
||||||
<xsl:variable name="num-attrs" select="count(@*)"/>
|
<xsl:variable name="num-attrs" select="count(@*)"/>
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
@ -277,6 +287,26 @@
|
|||||||
<xsl:value-of select="count(preceding::rtf:footnote) + 1"/>
|
<xsl:value-of select="count(preceding::rtf:footnote) + 1"/>
|
||||||
<xsl:text>]</xsl:text>
|
<xsl:text>]</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
<xsl:when test="(@superscript = 'true')">
|
||||||
|
<xsl:element name="sup">
|
||||||
|
<xsl:element name="span">
|
||||||
|
<xsl:attribute name="class">
|
||||||
|
<c:inline-class/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</xsl:element>
|
||||||
|
</xsl:element>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:when test="(@underscript = 'true')">
|
||||||
|
<xsl:element name="sub">
|
||||||
|
<xsl:element name="span">
|
||||||
|
<xsl:attribute name="class">
|
||||||
|
<c:inline-class/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</xsl:element>
|
||||||
|
</xsl:element>
|
||||||
|
</xsl:when>
|
||||||
<xsl:otherwise>
|
<xsl:otherwise>
|
||||||
<xsl:element name="span">
|
<xsl:element name="span">
|
||||||
<xsl:attribute name="class">
|
<xsl:attribute name="class">
|
||||||
@ -382,6 +412,10 @@
|
|||||||
<xsl:attribute name="style">page-break-after:always</xsl:attribute>
|
<xsl:attribute name="style">page-break-after:always</xsl:attribute>
|
||||||
</xsl:element>
|
</xsl:element>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
|
<xsl:template match="rtf:hardline-break">
|
||||||
|
<xsl:element name="br"/>
|
||||||
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="rtf:rtf-definition|rtf:font-table|rtf:color-table|rtf:style-table|rtf:page-definition|rtf:list-table|rtf:override-table|rtf:override-list|rtf:list-text"/>
|
<xsl:template match="rtf:rtf-definition|rtf:font-table|rtf:color-table|rtf:style-table|rtf:page-definition|rtf:list-table|rtf:override-table|rtf:override-list|rtf:list-text"/>
|
||||||
|
|
||||||
|
@ -115,7 +115,6 @@ if iswindows:
|
|||||||
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR', sw_lib_dir)
|
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR', sw_lib_dir)
|
||||||
popplerqt4_lib_dirs = poppler_lib_dirs
|
popplerqt4_lib_dirs = poppler_lib_dirs
|
||||||
poppler_libs = ['poppler']
|
poppler_libs = ['poppler']
|
||||||
popplerqt4_libs = poppler_libs + ['QtCore4', 'QtGui4']
|
|
||||||
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.5.6')]
|
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.5.6')]
|
||||||
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
|
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
|
||||||
magick_libs = ['CORE_RL_wand_', 'CORE_RL_magick_']
|
magick_libs = ['CORE_RL_wand_', 'CORE_RL_magick_']
|
||||||
@ -129,8 +128,8 @@ elif isosx:
|
|||||||
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
|
popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
|
||||||
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
|
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
|
||||||
'/sw/lib')
|
'/sw/lib')
|
||||||
|
poppler_libs = ['poppler']
|
||||||
popplerqt4_lib_dirs = poppler_lib_dirs
|
popplerqt4_lib_dirs = poppler_lib_dirs
|
||||||
poppler_libs = popplerqt4_libs = ['poppler']
|
|
||||||
podofo_inc = '/sw/podofo'
|
podofo_inc = '/sw/podofo'
|
||||||
podofo_lib = '/sw/lib'
|
podofo_lib = '/sw/lib'
|
||||||
magick_inc_dirs = consolidate('MAGICK_INC',
|
magick_inc_dirs = consolidate('MAGICK_INC',
|
||||||
@ -162,9 +161,6 @@ else:
|
|||||||
poppler_libs = pkgconfig_libs('poppler', '', '')
|
poppler_libs = pkgconfig_libs('poppler', '', '')
|
||||||
if not poppler_libs:
|
if not poppler_libs:
|
||||||
poppler_libs = ['poppler']
|
poppler_libs = ['poppler']
|
||||||
popplerqt4_libs = pkgconfig_libs('poppler-qt4', '', '')
|
|
||||||
if not popplerqt4_libs:
|
|
||||||
popplerqt4_libs = ['poppler-qt4', 'poppler']
|
|
||||||
magick_libs = pkgconfig_libs('MagickWand', '', '')
|
magick_libs = pkgconfig_libs('MagickWand', '', '')
|
||||||
if not magick_libs:
|
if not magick_libs:
|
||||||
magick_libs = ['MagickWand', 'MagickCore']
|
magick_libs = ['MagickWand', 'MagickCore']
|
||||||
|
@ -72,6 +72,13 @@ extensions = [
|
|||||||
lib_dirs=chmlib_lib_dirs,
|
lib_dirs=chmlib_lib_dirs,
|
||||||
cflags=["-D__PYTHON__"]),
|
cflags=["-D__PYTHON__"]),
|
||||||
|
|
||||||
|
Extension('magick',
|
||||||
|
['calibre/utils/magick/magick.c'],
|
||||||
|
headers=['calibre/utils/magick/magick_constants.h'],
|
||||||
|
libraries=magick_libs,
|
||||||
|
lib_dirs=magick_lib_dirs,
|
||||||
|
inc_dirs=magick_inc_dirs
|
||||||
|
),
|
||||||
|
|
||||||
Extension('pdfreflow',
|
Extension('pdfreflow',
|
||||||
reflow_sources,
|
reflow_sources,
|
||||||
|
@ -40,6 +40,7 @@ class LinuxFreeze(Command):
|
|||||||
'/usr/bin/pdftohtml',
|
'/usr/bin/pdftohtml',
|
||||||
'/usr/lib/libwmflite-0.2.so.7',
|
'/usr/lib/libwmflite-0.2.so.7',
|
||||||
'/usr/lib/liblcms.so.1',
|
'/usr/lib/liblcms.so.1',
|
||||||
|
'/usr/lib/liblcms2.so.2',
|
||||||
'/usr/lib/libstlport.so.5.1',
|
'/usr/lib/libstlport.so.5.1',
|
||||||
'/tmp/calibre-mount-helper',
|
'/tmp/calibre-mount-helper',
|
||||||
'/usr/lib/libunrar.so',
|
'/usr/lib/libunrar.so',
|
||||||
@ -50,10 +51,9 @@ class LinuxFreeze(Command):
|
|||||||
'/usr/lib/libpodofo.so.0.8.1',
|
'/usr/lib/libpodofo.so.0.8.1',
|
||||||
'/lib/libz.so.1',
|
'/lib/libz.so.1',
|
||||||
'/lib/libuuid.so.1',
|
'/lib/libuuid.so.1',
|
||||||
'/usr/lib/libtiff.so.3',
|
'/usr/lib/libtiff.so.5',
|
||||||
'/lib/libbz2.so.1',
|
'/lib/libbz2.so.1',
|
||||||
'/usr/lib/libpoppler.so.5',
|
'/usr/lib/libpoppler.so.6',
|
||||||
'/usr/lib/libpoppler-qt4.so.3',
|
|
||||||
'/usr/lib/libxml2.so.2',
|
'/usr/lib/libxml2.so.2',
|
||||||
'/usr/lib/libopenjpeg.so.2',
|
'/usr/lib/libopenjpeg.so.2',
|
||||||
'/usr/lib/libxslt.so.1',
|
'/usr/lib/libxslt.so.1',
|
||||||
@ -62,10 +62,10 @@ class LinuxFreeze(Command):
|
|||||||
'/usr/lib/libgthread-2.0.so.0',
|
'/usr/lib/libgthread-2.0.so.0',
|
||||||
stdcpp,
|
stdcpp,
|
||||||
ffi,
|
ffi,
|
||||||
'/usr/lib/libpng12.so.0',
|
'/usr/lib/libpng14.so.14',
|
||||||
'/usr/lib/libexslt.so.0',
|
'/usr/lib/libexslt.so.0',
|
||||||
'/usr/lib/libMagickWand.so.2',
|
'/usr/lib/libMagickWand.so.3',
|
||||||
'/usr/lib/libMagickCore.so.2',
|
'/usr/lib/libMagickCore.so.3',
|
||||||
'/usr/lib/libgcrypt.so.11',
|
'/usr/lib/libgcrypt.so.11',
|
||||||
'/usr/lib/libgpg-error.so.0',
|
'/usr/lib/libgpg-error.so.0',
|
||||||
'/usr/lib/libphonon.so.4',
|
'/usr/lib/libphonon.so.4',
|
||||||
|
@ -13,7 +13,7 @@ from setup import Command, modules, functions, basenames, __version__, \
|
|||||||
from setup.build_environment import msvc, MT, RC
|
from setup.build_environment import msvc, MT, RC
|
||||||
from setup.installer.windows.wix import WixMixIn
|
from setup.installer.windows.wix import WixMixIn
|
||||||
|
|
||||||
QT_DIR = 'C:\\Qt\\4.6.0'
|
QT_DIR = 'C:\\Qt\\4.6.3'
|
||||||
QT_DLLS = ['Core', 'Gui', 'Network', 'Svg', 'WebKit', 'Xml', 'XmlPatterns']
|
QT_DLLS = ['Core', 'Gui', 'Network', 'Svg', 'WebKit', 'Xml', 'XmlPatterns']
|
||||||
LIBUSB_DIR = 'C:\\libusb'
|
LIBUSB_DIR = 'C:\\libusb'
|
||||||
LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll'
|
LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll'
|
||||||
|
@ -1,17 +1,85 @@
|
|||||||
Notes on setting up the windows development environment
|
Notes on setting up the windows development environment
|
||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
Set CMAKE_PREFIX_PATH to C:\cygwin\home\kovid\sw
|
Overview
|
||||||
|
----------
|
||||||
|
|
||||||
|
calibre and all its dependencies are compiled using Visual Studio 2008 express edition (free from MS). All the following instructions must be run in a visual studio command prompt unless otherwise noted.
|
||||||
|
|
||||||
|
calibre contains build script to automate the building of the calibre installer. These scripts make certain assumptions about where dependencies are installed. Your best best is to setup a VM and replicate the paths mentioned below exactly.
|
||||||
|
|
||||||
|
Basic dependencies
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Install cygwin and setup sshd (optional). Used to enable automation of the calibre build VM from linux, not needed if you are building manually.
|
||||||
|
|
||||||
|
Install MS Visual Studio 2008, cmake, python and WiX.
|
||||||
|
|
||||||
|
Set CMAKE_PREFIX_PATH environment variable to C:\cygwin\home\kovid\sw
|
||||||
|
|
||||||
|
This is where all dependencies will be installed.
|
||||||
|
|
||||||
|
Add C:\Python26\Scripts and C:\Python26 to PATH
|
||||||
|
|
||||||
|
Install setuptools from http://pypi.python.org/pypi/setuptools
|
||||||
|
If there are no windows binaries already compiled for the version of python you are using then download the source and run the following command in the folder where the source has been unpacked::
|
||||||
|
|
||||||
|
python setup.py install
|
||||||
|
|
||||||
|
Run the following command to install python dependencies::
|
||||||
|
|
||||||
|
easy_install --always-unzip -U ipython mechanize BeautifulSoup pyreadline python-dateutil dnspython
|
||||||
|
|
||||||
|
Qt
|
||||||
|
--------
|
||||||
|
|
||||||
|
Extract Qt sourcecode to C:\Qt\4.x.x. Run configure and make::
|
||||||
|
|
||||||
|
configure -opensource -qt-zlib -qt-gif -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc -no-qt3support -webkit -xmlpatterns -no-phonon
|
||||||
|
nmake
|
||||||
|
|
||||||
|
SIP
|
||||||
|
-----
|
||||||
|
|
||||||
|
Available from: http://www.riverbankcomputing.co.uk/software/sip/download ::
|
||||||
|
|
||||||
|
python configure.py -p win32-msvc2008
|
||||||
|
nmake
|
||||||
|
nmake install
|
||||||
|
|
||||||
|
PyQt4
|
||||||
|
----------
|
||||||
|
|
||||||
|
Compiling instructions::
|
||||||
|
|
||||||
|
python configure.py -c -j5 -e QtCore -e QtGui -e QtSvg -e QtNetwork -e QtWebKit -e QtXmlPatterns --verbose
|
||||||
|
nmake
|
||||||
|
nmake install
|
||||||
|
|
||||||
|
Python Imaging Library
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Install as normal using provided installer.
|
||||||
|
|
||||||
|
Libunrar
|
||||||
|
----------
|
||||||
|
|
||||||
|
http://www.rarlab.com/rar/UnRARDLL.exe install and add C:\Program Files\UnrarDLL to PATH
|
||||||
|
|
||||||
|
lxml
|
||||||
|
------
|
||||||
|
|
||||||
|
http://pypi.python.org/pypi/lxml
|
||||||
|
|
||||||
jpeg-7
|
jpeg-7
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Copy
|
Copy::
|
||||||
jconfig.vc to jconfig.h, makejsln.vc9 to jpeg.sln,
|
jconfig.vc to jconfig.h, makejsln.vc9 to jpeg.sln,
|
||||||
makeasln.vc9 to apps.sln, makejvcp.vc9 to jpeg.vcproj,
|
makeasln.vc9 to apps.sln, makejvcp.vc9 to jpeg.vcproj,
|
||||||
makecvcp.vc9 to cjpeg.vcproj, makedvcp.vc9 to djpeg.vcproj,
|
makecvcp.vc9 to cjpeg.vcproj, makedvcp.vc9 to djpeg.vcproj,
|
||||||
maketvcp.vc9 to jpegtran.vcproj, makervcp.vc9 to rdjpgcom.vcproj, and
|
maketvcp.vc9 to jpegtran.vcproj, makervcp.vc9 to rdjpgcom.vcproj, and
|
||||||
makewvcp.vc9 to wrjpgcom.vcproj. (Note that the renaming is critical!)
|
makewvcp.vc9 to wrjpgcom.vcproj. (Note that the renaming is critical!)
|
||||||
|
|
||||||
Load jpeg.sln in Visual Studio
|
Load jpeg.sln in Visual Studio
|
||||||
|
|
||||||
@ -169,7 +237,7 @@ cp build/podofo/build/src/Release/podofo.exp lib/
|
|||||||
cp build/podofo/build/podofo_config.h include/podofo/
|
cp build/podofo/build/podofo_config.h include/podofo/
|
||||||
cp -r build/podofo/src/* include/podofo/
|
cp -r build/podofo/src/* include/podofo/
|
||||||
|
|
||||||
The following patch was required to get it to compile:
|
The following patch (against 0.8.1) was required to get it to compile:
|
||||||
|
|
||||||
Index: src/PdfImage.cpp
|
Index: src/PdfImage.cpp
|
||||||
===================================================================
|
===================================================================
|
||||||
@ -214,7 +282,7 @@ Edit VisualMagick/configure/configure.cpp to set
|
|||||||
|
|
||||||
int projectType = MULTITHREADEDDLL;
|
int projectType = MULTITHREADEDDLL;
|
||||||
|
|
||||||
Run configure.bat ina visual studio command prompt
|
Run configure.bat in a visual studio command prompt
|
||||||
|
|
||||||
Edit magick/magick-config.h
|
Edit magick/magick-config.h
|
||||||
|
|
||||||
@ -222,3 +290,19 @@ Undefine ProvideDllMain and MAGICKCORE_X11_DELEGATE
|
|||||||
|
|
||||||
Now open VisualMagick/VisualDynamicMT.sln set to Release
|
Now open VisualMagick/VisualDynamicMT.sln set to Release
|
||||||
Remove the CORE_xlib project
|
Remove the CORE_xlib project
|
||||||
|
|
||||||
|
calibre
|
||||||
|
---------
|
||||||
|
|
||||||
|
Take a linux calibre tree on which you have run the following command::
|
||||||
|
|
||||||
|
python setup.py stage1
|
||||||
|
|
||||||
|
and copy it to windows.
|
||||||
|
|
||||||
|
Run::
|
||||||
|
|
||||||
|
python setup.py build
|
||||||
|
python setup.py win32_freeze
|
||||||
|
|
||||||
|
This will create the .msi in the dist directory.
|
||||||
|
@ -73,11 +73,11 @@ class Manual(Command):
|
|||||||
os.makedirs(d)
|
os.makedirs(d)
|
||||||
if not os.path.exists('.build'+os.sep+'html'):
|
if not os.path.exists('.build'+os.sep+'html'):
|
||||||
os.makedirs('.build'+os.sep+'html')
|
os.makedirs('.build'+os.sep+'html')
|
||||||
os.environ['__appname__']= __appname__
|
os.environ['__appname__'] = __appname__
|
||||||
os.environ['__version__']= __version__
|
os.environ['__version__'] = __version__
|
||||||
subprocess.check_call(['sphinx-build', '-b', 'custom', '-t', 'online',
|
subprocess.check_call(['sphinx-build', '-b', 'html', '-t', 'online',
|
||||||
'-d', '.build/doctrees', '.', '.build/html'])
|
'-d', '.build/doctrees', '.', '.build/html'])
|
||||||
subprocess.check_call(['sphinx-build', '-b', 'epub', '-d',
|
subprocess.check_call(['sphinx-build', '-b', 'myepub', '-d',
|
||||||
'.build/doctrees', '.', '.build/epub'])
|
'.build/doctrees', '.', '.build/epub'])
|
||||||
shutil.copyfile(self.j('.build', 'epub', 'calibre.epub'), self.j('.build',
|
shutil.copyfile(self.j('.build', 'epub', 'calibre.epub'), self.j('.build',
|
||||||
'html', 'calibre.epub'))
|
'html', 'calibre.epub'))
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import sys, os, re, logging, time, mimetypes, \
|
import sys, os, re, logging, time, mimetypes, \
|
||||||
__builtin__, warnings, multiprocessing
|
__builtin__, warnings, multiprocessing
|
||||||
from urllib import getproxies
|
from urllib import getproxies
|
||||||
@ -13,12 +14,13 @@ from functools import partial
|
|||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
warnings.simplefilter('ignore', DeprecationWarning)
|
||||||
|
|
||||||
|
|
||||||
from calibre.startup import plugins, winutil, winutilerror
|
|
||||||
from calibre.constants import iswindows, isosx, islinux, isfreebsd, isfrozen, \
|
from calibre.constants import iswindows, isosx, islinux, isfreebsd, isfrozen, \
|
||||||
terminal_controller, preferred_encoding, \
|
terminal_controller, preferred_encoding, \
|
||||||
__appname__, __version__, __author__, \
|
__appname__, __version__, __author__, \
|
||||||
win32event, win32api, winerror, fcntl, \
|
win32event, win32api, winerror, fcntl, \
|
||||||
filesystem_encoding
|
filesystem_encoding, plugins, config_dir
|
||||||
|
from calibre.startup import winutil, winutilerror
|
||||||
|
|
||||||
import mechanize
|
import mechanize
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
@ -361,6 +363,8 @@ def strftime(fmt, t=None):
|
|||||||
before 1900 '''
|
before 1900 '''
|
||||||
if t is None:
|
if t is None:
|
||||||
t = time.localtime()
|
t = time.localtime()
|
||||||
|
if hasattr(t, 'timetuple'):
|
||||||
|
t = t.timetuple()
|
||||||
early_year = t[0] < 1900
|
early_year = t[0] < 1900
|
||||||
if early_year:
|
if early_year:
|
||||||
replacement = 1900 if t[0]%4 == 0 else 1901
|
replacement = 1900 if t[0]%4 == 0 else 1901
|
||||||
@ -409,15 +413,13 @@ def entity_to_unicode(match, exceptions=[], encoding='cp1252',
|
|||||||
return check("'")
|
return check("'")
|
||||||
if ent == 'hellips':
|
if ent == 'hellips':
|
||||||
ent = 'hellip'
|
ent = 'hellip'
|
||||||
if ent.lower().startswith(u'#x'):
|
if ent.startswith('#'):
|
||||||
num = int(ent[2:], 16)
|
|
||||||
if encoding is None or num > 255:
|
|
||||||
return check(my_unichr(num))
|
|
||||||
return check(chr(num).decode(encoding))
|
|
||||||
if ent.startswith(u'#'):
|
|
||||||
try:
|
try:
|
||||||
num = int(ent[1:])
|
if ent[1] in ('x', 'X'):
|
||||||
except ValueError:
|
num = int(ent[2:], 16)
|
||||||
|
else:
|
||||||
|
num = int(ent[1:])
|
||||||
|
except:
|
||||||
return '&'+ent+';'
|
return '&'+ent+';'
|
||||||
if encoding is None or num > 255:
|
if encoding is None or num > 255:
|
||||||
return check(my_unichr(num))
|
return check(my_unichr(num))
|
||||||
@ -438,6 +440,9 @@ xml_entity_to_unicode = partial(entity_to_unicode, result_exceptions = {
|
|||||||
'>' : '>',
|
'>' : '>',
|
||||||
'&' : '&'})
|
'&' : '&'})
|
||||||
|
|
||||||
|
def replace_entities(raw):
|
||||||
|
return _ent_pat.sub(entity_to_unicode, raw)
|
||||||
|
|
||||||
def prepare_string_for_xml(raw, attribute=False):
|
def prepare_string_for_xml(raw, attribute=False):
|
||||||
raw = _ent_pat.sub(entity_to_unicode, raw)
|
raw = _ent_pat.sub(entity_to_unicode, raw)
|
||||||
raw = raw.replace('&', '&').replace('<', '<').replace('>', '>')
|
raw = raw.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||||
@ -481,7 +486,6 @@ def ipython(user_ns=None):
|
|||||||
sys.argv = ['ipython']
|
sys.argv = ['ipython']
|
||||||
if user_ns is None:
|
if user_ns is None:
|
||||||
user_ns = locals()
|
user_ns = locals()
|
||||||
from calibre.utils.config import config_dir
|
|
||||||
ipydir = os.path.join(config_dir, ('_' if iswindows else '.')+'ipython')
|
ipydir = os.path.join(config_dir, ('_' if iswindows else '.')+'ipython')
|
||||||
os.environ['IPYTHONDIR'] = ipydir
|
os.environ['IPYTHONDIR'] = ipydir
|
||||||
if not os.path.exists(ipydir):
|
if not os.path.exists(ipydir):
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.7.8'
|
__version__ = '0.7.13'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
@ -14,7 +14,7 @@ numeric_version = tuple(_ver)
|
|||||||
Various run time constants.
|
Various run time constants.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import sys, locale, codecs
|
import sys, locale, codecs, os
|
||||||
from calibre.utils.terminfo import TerminalController
|
from calibre.utils.terminfo import TerminalController
|
||||||
|
|
||||||
terminal_controller = TerminalController(sys.stdout)
|
terminal_controller = TerminalController(sys.stdout)
|
||||||
@ -47,7 +47,7 @@ def debug():
|
|||||||
global DEBUG
|
global DEBUG
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
################################################################################
|
# plugins {{{
|
||||||
plugins = None
|
plugins = None
|
||||||
if plugins is None:
|
if plugins is None:
|
||||||
# Load plugins
|
# Load plugins
|
||||||
@ -60,6 +60,7 @@ if plugins is None:
|
|||||||
'pictureflow',
|
'pictureflow',
|
||||||
'lzx',
|
'lzx',
|
||||||
'msdes',
|
'msdes',
|
||||||
|
'magick',
|
||||||
'podofo',
|
'podofo',
|
||||||
'cPalmdoc',
|
'cPalmdoc',
|
||||||
'fontconfig',
|
'fontconfig',
|
||||||
@ -80,3 +81,22 @@ if plugins is None:
|
|||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
plugins = load_plugins()
|
plugins = load_plugins()
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# config_dir {{{
|
||||||
|
if os.environ.has_key('CALIBRE_CONFIG_DIRECTORY'):
|
||||||
|
config_dir = os.path.abspath(os.environ['CALIBRE_CONFIG_DIRECTORY'])
|
||||||
|
elif iswindows:
|
||||||
|
if plugins['winutil'][0] is None:
|
||||||
|
raise Exception(plugins['winutil'][1])
|
||||||
|
config_dir = plugins['winutil'][0].special_folder_path(plugins['winutil'][0].CSIDL_APPDATA)
|
||||||
|
if not os.access(config_dir, os.W_OK|os.X_OK):
|
||||||
|
config_dir = os.path.expanduser('~')
|
||||||
|
config_dir = os.path.join(config_dir, 'calibre')
|
||||||
|
elif isosx:
|
||||||
|
config_dir = os.path.expanduser('~/Library/Preferences/calibre')
|
||||||
|
else:
|
||||||
|
bdir = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME', '~/.config')))
|
||||||
|
config_dir = os.path.join(bdir, 'calibre')
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
@ -262,31 +262,21 @@ class CatalogPlugin(Plugin):
|
|||||||
|
|
||||||
type = _('Catalog generator')
|
type = _('Catalog generator')
|
||||||
|
|
||||||
#: CLI parser options specific to this plugin, declared as namedtuple Option
|
#: CLI parser options specific to this plugin, declared as namedtuple Option::
|
||||||
#:
|
#:
|
||||||
#: from collections import namedtuple
|
#: from collections import namedtuple
|
||||||
#: Option = namedtuple('Option', 'option, default, dest, help')
|
#: Option = namedtuple('Option', 'option, default, dest, help')
|
||||||
#: cli_options = [Option('--catalog-title',
|
#: cli_options = [Option('--catalog-title',
|
||||||
#: default = 'My Catalog',
|
#: default = 'My Catalog',
|
||||||
#: dest = 'catalog_title',
|
#: dest = 'catalog_title',
|
||||||
#: help = (_('Title of generated catalog. \nDefault:') + " '" +
|
#: help = (_('Title of generated catalog. \nDefault:') + " '" +
|
||||||
#: '%default' + "'"))]
|
#: '%default' + "'"))]
|
||||||
#: cli_options parsed in library.cli:catalog_option_parser()
|
#: cli_options parsed in library.cli:catalog_option_parser()
|
||||||
|
|
||||||
cli_options = []
|
cli_options = []
|
||||||
|
|
||||||
|
|
||||||
def search_sort_db(self, db, opts):
|
def search_sort_db(self, db, opts):
|
||||||
|
|
||||||
'''
|
|
||||||
# Don't add Catalogs to the generated Catalogs
|
|
||||||
cat = _('Catalog')
|
|
||||||
if opts.search_text:
|
|
||||||
opts.search_text += ' not tag:'+cat
|
|
||||||
else:
|
|
||||||
opts.search_text = 'not tag:'+cat
|
|
||||||
'''
|
|
||||||
|
|
||||||
db.search(opts.search_text)
|
db.search(opts.search_text)
|
||||||
|
|
||||||
if opts.sort_by:
|
if opts.sort_by:
|
||||||
@ -349,8 +339,7 @@ class CatalogPlugin(Plugin):
|
|||||||
It should generate the catalog in the format specified
|
It should generate the catalog in the format specified
|
||||||
in file_types, returning the absolute path to the
|
in file_types, returning the absolute path to the
|
||||||
generated catalog file. If an error is encountered
|
generated catalog file. If an error is encountered
|
||||||
it should raise an Exception and return None. The default
|
it should raise an Exception.
|
||||||
implementation simply returns None.
|
|
||||||
|
|
||||||
The generated catalog file should be created with the
|
The generated catalog file should be created with the
|
||||||
:meth:`temporary_file` method.
|
:meth:`temporary_file` method.
|
||||||
@ -358,9 +347,6 @@ class CatalogPlugin(Plugin):
|
|||||||
:param path_to_output: Absolute path to the generated catalog file.
|
:param path_to_output: Absolute path to the generated catalog file.
|
||||||
:param opts: A dictionary of keyword arguments
|
:param opts: A dictionary of keyword arguments
|
||||||
:param db: A LibraryDatabase2 object
|
:param db: A LibraryDatabase2 object
|
||||||
|
|
||||||
:return: None
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# Default implementation does nothing
|
# Default implementation does nothing
|
||||||
raise NotImplementedError('CatalogPlugin.generate_catalog() default '
|
raise NotImplementedError('CatalogPlugin.generate_catalog() default '
|
||||||
|
@ -446,7 +446,7 @@ from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \
|
|||||||
BOOQ, ELONEX, POCKETBOOK301, MENTOR
|
BOOQ, ELONEX, POCKETBOOK301, MENTOR
|
||||||
from calibre.devices.iliad.driver import ILIAD
|
from calibre.devices.iliad.driver import ILIAD
|
||||||
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
||||||
from calibre.devices.jetbook.driver import JETBOOK
|
from calibre.devices.jetbook.driver import JETBOOK, MIBUK
|
||||||
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
||||||
from calibre.devices.nook.driver import NOOK
|
from calibre.devices.nook.driver import NOOK
|
||||||
from calibre.devices.prs505.driver import PRS505
|
from calibre.devices.prs505.driver import PRS505
|
||||||
@ -467,12 +467,15 @@ from calibre.devices.kobo.driver import KOBO
|
|||||||
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon, \
|
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon, \
|
||||||
LibraryThing
|
LibraryThing
|
||||||
from calibre.ebooks.metadata.douban import DoubanBooks
|
from calibre.ebooks.metadata.douban import DoubanBooks
|
||||||
from calibre.library.catalog import CSV_XML, EPUB_MOBI
|
from calibre.ebooks.metadata.covers import OpenLibraryCovers, \
|
||||||
|
LibraryThingCovers
|
||||||
|
from calibre.library.catalog import CSV_XML, EPUB_MOBI, BIBTEX
|
||||||
from calibre.ebooks.epub.fix.unmanifested import Unmanifested
|
from calibre.ebooks.epub.fix.unmanifested import Unmanifested
|
||||||
from calibre.ebooks.epub.fix.epubcheck import Epubcheck
|
from calibre.ebooks.epub.fix.epubcheck import Epubcheck
|
||||||
|
|
||||||
plugins = [HTML2ZIP, PML2PMLZ, ArchiveExtract, GoogleBooks, ISBNDB, Amazon,
|
plugins = [HTML2ZIP, PML2PMLZ, ArchiveExtract, GoogleBooks, ISBNDB, Amazon,
|
||||||
LibraryThing, DoubanBooks, CSV_XML, EPUB_MOBI, Unmanifested, Epubcheck]
|
LibraryThing, DoubanBooks, CSV_XML, EPUB_MOBI, BIBTEX, Unmanifested,
|
||||||
|
Epubcheck, OpenLibraryCovers, LibraryThingCovers]
|
||||||
plugins += [
|
plugins += [
|
||||||
ComicInput,
|
ComicInput,
|
||||||
EPUBInput,
|
EPUBInput,
|
||||||
@ -517,6 +520,7 @@ plugins += [
|
|||||||
IREXDR1000,
|
IREXDR1000,
|
||||||
IREXDR800,
|
IREXDR800,
|
||||||
JETBOOK,
|
JETBOOK,
|
||||||
|
MIBUK,
|
||||||
SHINEBOOK,
|
SHINEBOOK,
|
||||||
POCKETBOOK360,
|
POCKETBOOK360,
|
||||||
POCKETBOOK301,
|
POCKETBOOK301,
|
||||||
|
@ -28,7 +28,7 @@ class ConversionOption(object):
|
|||||||
|
|
||||||
def validate_parameters(self):
|
def validate_parameters(self):
|
||||||
'''
|
'''
|
||||||
Validate the parameters passed to :method:`__init__`.
|
Validate the parameters passed to :meth:`__init__`.
|
||||||
'''
|
'''
|
||||||
if re.match(r'[a-zA-Z_]([a-zA-Z0-9_])*', self.name) is None:
|
if re.match(r'[a-zA-Z_]([a-zA-Z0-9_])*', self.name) is None:
|
||||||
raise ValueError(self.name + ' is not a valid Python identifier')
|
raise ValueError(self.name + ' is not a valid Python identifier')
|
||||||
@ -96,7 +96,7 @@ class InputFormatPlugin(Plugin):
|
|||||||
InputFormatPlugins are responsible for converting a document into
|
InputFormatPlugins are responsible for converting a document into
|
||||||
HTML+OPF+CSS+etc.
|
HTML+OPF+CSS+etc.
|
||||||
The results of the conversion *must* be encoded in UTF-8.
|
The results of the conversion *must* be encoded in UTF-8.
|
||||||
The main action happens in :method:`convert`.
|
The main action happens in :meth:`convert`.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
type = _('Conversion Input')
|
type = _('Conversion Input')
|
||||||
@ -109,7 +109,7 @@ class InputFormatPlugin(Plugin):
|
|||||||
|
|
||||||
#: If True, this input plugin generates a collection of images,
|
#: If True, this input plugin generates a collection of images,
|
||||||
#: one per HTML file. You can obtain access to the images via
|
#: one per HTML file. You can obtain access to the images via
|
||||||
#: convenience method, :method:`get_image_collection`.
|
#: convenience method, :meth:`get_image_collection`.
|
||||||
is_image_collection = False
|
is_image_collection = False
|
||||||
|
|
||||||
#: If set to True, the input plugin will perform special processing
|
#: If set to True, the input plugin will perform special processing
|
||||||
@ -117,7 +117,7 @@ class InputFormatPlugin(Plugin):
|
|||||||
for_viewer = False
|
for_viewer = False
|
||||||
|
|
||||||
#: Options shared by all Input format plugins. Do not override
|
#: Options shared by all Input format plugins. Do not override
|
||||||
#: in sub-classes. Use :member:`options` instead. Every option must be an
|
#: in sub-classes. Use :attr:`options` instead. Every option must be an
|
||||||
#: instance of :class:`OptionRecommendation`.
|
#: instance of :class:`OptionRecommendation`.
|
||||||
common_options = set([
|
common_options = set([
|
||||||
OptionRecommendation(name='input_encoding',
|
OptionRecommendation(name='input_encoding',
|
||||||
@ -173,7 +173,6 @@ class InputFormatPlugin(Plugin):
|
|||||||
returns.
|
returns.
|
||||||
|
|
||||||
:param stream: A file like object that contains the input file.
|
:param stream: A file like object that contains the input file.
|
||||||
|
|
||||||
:param options: Options to customize the conversion process.
|
:param options: Options to customize the conversion process.
|
||||||
Guaranteed to have attributes corresponding
|
Guaranteed to have attributes corresponding
|
||||||
to all the options declared by this plugin. In
|
to all the options declared by this plugin. In
|
||||||
@ -182,14 +181,11 @@ class InputFormatPlugin(Plugin):
|
|||||||
mean be more verbose. Another useful attribute is
|
mean be more verbose. Another useful attribute is
|
||||||
``input_profile`` that is an instance of
|
``input_profile`` that is an instance of
|
||||||
:class:`calibre.customize.profiles.InputProfile`.
|
:class:`calibre.customize.profiles.InputProfile`.
|
||||||
|
|
||||||
:param file_ext: The extension (without the .) of the input file. It
|
:param file_ext: The extension (without the .) of the input file. It
|
||||||
is guaranteed to be one of the `file_types` supported
|
is guaranteed to be one of the `file_types` supported
|
||||||
by this plugin.
|
by this plugin.
|
||||||
|
|
||||||
:param log: A :class:`calibre.utils.logging.Log` object. All output
|
:param log: A :class:`calibre.utils.logging.Log` object. All output
|
||||||
should use this object.
|
should use this object.
|
||||||
|
|
||||||
:param accelarators: A dictionary of various information that the input
|
:param accelarators: A dictionary of various information that the input
|
||||||
plugin can get easily that would speed up the
|
plugin can get easily that would speed up the
|
||||||
subsequent stages of the conversion.
|
subsequent stages of the conversion.
|
||||||
@ -235,7 +231,7 @@ class OutputFormatPlugin(Plugin):
|
|||||||
(OPF+HTML) into an output ebook.
|
(OPF+HTML) into an output ebook.
|
||||||
|
|
||||||
The OEB document can be assumed to be encoded in UTF-8.
|
The OEB document can be assumed to be encoded in UTF-8.
|
||||||
The main action happens in :method:`convert`.
|
The main action happens in :meth:`convert`.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
type = _('Conversion Output')
|
type = _('Conversion Output')
|
||||||
@ -247,7 +243,7 @@ class OutputFormatPlugin(Plugin):
|
|||||||
file_type = None
|
file_type = None
|
||||||
|
|
||||||
#: Options shared by all Input format plugins. Do not override
|
#: Options shared by all Input format plugins. Do not override
|
||||||
#: in sub-classes. Use :member:`options` instead. Every option must be an
|
#: in sub-classes. Use :attr:`options` instead. Every option must be an
|
||||||
#: instance of :class:`OptionRecommendation`.
|
#: instance of :class:`OptionRecommendation`.
|
||||||
common_options = set([
|
common_options = set([
|
||||||
OptionRecommendation(name='pretty_print',
|
OptionRecommendation(name='pretty_print',
|
||||||
@ -277,17 +273,15 @@ class OutputFormatPlugin(Plugin):
|
|||||||
:class:`calibre.ebooks.oeb.OEBBook` to the file specified by output.
|
:class:`calibre.ebooks.oeb.OEBBook` to the file specified by output.
|
||||||
|
|
||||||
:param output: Either a file like object or a string. If it is a string
|
:param output: Either a file like object or a string. If it is a string
|
||||||
it is the path to a directory that may or may not exist. The output
|
it is the path to a directory that may or may not exist. The output
|
||||||
plugin should write its output into that directory. If it is a file like
|
plugin should write its output into that directory. If it is a file like
|
||||||
object, the output plugin should write its output into the file.
|
object, the output plugin should write its output into the file.
|
||||||
|
|
||||||
:param input_plugin: The input plugin that was used at the beginning of
|
:param input_plugin: The input plugin that was used at the beginning of
|
||||||
the conversion pipeline.
|
the conversion pipeline.
|
||||||
|
|
||||||
:param opts: Conversion options. Guaranteed to have attributes
|
:param opts: Conversion options. Guaranteed to have attributes
|
||||||
corresponding to the OptionRecommendations of this plugin.
|
corresponding to the OptionRecommendations of this plugin.
|
||||||
|
|
||||||
:param log: The logger. Print debug/info messages etc. using this.
|
:param log: The logger. Print debug/info messages etc. using this.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@ -233,18 +233,20 @@ class OutputProfile(Plugin):
|
|||||||
'if you want to produce a document intended to be read at a '
|
'if you want to produce a document intended to be read at a '
|
||||||
'computer or on a range of devices.')
|
'computer or on a range of devices.')
|
||||||
|
|
||||||
# The image size for comics
|
#: The image size for comics
|
||||||
comic_screen_size = (584, 754)
|
comic_screen_size = (584, 754)
|
||||||
|
|
||||||
# If True the MOBI renderer on the device supports MOBI indexing
|
#: If True the MOBI renderer on the device supports MOBI indexing
|
||||||
supports_mobi_indexing = False
|
supports_mobi_indexing = False
|
||||||
|
|
||||||
# If True output should be optimized for a touchscreen interface
|
#: If True output should be optimized for a touchscreen interface
|
||||||
touchscreen = False
|
touchscreen = False
|
||||||
touchscreen_news_css = ''
|
touchscreen_news_css = ''
|
||||||
# A list of extra (beyond CSS 2.1) modules supported by the device
|
#: A list of extra (beyond CSS 2.1) modules supported by the device
|
||||||
# Format is a cssutils profile dictionary (see iPad for example)
|
#: Format is a cssutils profile dictionary (see iPad for example)
|
||||||
extra_css_modules = []
|
extra_css_modules = []
|
||||||
|
#: If True, the date is appended to the title of downloaded news
|
||||||
|
periodical_date_in_title = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tags_to_string(cls, tags):
|
def tags_to_string(cls, tags):
|
||||||
@ -550,6 +552,7 @@ class KindleOutput(OutputProfile):
|
|||||||
fbase = 16
|
fbase = 16
|
||||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||||
supports_mobi_indexing = True
|
supports_mobi_indexing = True
|
||||||
|
periodical_date_in_title = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tags_to_string(cls, tags):
|
def tags_to_string(cls, tags):
|
||||||
@ -567,6 +570,7 @@ class KindleDXOutput(OutputProfile):
|
|||||||
dpi = 150.0
|
dpi = 150.0
|
||||||
comic_screen_size = (741, 1022)
|
comic_screen_size = (741, 1022)
|
||||||
supports_mobi_indexing = True
|
supports_mobi_indexing = True
|
||||||
|
periodical_date_in_title = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tags_to_string(cls, tags):
|
def tags_to_string(cls, tags):
|
||||||
|
@ -13,6 +13,7 @@ from calibre.customize.builtins import plugins as builtin_plugins
|
|||||||
from calibre.constants import numeric_version as version, iswindows, isosx
|
from calibre.constants import numeric_version as version, iswindows, isosx
|
||||||
from calibre.devices.interface import DevicePlugin
|
from calibre.devices.interface import DevicePlugin
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
|
from calibre.ebooks.metadata.covers import CoverDownload
|
||||||
from calibre.ebooks.metadata.fetch import MetadataSource
|
from calibre.ebooks.metadata.fetch import MetadataSource
|
||||||
from calibre.utils.config import make_config_dir, Config, ConfigProxy, \
|
from calibre.utils.config import make_config_dir, Config, ConfigProxy, \
|
||||||
plugin_dir, OptionParser, prefs
|
plugin_dir, OptionParser, prefs
|
||||||
@ -234,6 +235,15 @@ def migrate_isbndb_key():
|
|||||||
if key:
|
if key:
|
||||||
prefs.set('isbndb_com_key', '')
|
prefs.set('isbndb_com_key', '')
|
||||||
set_isbndb_key(key)
|
set_isbndb_key(key)
|
||||||
|
|
||||||
|
def cover_sources():
|
||||||
|
customization = config['plugin_customization']
|
||||||
|
for plugin in _initialized_plugins:
|
||||||
|
if isinstance(plugin, CoverDownload):
|
||||||
|
if not is_disabled(plugin):
|
||||||
|
plugin.site_customization = customization.get(plugin.name, '')
|
||||||
|
yield plugin
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Metadata read/write {{{
|
# Metadata read/write {{{
|
||||||
|
@ -19,10 +19,12 @@ class ANDROID(USBMS):
|
|||||||
|
|
||||||
VENDOR_ID = {
|
VENDOR_ID = {
|
||||||
# HTC
|
# HTC
|
||||||
0x0bb4 : { 0x0c02 : [0x100], 0x0c01 : [0x100], 0x0ff9 : [0x0100]},
|
0x0bb4 : { 0x0c02 : [0x100, 0x227], 0x0c01 : [0x100, 0x227], 0x0ff9
|
||||||
|
: [0x0100, 0x227]},
|
||||||
|
|
||||||
# Motorola
|
# Motorola
|
||||||
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216]},
|
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
|
||||||
|
0x4285 : [0x216]},
|
||||||
|
|
||||||
# Sony Ericsson
|
# Sony Ericsson
|
||||||
0xfce : { 0xd12e : [0x0100]},
|
0xfce : { 0xd12e : [0x0100]},
|
||||||
@ -30,7 +32,8 @@ class ANDROID(USBMS):
|
|||||||
0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]},
|
0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]},
|
||||||
|
|
||||||
# Samsung
|
# Samsung
|
||||||
0x04e8 : { 0x681d : [0x0222, 0x0400], 0x681c : [0x0222, 0x0224]},
|
0x04e8 : { 0x681d : [0x0222, 0x0400],
|
||||||
|
0x681c : [0x0222, 0x0224, 0x0400]},
|
||||||
|
|
||||||
# Acer
|
# Acer
|
||||||
0x502 : { 0x3203 : [0x0100]},
|
0x502 : { 0x3203 : [0x0100]},
|
||||||
@ -51,9 +54,9 @@ class ANDROID(USBMS):
|
|||||||
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX']
|
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX']
|
||||||
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',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET']
|
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959']
|
||||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD',
|
||||||
'FILE-STOR_GADGET']
|
'FILE-STOR_GADGET', 'SGH-T959']
|
||||||
|
|
||||||
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
||||||
|
|
||||||
@ -70,6 +73,16 @@ class ANDROID(USBMS):
|
|||||||
dirs = [x.strip() for x in dirs.split(',')]
|
dirs = [x.strip() for x in dirs.split(',')]
|
||||||
self.EBOOK_DIR_MAIN = dirs
|
self.EBOOK_DIR_MAIN = dirs
|
||||||
|
|
||||||
|
def get_main_ebook_dir(self, for_upload=False):
|
||||||
|
dirs = self.EBOOK_DIR_MAIN
|
||||||
|
if not for_upload:
|
||||||
|
def aldiko_tweak(x):
|
||||||
|
return 'eBooks' if x == 'eBooks/import' else x
|
||||||
|
if isinstance(dirs, basestring):
|
||||||
|
dirs = [dirs]
|
||||||
|
dirs = list(map(aldiko_tweak, dirs))
|
||||||
|
return dirs
|
||||||
|
|
||||||
class S60(USBMS):
|
class S60(USBMS):
|
||||||
|
|
||||||
name = 'S60 driver'
|
name = 'S60 driver'
|
||||||
|
@ -2586,14 +2586,20 @@ class ITUNES(DriverBase):
|
|||||||
if metadata.series and self.settings().read_metadata:
|
if metadata.series and self.settings().read_metadata:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" using Series name as Genre")
|
self.log.info(" using Series name as Genre")
|
||||||
|
|
||||||
|
# Format the index as a sort key
|
||||||
|
index = metadata.series_index
|
||||||
|
integer = int(index)
|
||||||
|
fraction = index-integer
|
||||||
|
series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
|
||||||
if lb_added:
|
if lb_added:
|
||||||
lb_added.sort_name.set("%s %04f" % (metadata.series, metadata.series_index))
|
lb_added.sort_name.set("%s %s" % (metadata.series, series_index))
|
||||||
lb_added.genre.set(metadata.series)
|
lb_added.genre.set(metadata.series)
|
||||||
lb_added.episode_ID.set(metadata.series)
|
lb_added.episode_ID.set(metadata.series)
|
||||||
lb_added.episode_number.set(metadata.series_index)
|
lb_added.episode_number.set(metadata.series_index)
|
||||||
|
|
||||||
if db_added:
|
if db_added:
|
||||||
db_added.sort_name.set("%s %04f" % (metadata.series, metadata.series_index))
|
db_added.sort_name.set("%s %s" % (metadata.series, series_index))
|
||||||
db_added.genre.set(metadata.series)
|
db_added.genre.set(metadata.series)
|
||||||
db_added.episode_ID.set(metadata.series)
|
db_added.episode_ID.set(metadata.series)
|
||||||
db_added.episode_number.set(metadata.series_index)
|
db_added.episode_number.set(metadata.series_index)
|
||||||
@ -2658,8 +2664,13 @@ class ITUNES(DriverBase):
|
|||||||
if metadata.series and self.settings().read_metadata:
|
if metadata.series and self.settings().read_metadata:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" using Series name as Genre")
|
self.log.info(" using Series name as Genre")
|
||||||
|
# Format the index as a sort key
|
||||||
|
index = metadata.series_index
|
||||||
|
integer = int(index)
|
||||||
|
fraction = index-integer
|
||||||
|
series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
|
||||||
if lb_added:
|
if lb_added:
|
||||||
lb_added.SortName = "%s %04f" % (metadata.series, metadata.series_index)
|
lb_added.SortName = "%s %s" % (metadata.series, series_index)
|
||||||
lb_added.Genre = metadata.series
|
lb_added.Genre = metadata.series
|
||||||
lb_added.EpisodeID = metadata.series
|
lb_added.EpisodeID = metadata.series
|
||||||
try:
|
try:
|
||||||
@ -2667,7 +2678,7 @@ class ITUNES(DriverBase):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
if db_added:
|
if db_added:
|
||||||
db_added.SortName = "%s %04f" % (metadata.series, metadata.series_index)
|
db_added.SortName = "%s %s" % (metadata.series, series_index)
|
||||||
db_added.Genre = metadata.series
|
db_added.Genre = metadata.series
|
||||||
db_added.EpisodeID = metadata.series
|
db_added.EpisodeID = metadata.series
|
||||||
try:
|
try:
|
||||||
|
@ -43,6 +43,7 @@ class THEBOOK(N516):
|
|||||||
BCD = [0x399]
|
BCD = [0x399]
|
||||||
MAIN_MEMORY_VOLUME_LABEL = 'The Book Main Memory'
|
MAIN_MEMORY_VOLUME_LABEL = 'The Book Main Memory'
|
||||||
EBOOK_DIR_MAIN = 'My books'
|
EBOOK_DIR_MAIN = 'My books'
|
||||||
|
WINDOWS_CARD_A_MEM = '_FILE-STOR_GADGE'
|
||||||
|
|
||||||
class ALEX(N516):
|
class ALEX(N516):
|
||||||
|
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
"""
|
|
||||||
Define the minimum interface that a device backend must satisfy to be used in
|
|
||||||
the GUI. A device backend must subclass the L{Device} class. See prs500.py for
|
|
||||||
a backend that implement the Device interface for the SONY PRS500 Reader.
|
|
||||||
"""
|
|
||||||
import os
|
import os
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
@ -15,32 +10,38 @@ class DevicePlugin(Plugin):
|
|||||||
"""
|
"""
|
||||||
Defines the interface that should be implemented by backends that
|
Defines the interface that should be implemented by backends that
|
||||||
communicate with an ebook reader.
|
communicate with an ebook reader.
|
||||||
|
|
||||||
The C{end_session} variables are used for USB session management. Sometimes
|
|
||||||
the front-end needs to call several methods one after another, in which case
|
|
||||||
the USB session should not be closed after each method call.
|
|
||||||
"""
|
"""
|
||||||
type = _('Device Interface')
|
type = _('Device Interface')
|
||||||
|
|
||||||
# Ordered list of supported formats
|
#: Ordered list of supported formats
|
||||||
FORMATS = ["lrf", "rtf", "pdf", "txt"]
|
FORMATS = ["lrf", "rtf", "pdf", "txt"]
|
||||||
|
|
||||||
#: VENDOR_ID can be either an integer, a list of integers or a dictionary
|
#: VENDOR_ID can be either an integer, a list of integers or a dictionary
|
||||||
#: If it is a dictionary, it must be a dictionary of dictionaries, of the form
|
#: If it is a dictionary, it must be a dictionary of dictionaries,
|
||||||
#: {
|
#: of the form::
|
||||||
#: integer_vendor_id : { product_id : [list of BCDs], ... },
|
#:
|
||||||
#: ...
|
#: {
|
||||||
#: }
|
#: integer_vendor_id : { product_id : [list of BCDs], ... },
|
||||||
|
#: ...
|
||||||
|
#: }
|
||||||
|
#:
|
||||||
VENDOR_ID = 0x0000
|
VENDOR_ID = 0x0000
|
||||||
|
|
||||||
#: An integer or a list of integers
|
#: An integer or a list of integers
|
||||||
PRODUCT_ID = 0x0000
|
PRODUCT_ID = 0x0000
|
||||||
# BCD can be either None to not distinguish between devices based on BCD, or
|
#: BCD can be either None to not distinguish between devices based on BCD, or
|
||||||
# it can be a list of the BCD numbers of all devices supported by this driver.
|
#: it can be a list of the BCD numbers of all devices supported by this driver.
|
||||||
BCD = None
|
BCD = None
|
||||||
THUMBNAIL_HEIGHT = 68 # Height for thumbnails on device
|
|
||||||
# Whether the metadata on books can be set via the GUI.
|
#: Height for thumbnails on the device
|
||||||
|
THUMBNAIL_HEIGHT = 68
|
||||||
|
|
||||||
|
#: Whether the metadata on books can be set via the GUI.
|
||||||
CAN_SET_METADATA = True
|
CAN_SET_METADATA = True
|
||||||
|
|
||||||
#: Path separator for paths to books on device
|
#: Path separator for paths to books on device
|
||||||
path_sep = os.sep
|
path_sep = os.sep
|
||||||
|
|
||||||
#: Icon for this device
|
#: Icon for this device
|
||||||
icon = I('reader.svg')
|
icon = I('reader.svg')
|
||||||
|
|
||||||
@ -51,6 +52,11 @@ class DevicePlugin(Plugin):
|
|||||||
#: long time
|
#: long time
|
||||||
OPEN_FEEDBACK_MESSAGE = None
|
OPEN_FEEDBACK_MESSAGE = None
|
||||||
|
|
||||||
|
#: Set of extensions that are "virtual books" on the device
|
||||||
|
#: and therefore cannot be viewed/saved/added to library
|
||||||
|
#: For example: ``frozenset(['kobo'])``
|
||||||
|
VIRTUAL_BOOK_EXTENSIONS = frozenset([])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_gui_name(cls):
|
def get_gui_name(cls):
|
||||||
if hasattr(cls, 'gui_name'):
|
if hasattr(cls, 'gui_name'):
|
||||||
@ -121,6 +127,7 @@ class DevicePlugin(Plugin):
|
|||||||
Return True, device_info if a device handled by this plugin is currently connected.
|
Return True, device_info if a device handled by this plugin is currently connected.
|
||||||
|
|
||||||
:param devices_on_system: List of devices currently connected
|
:param devices_on_system: List of devices currently connected
|
||||||
|
|
||||||
'''
|
'''
|
||||||
if iswindows:
|
if iswindows:
|
||||||
return self.is_usb_connected_windows(devices_on_system,
|
return self.is_usb_connected_windows(devices_on_system,
|
||||||
@ -157,13 +164,14 @@ class DevicePlugin(Plugin):
|
|||||||
def reset(self, key='-1', log_packets=False, report_progress=None,
|
def reset(self, key='-1', log_packets=False, report_progress=None,
|
||||||
detected_device=None) :
|
detected_device=None) :
|
||||||
"""
|
"""
|
||||||
:key: The key to unlock the device
|
:param key: The key to unlock the device
|
||||||
:log_packets: If true the packet stream to/from the device is logged
|
:param log_packets: If true the packet stream to/from the device is logged
|
||||||
:report_progress: Function that is called with a % progress
|
:param report_progress: Function that is called with a % progress
|
||||||
(number between 0 and 100) for various tasks
|
(number between 0 and 100) for various tasks
|
||||||
If it is called with -1 that means that the
|
If it is called with -1 that means that the
|
||||||
task does not have any progress information
|
task does not have any progress information
|
||||||
:detected_device: Device information from the device scanner
|
:param detected_device: Device information from the device scanner
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -174,19 +182,21 @@ class DevicePlugin(Plugin):
|
|||||||
is only called after the vendor, product ids and the bcd have matched, so
|
is only called after the vendor, product ids and the bcd have matched, so
|
||||||
it can do some relatively time intensive checks. The default implementation
|
it can do some relatively time intensive checks. The default implementation
|
||||||
returns True. This method is called only on windows. See also
|
returns True. This method is called only on windows. See also
|
||||||
:method:`can_handle`.
|
:meth:`can_handle`.
|
||||||
|
|
||||||
:param device_info: On windows a device ID string. On Unix a tuple of
|
:param device_info: On windows a device ID string. On Unix a tuple of
|
||||||
``(vendor_id, product_id, bcd)``.
|
``(vendor_id, product_id, bcd)``.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def can_handle(self, device_info, debug=False):
|
def can_handle(self, device_info, debug=False):
|
||||||
'''
|
'''
|
||||||
Unix version of :method:`can_handle_windows`
|
Unix version of :meth:`can_handle_windows`
|
||||||
|
|
||||||
:param device_info: Is a tupe of (vid, pid, bcd, manufacturer, product,
|
:param device_info: Is a tupe of (vid, pid, bcd, manufacturer, product,
|
||||||
serial number)
|
serial number)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -198,7 +208,8 @@ class DevicePlugin(Plugin):
|
|||||||
For example: For devices that present themselves as USB Mass storage
|
For example: For devices that present themselves as USB Mass storage
|
||||||
devices, this method would be responsible for mounting the device or
|
devices, this method would be responsible for mounting the device or
|
||||||
if the device has been automounted, for finding out where it has been
|
if the device has been automounted, for finding out where it has been
|
||||||
mounted. The base class within USBMS device.py has a implementation of
|
mounted. The method :meth:`calibre.devices.usbms.device.Device.open` has
|
||||||
|
an implementation of
|
||||||
this function that should serve as a good example for USB Mass storage
|
this function that should serve as a good example for USB Mass storage
|
||||||
devices.
|
devices.
|
||||||
'''
|
'''
|
||||||
@ -219,17 +230,20 @@ class DevicePlugin(Plugin):
|
|||||||
|
|
||||||
def set_progress_reporter(self, report_progress):
|
def set_progress_reporter(self, report_progress):
|
||||||
'''
|
'''
|
||||||
@param report_progress: Function that is called with a % progress
|
:param report_progress: Function that is called with a % progress
|
||||||
(number between 0 and 100) for various tasks
|
(number between 0 and 100) for various tasks
|
||||||
If it is called with -1 that means that the
|
If it is called with -1 that means that the
|
||||||
task does not have any progress information
|
task does not have any progress information
|
||||||
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def get_device_information(self, end_session=True):
|
def get_device_information(self, end_session=True):
|
||||||
"""
|
"""
|
||||||
Ask device for device information. See L{DeviceInfoQuery}.
|
Ask device for device information. See L{DeviceInfoQuery}.
|
||||||
@return: (device name, device version, software version on device, mime type)
|
|
||||||
|
:return: (device name, device version, software version on device, mime type)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -252,8 +266,9 @@ class DevicePlugin(Plugin):
|
|||||||
2. Memory Card A
|
2. Memory Card A
|
||||||
3. Memory Card B
|
3. Memory Card B
|
||||||
|
|
||||||
@return: A 3 element list with total space in bytes of (1, 2, 3). If a
|
:return: A 3 element list with total space in bytes of (1, 2, 3). If a
|
||||||
particular device doesn't have any of these locations it should return 0.
|
particular device doesn't have any of these locations it should return 0.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -264,19 +279,23 @@ class DevicePlugin(Plugin):
|
|||||||
2. Card A
|
2. Card A
|
||||||
3. Card B
|
3. Card B
|
||||||
|
|
||||||
@return: A 3 element list with free space in bytes of (1, 2, 3). If a
|
:return: A 3 element list with free space in bytes of (1, 2, 3). If a
|
||||||
particular device doesn't have any of these locations it should return -1.
|
particular device doesn't have any of these locations it should return -1.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def books(self, oncard=None, end_session=True):
|
def books(self, oncard=None, end_session=True):
|
||||||
"""
|
"""
|
||||||
Return a list of ebooks on the device.
|
Return a list of ebooks on the device.
|
||||||
@param oncard: If 'carda' or 'cardb' return a list of ebooks on the
|
|
||||||
|
:param oncard: If 'carda' or 'cardb' return a list of ebooks on the
|
||||||
specific storage card, otherwise return list of ebooks
|
specific storage card, otherwise return list of ebooks
|
||||||
in main memory of device. If a card is specified and no
|
in main memory of device. If a card is specified and no
|
||||||
books are on the card return empty list.
|
books are on the card return empty list.
|
||||||
@return: A BookList.
|
|
||||||
|
:return: A BookList.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -285,25 +304,27 @@ class DevicePlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
Upload a list of books to the device. If a file already
|
Upload a list of books to the device. If a file already
|
||||||
exists on the device, it should be replaced.
|
exists on the device, it should be replaced.
|
||||||
This method should raise a L{FreeSpaceError} if there is not enough
|
This method should raise a :class:`FreeSpaceError` if there is not enough
|
||||||
free space on the device. The text of the FreeSpaceError must contain the
|
free space on the device. The text of the FreeSpaceError must contain the
|
||||||
word "card" if C{on_card} is not None otherwise it must contain the word "memory".
|
word "card" if ``on_card`` is not None otherwise it must contain the word "memory".
|
||||||
:files: A list of paths and/or file-like objects. If they are paths and
|
|
||||||
the paths point to temporary files, they may have an additional
|
:param files: A list of paths and/or file-like objects. If they are paths and
|
||||||
attribute, original_file_path pointing to the originals. They may have
|
the paths point to temporary files, they may have an additional
|
||||||
another optional attribute, deleted_after_upload which if True means
|
attribute, original_file_path pointing to the originals. They may have
|
||||||
that the file pointed to by original_file_path will be deleted after
|
another optional attribute, deleted_after_upload which if True means
|
||||||
being uploaded to the device.
|
that the file pointed to by original_file_path will be deleted after
|
||||||
:names: A list of file names that the books should have
|
being uploaded to the device.
|
||||||
once uploaded to the device. len(names) == len(files)
|
:param names: A list of file names that the books should have
|
||||||
|
once uploaded to the device. len(names) == len(files)
|
||||||
|
:param metadata: If not None, it is a list of :class:`MetaInformation` objects.
|
||||||
|
The idea is to use the metadata to determine where on the device to
|
||||||
|
put the book. len(metadata) == len(files). Apart from the regular
|
||||||
|
cover (path to cover), there may also be a thumbnail attribute, which should
|
||||||
|
be used in preference. The thumbnail attribute is of the form
|
||||||
|
(width, height, cover_data as jpeg).
|
||||||
|
|
||||||
:return: A list of 3-element tuples. The list is meant to be passed
|
:return: A list of 3-element tuples. The list is meant to be passed
|
||||||
to L{add_books_to_metadata}.
|
to :meth:`add_books_to_metadata`.
|
||||||
:metadata: If not None, it is a list of :class:`MetaInformation` objects.
|
|
||||||
The idea is to use the metadata to determine where on the device to
|
|
||||||
put the book. len(metadata) == len(files). Apart from the regular
|
|
||||||
cover (path to cover), there may also be a thumbnail attribute, which should
|
|
||||||
be used in preference. The thumbnail attribute is of the form
|
|
||||||
(width, height, cover_data as jpeg).
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -312,12 +333,15 @@ class DevicePlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
Add locations to the booklists. This function must not communicate with
|
Add locations to the booklists. This function must not communicate with
|
||||||
the device.
|
the device.
|
||||||
@param locations: Result of a call to L{upload_books}
|
|
||||||
@param metadata: List of MetaInformation objects, same as for
|
:param locations: Result of a call to L{upload_books}
|
||||||
:method:`upload_books`.
|
:param metadata: List of :class:`MetaInformation` objects, same as for
|
||||||
@param booklists: A tuple containing the result of calls to
|
:meth:`upload_books`.
|
||||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
:param booklists: A tuple containing the result of calls to
|
||||||
L{books}(oncard='cardb')).
|
(:meth:`books(oncard=None)`,
|
||||||
|
:meth:`books(oncard='carda')`,
|
||||||
|
:meth`books(oncard='cardb')`).
|
||||||
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -332,26 +356,35 @@ class DevicePlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
Remove books from the metadata list. This function must not communicate
|
Remove books from the metadata list. This function must not communicate
|
||||||
with the device.
|
with the device.
|
||||||
@param paths: paths to books on the device.
|
|
||||||
@param booklists: A tuple containing the result of calls to
|
:param paths: paths to books on the device.
|
||||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
:param booklists: A tuple containing the result of calls to
|
||||||
L{books}(oncard='cardb')).
|
(:meth:`books(oncard=None)`,
|
||||||
|
:meth:`books(oncard='carda')`,
|
||||||
|
:meth`books(oncard='cardb')`).
|
||||||
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def sync_booklists(self, booklists, end_session=True):
|
def sync_booklists(self, booklists, end_session=True):
|
||||||
'''
|
'''
|
||||||
Update metadata on device.
|
Update metadata on device.
|
||||||
@param booklists: A tuple containing the result of calls to
|
|
||||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
:param booklists: A tuple containing the result of calls to
|
||||||
L{books}(oncard='cardb')).
|
(:meth:`books(oncard=None)`,
|
||||||
|
:meth:`books(oncard='carda')`,
|
||||||
|
:meth`books(oncard='cardb')`).
|
||||||
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def get_file(self, path, outfile, end_session=True):
|
def get_file(self, path, outfile, end_session=True):
|
||||||
'''
|
'''
|
||||||
Read the file at C{path} on the device and write it to outfile.
|
Read the file at ``path`` on the device and write it to outfile.
|
||||||
@param outfile: file object like C{sys.stdout} or the result of an C{open} call
|
|
||||||
|
:param outfile: file object like ``sys.stdout`` or the result of an
|
||||||
|
:func:`open` call.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -365,8 +398,8 @@ class DevicePlugin(Plugin):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def save_settings(cls, settings_widget):
|
def save_settings(cls, settings_widget):
|
||||||
'''
|
'''
|
||||||
Should save settings to disk. Takes the widget created in config_widget
|
Should save settings to disk. Takes the widget created in
|
||||||
and saves all settings to disk.
|
:meth:`config_widget` and saves all settings to disk.
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -381,16 +414,18 @@ class DevicePlugin(Plugin):
|
|||||||
|
|
||||||
class BookList(list):
|
class BookList(list):
|
||||||
'''
|
'''
|
||||||
A list of books. Each Book object must have the fields:
|
A list of books. Each Book object must have the fields
|
||||||
1. title
|
|
||||||
2. authors
|
#. title
|
||||||
3. size (file size of the book)
|
#. authors
|
||||||
4. datetime (a UTC time tuple)
|
#. size (file size of the book)
|
||||||
5. path (path on the device to the book)
|
#. datetime (a UTC time tuple)
|
||||||
6. thumbnail (can be None) thumbnail is either a str/bytes object with the
|
#. path (path on the device to the book)
|
||||||
|
#. thumbnail (can be None) thumbnail is either a str/bytes object with the
|
||||||
image data or it should have an attribute image_path that stores an
|
image data or it should have an attribute image_path that stores an
|
||||||
absolute (platform native) path to the image
|
absolute (platform native) path to the image
|
||||||
7. tags (a list of strings, can be empty).
|
#. tags (a list of strings, can be empty).
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
__getslice__ = None
|
__getslice__ = None
|
||||||
@ -427,6 +462,7 @@ class BookList(list):
|
|||||||
created from series, in which case series_index is used.
|
created from series, in which case series_index is used.
|
||||||
|
|
||||||
:param collection_attributes: A list of attributes of the Book object
|
:param collection_attributes: A list of attributes of the Book object
|
||||||
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -80,3 +80,21 @@ class JETBOOK(USBMS):
|
|||||||
|
|
||||||
return mi
|
return mi
|
||||||
|
|
||||||
|
class MIBUK(USBMS):
|
||||||
|
|
||||||
|
name = 'MiBuk Wolder Device Interface'
|
||||||
|
description = _('Communicate with the MiBuk Wolder reader.')
|
||||||
|
author = 'Kovid Goyal'
|
||||||
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
|
||||||
|
FORMATS = ['epub', 'mobi', 'prc', 'fb2', 'txt', 'rtf', 'pdf']
|
||||||
|
|
||||||
|
VENDOR_ID = [0x0525]
|
||||||
|
PRODUCT_ID = [0xa4a5]
|
||||||
|
BCD = [0x314]
|
||||||
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
VENDOR_NAME = 'LINUX'
|
||||||
|
WINDOWS_MAIN_MEM = 'WOLDERMIBUK'
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class Book(MetaInformation):
|
|||||||
def thumbnail(self):
|
def thumbnail(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def smart_update(self, other):
|
def smart_update(self, other, replace_metadata=False):
|
||||||
'''
|
'''
|
||||||
Merge the information in C{other} into self. In case of conflicts, the information
|
Merge the information in C{other} into self. In case of conflicts, the information
|
||||||
in C{other} takes precedence, unless the information in C{other} is NULL.
|
in C{other} takes precedence, unless the information in C{other} is NULL.
|
||||||
|
@ -38,6 +38,8 @@ class KOBO(USBMS):
|
|||||||
EBOOK_DIR_MAIN = ''
|
EBOOK_DIR_MAIN = ''
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
VIRTUAL_BOOK_EXTENSIONS = frozenset(['kobo'])
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
USBMS.initialize(self)
|
USBMS.initialize(self)
|
||||||
self.book_class = Book
|
self.book_class = Book
|
||||||
@ -235,7 +237,7 @@ class KOBO(USBMS):
|
|||||||
else: # if extension == '.html' or extension == '.txt':
|
else: # if extension == '.html' or extension == '.txt':
|
||||||
ContentType = 999 # Yet another hack: to get around Kobo changing how ContentID is stored
|
ContentType = 999 # Yet another hack: to get around Kobo changing how ContentID is stored
|
||||||
ContentID = self.contentid_from_path(path, ContentType)
|
ContentID = self.contentid_from_path(path, ContentType)
|
||||||
|
|
||||||
ImageID = self.delete_via_sql(ContentID, ContentType)
|
ImageID = self.delete_via_sql(ContentID, ContentType)
|
||||||
#print " We would now delete the Images for" + ImageID
|
#print " We would now delete the Images for" + ImageID
|
||||||
self.delete_images(ImageID)
|
self.delete_images(ImageID)
|
||||||
@ -355,3 +357,17 @@ class KOBO(USBMS):
|
|||||||
# print "Internal: " + filename
|
# print "Internal: " + filename
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
def get_file(self, path, *args, **kwargs):
|
||||||
|
tpath = self.munge_path(path)
|
||||||
|
extension = os.path.splitext(tpath)[1]
|
||||||
|
if extension == '.kobo':
|
||||||
|
from calibre.devices.errors import UserFeedback
|
||||||
|
raise UserFeedback(_("Not Implemented"),
|
||||||
|
_('".kobo" files do not exist on the device as books '
|
||||||
|
'instead, they are rows in the sqlite database. '
|
||||||
|
'Currently they cannot be exported or viewed.'),
|
||||||
|
UserFeedback.WARN)
|
||||||
|
|
||||||
|
return USBMS.get_file(self, path, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
class PALMPRE(USBMS):
|
class PALMPRE(USBMS):
|
||||||
@ -44,12 +46,13 @@ class AVANT(USBMS):
|
|||||||
BCD = [0x0319]
|
BCD = [0x0319]
|
||||||
|
|
||||||
VENDOR_NAME = 'E-BOOK'
|
VENDOR_NAME = 'E-BOOK'
|
||||||
WINDOWS_MAIN_MEM = 'READER'
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'READER'
|
||||||
|
|
||||||
EBOOK_DIR_MAIN = ''
|
EBOOK_DIR_MAIN = ''
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
class SWEEX(USBMS):
|
class SWEEX(USBMS):
|
||||||
|
# Identical to the Promedia
|
||||||
name = 'Sweex Device Interface'
|
name = 'Sweex Device Interface'
|
||||||
gui_name = 'Sweex'
|
gui_name = 'Sweex'
|
||||||
description = _('Communicate with the Sweex MM300')
|
description = _('Communicate with the Sweex MM300')
|
||||||
@ -83,7 +86,17 @@ class PDNOVEL(USBMS):
|
|||||||
|
|
||||||
VENDOR_NAME = 'ANDROID'
|
VENDOR_NAME = 'ANDROID'
|
||||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '__UMS_COMPOSITE'
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '__UMS_COMPOSITE'
|
||||||
|
THUMBNAIL_HEIGHT = 144
|
||||||
|
|
||||||
EBOOK_DIR_MAIN = 'eBooks'
|
EBOOK_DIR_MAIN = 'eBooks'
|
||||||
SUPPORTS_SUB_DIRS = False
|
SUPPORTS_SUB_DIRS = False
|
||||||
|
DELETE_EXTS = ['.jpg', '.jpeg', '.png']
|
||||||
|
|
||||||
|
|
||||||
|
def upload_cover(self, path, filename, metadata):
|
||||||
|
coverdata = getattr(metadata, 'thumbnail', None)
|
||||||
|
if coverdata and coverdata[2]:
|
||||||
|
with open('%s.jpg' % os.path.join(path, filename), 'wb') as coverfile:
|
||||||
|
coverfile.write(coverdata[2])
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class NOOK(USBMS):
|
|||||||
# Ordered list of supported formats
|
# Ordered list of supported formats
|
||||||
FORMATS = ['epub', 'pdb', 'pdf']
|
FORMATS = ['epub', 'pdb', 'pdf']
|
||||||
|
|
||||||
VENDOR_ID = [0x2080]
|
VENDOR_ID = [0x2080, 0x18d1] # 0x18d1 is for softrooted nook
|
||||||
PRODUCT_ID = [0x001]
|
PRODUCT_ID = [0x001]
|
||||||
BCD = [0x322]
|
BCD = [0x322]
|
||||||
|
|
||||||
|
@ -10,10 +10,10 @@ from base64 import b64decode
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from calibre import prints, guess_type
|
from calibre import prints, guess_type, isbytestring
|
||||||
from calibre.devices.errors import DeviceError
|
from calibre.devices.errors import DeviceError
|
||||||
from calibre.devices.usbms.driver import debug_print
|
from calibre.devices.usbms.driver import debug_print
|
||||||
from calibre.constants import DEBUG
|
from calibre.constants import DEBUG, preferred_encoding
|
||||||
from calibre.ebooks.chardet import xml_to_unicode
|
from calibre.ebooks.chardet import xml_to_unicode
|
||||||
from calibre.ebooks.metadata import authors_to_string, title_sort
|
from calibre.ebooks.metadata import authors_to_string, title_sort
|
||||||
|
|
||||||
@ -46,7 +46,11 @@ def strptime(src):
|
|||||||
return time.strptime(' '.join(src), '%w, %d %m %Y %H:%M:%S %Z')
|
return time.strptime(' '.join(src), '%w, %d %m %Y %H:%M:%S %Z')
|
||||||
|
|
||||||
def strftime(epoch, zone=time.localtime):
|
def strftime(epoch, zone=time.localtime):
|
||||||
src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split()
|
try:
|
||||||
|
src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone(epoch)).split()
|
||||||
|
except:
|
||||||
|
src = time.strftime("%w, %d %m %Y %H:%M:%S GMT", zone()).split()
|
||||||
|
|
||||||
src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+','
|
src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+','
|
||||||
src[2] = INVERSE_MONTH_MAP[int(src[2])]
|
src[2] = INVERSE_MONTH_MAP[int(src[2])]
|
||||||
return ' '.join(src)
|
return ' '.join(src)
|
||||||
@ -328,7 +332,10 @@ class XMLCache(object):
|
|||||||
'descendant::*[local-name()="jpeg"]|'
|
'descendant::*[local-name()="jpeg"]|'
|
||||||
'descendant::*[local-name()="png"]'):
|
'descendant::*[local-name()="png"]'):
|
||||||
if img.text:
|
if img.text:
|
||||||
raw = b64decode(img.text.strip())
|
try:
|
||||||
|
raw = b64decode(img.text.strip())
|
||||||
|
except:
|
||||||
|
continue
|
||||||
book.thumbnail = raw
|
book.thumbnail = raw
|
||||||
break
|
break
|
||||||
break
|
break
|
||||||
@ -473,6 +480,13 @@ class XMLCache(object):
|
|||||||
# if the case of a tie, and hope it is right.
|
# if the case of a tie, and hope it is right.
|
||||||
timestamp = os.path.getmtime(path)
|
timestamp = os.path.getmtime(path)
|
||||||
rec_date = record.get('date', None)
|
rec_date = record.get('date', None)
|
||||||
|
|
||||||
|
def clean(x):
|
||||||
|
if isbytestring(x):
|
||||||
|
x = x.decode(preferred_encoding, 'replace')
|
||||||
|
x.replace(u'\0', '')
|
||||||
|
return x
|
||||||
|
|
||||||
if not getattr(book, '_new_book', False): # book is not new
|
if not getattr(book, '_new_book', False): # book is not new
|
||||||
if strftime(timestamp, zone=time.gmtime) == rec_date:
|
if strftime(timestamp, zone=time.gmtime) == rec_date:
|
||||||
gtz_count += 1
|
gtz_count += 1
|
||||||
@ -486,19 +500,19 @@ class XMLCache(object):
|
|||||||
tz = time.gmtime
|
tz = time.gmtime
|
||||||
debug_print("Using GMT TZ for new book", book.lpath)
|
debug_print("Using GMT TZ for new book", book.lpath)
|
||||||
date = strftime(timestamp, zone=tz)
|
date = strftime(timestamp, zone=tz)
|
||||||
record.set('date', date)
|
record.set('date', clean(date))
|
||||||
|
|
||||||
record.set('size', str(os.stat(path).st_size))
|
record.set('size', clean(str(os.stat(path).st_size)))
|
||||||
title = book.title if book.title else _('Unknown')
|
title = book.title if book.title else _('Unknown')
|
||||||
record.set('title', title)
|
record.set('title', clean(title))
|
||||||
ts = book.title_sort
|
ts = book.title_sort
|
||||||
if not ts:
|
if not ts:
|
||||||
ts = title_sort(title)
|
ts = title_sort(title)
|
||||||
record.set('titleSorter', ts)
|
record.set('titleSorter', clean(ts))
|
||||||
if self.use_author_sort and book.author_sort is not None:
|
if self.use_author_sort and book.author_sort is not None:
|
||||||
record.set('author', book.author_sort)
|
record.set('author', clean(book.author_sort))
|
||||||
else:
|
else:
|
||||||
record.set('author', authors_to_string(book.authors))
|
record.set('author', clean(authors_to_string(book.authors)))
|
||||||
ext = os.path.splitext(path)[1]
|
ext = os.path.splitext(path)[1]
|
||||||
if ext:
|
if ext:
|
||||||
ext = ext[1:].lower()
|
ext = ext[1:].lower()
|
||||||
@ -506,7 +520,7 @@ class XMLCache(object):
|
|||||||
if mime is None:
|
if mime is None:
|
||||||
mime = guess_type('a.'+ext)[0]
|
mime = guess_type('a.'+ext)[0]
|
||||||
if mime is not None:
|
if mime is not None:
|
||||||
record.set('mime', mime)
|
record.set('mime', clean(mime))
|
||||||
if 'sourceid' not in record.attrib:
|
if 'sourceid' not in record.attrib:
|
||||||
record.set('sourceid', '1')
|
record.set('sourceid', '1')
|
||||||
if 'id' not in record.attrib:
|
if 'id' not in record.attrib:
|
||||||
|
@ -98,6 +98,9 @@ class LinuxScanner(object):
|
|||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
ans = set([])
|
ans = set([])
|
||||||
|
if not self.ok:
|
||||||
|
raise RuntimeError('DeviceScanner requires the /sys filesystem to work.')
|
||||||
|
|
||||||
for x in os.listdir(self.base):
|
for x in os.listdir(self.base):
|
||||||
base = os.path.join(self.base, x)
|
base = os.path.join(self.base, x)
|
||||||
ven = os.path.join(base, 'idVendor')
|
ven = os.path.join(base, 'idVendor')
|
||||||
@ -145,8 +148,6 @@ class DeviceScanner(object):
|
|||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
if isosx and osx_scanner is None:
|
if isosx and osx_scanner is None:
|
||||||
raise RuntimeError('The Python extension usbobserver must be available on OS X.')
|
raise RuntimeError('The Python extension usbobserver must be available on OS X.')
|
||||||
if islinux and not linux_scanner.ok:
|
|
||||||
raise RuntimeError('DeviceScanner requires the /sys filesystem to work.')
|
|
||||||
self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner
|
self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner
|
||||||
self.devices = []
|
self.devices = []
|
||||||
|
|
||||||
|
@ -72,13 +72,13 @@ class Book(MetaInformation):
|
|||||||
def thumbnail(self):
|
def thumbnail(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def smart_update(self, other):
|
def smart_update(self, other, replace_metadata=False):
|
||||||
'''
|
'''
|
||||||
Merge the information in C{other} into self. In case of conflicts, the information
|
Merge the information in C{other} into self. In case of conflicts, the information
|
||||||
in C{other} takes precedence, unless the information in C{other} is NULL.
|
in C{other} takes precedence, unless the information in C{other} is NULL.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
MetaInformation.smart_update(self, other, replace_tags=True)
|
MetaInformation.smart_update(self, other, replace_metadata)
|
||||||
|
|
||||||
for attr in self.BOOK_ATTRS:
|
for attr in self.BOOK_ATTRS:
|
||||||
if hasattr(other, attr):
|
if hasattr(other, attr):
|
||||||
@ -116,7 +116,7 @@ class BookList(_BookList):
|
|||||||
self.append(book)
|
self.append(book)
|
||||||
return True
|
return True
|
||||||
if replace_metadata:
|
if replace_metadata:
|
||||||
self[b].smart_update(book)
|
self[b].smart_update(book, replace_metadata=True)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -132,6 +132,8 @@ class CollectionsBookList(BookList):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def get_collections(self, collection_attributes):
|
def get_collections(self, collection_attributes):
|
||||||
|
from calibre.devices.usbms.driver import debug_print
|
||||||
|
debug_print('Starting get_collections:', prefs['manage_device_metadata'])
|
||||||
collections = {}
|
collections = {}
|
||||||
series_categories = set([])
|
series_categories = set([])
|
||||||
# This map of sets is used to avoid linear searches when testing for
|
# This map of sets is used to avoid linear searches when testing for
|
||||||
@ -146,14 +148,19 @@ class CollectionsBookList(BookList):
|
|||||||
# book in all existing collections. Do not add any new ones.
|
# book in all existing collections. Do not add any new ones.
|
||||||
attrs = ['device_collections']
|
attrs = ['device_collections']
|
||||||
if getattr(book, '_new_book', False):
|
if getattr(book, '_new_book', False):
|
||||||
if prefs['preserve_user_collections']:
|
if prefs['manage_device_metadata'] == 'manual':
|
||||||
# Ensure that the book is in all the book's existing
|
# Ensure that the book is in all the book's existing
|
||||||
# collections plus all metadata collections
|
# collections plus all metadata collections
|
||||||
attrs += collection_attributes
|
attrs += collection_attributes
|
||||||
else:
|
else:
|
||||||
# The book's existing collections are ignored. Put the book
|
# For new books, both 'on_send' and 'on_connect' do the same
|
||||||
# in collections defined by its metadata.
|
# thing. The book's existing collections are ignored. Put
|
||||||
|
# the book in collections defined by its metadata.
|
||||||
attrs = collection_attributes
|
attrs = collection_attributes
|
||||||
|
elif prefs['manage_device_metadata'] == 'on_connect':
|
||||||
|
# For existing books, modify the collections only if the user
|
||||||
|
# specified 'on_connect'
|
||||||
|
attrs = collection_attributes
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
attr = attr.strip()
|
attr = attr.strip()
|
||||||
val = getattr(book, attr, None)
|
val = getattr(book, attr, None)
|
||||||
|
@ -47,8 +47,8 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
This class provides logic common to all drivers for devices that export themselves
|
This class provides logic common to all drivers for devices that export themselves
|
||||||
as USB Mass Storage devices. If you are writing such a driver, inherit from this
|
as USB Mass Storage devices. Provides implementations for mounting/ejecting
|
||||||
class.
|
of USBMS devices on all platforms.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
VENDOR_ID = 0x0
|
VENDOR_ID = 0x0
|
||||||
@ -57,9 +57,19 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
VENDOR_NAME = None
|
VENDOR_NAME = None
|
||||||
|
|
||||||
# These can be None, string, list of strings or compiled regex
|
#: String identifying the main memory of the device in the windows PnP id
|
||||||
|
#: strings
|
||||||
|
#: This can be None, string, list of strings or compiled regex
|
||||||
WINDOWS_MAIN_MEM = None
|
WINDOWS_MAIN_MEM = None
|
||||||
|
|
||||||
|
#: String identifying the first card of the device in the windows PnP id
|
||||||
|
#: strings
|
||||||
|
#: This can be None, string, list of strings or compiled regex
|
||||||
WINDOWS_CARD_A_MEM = None
|
WINDOWS_CARD_A_MEM = None
|
||||||
|
|
||||||
|
#: String identifying the second card of the device in the windows PnP id
|
||||||
|
#: strings
|
||||||
|
#: This can be None, string, list of strings or compiled regex
|
||||||
WINDOWS_CARD_B_MEM = None
|
WINDOWS_CARD_B_MEM = None
|
||||||
|
|
||||||
# The following are used by the check_ioreg_line method and can be either:
|
# The following are used by the check_ioreg_line method and can be either:
|
||||||
@ -68,9 +78,9 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
OSX_CARD_A_MEM = None
|
OSX_CARD_A_MEM = None
|
||||||
OSX_CARD_B_MEM = None
|
OSX_CARD_B_MEM = None
|
||||||
|
|
||||||
# Used by the new driver detection to disambiguate main memory from
|
#: Used by the new driver detection to disambiguate main memory from
|
||||||
# storage cards. Should be a regular expression that matches the
|
#: storage cards. Should be a regular expression that matches the
|
||||||
# main memory mount point assigned by OS X
|
#: main memory mount point assigned by OS X
|
||||||
OSX_MAIN_MEM_VOL_PAT = None
|
OSX_MAIN_MEM_VOL_PAT = None
|
||||||
OSX_EJECT_COMMAND = ['diskutil', 'eject']
|
OSX_EJECT_COMMAND = ['diskutil', 'eject']
|
||||||
|
|
||||||
@ -732,7 +742,7 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
|
self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
|
||||||
|
|
||||||
def get_main_ebook_dir(self):
|
def get_main_ebook_dir(self, for_upload=False):
|
||||||
return self.EBOOK_DIR_MAIN
|
return self.EBOOK_DIR_MAIN
|
||||||
|
|
||||||
def _sanity_check(self, on_card, files):
|
def _sanity_check(self, on_card, files):
|
||||||
@ -750,7 +760,7 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
path = os.path.join(self._card_b_prefix,
|
path = os.path.join(self._card_b_prefix,
|
||||||
*(self.EBOOK_DIR_CARD_B.split('/')))
|
*(self.EBOOK_DIR_CARD_B.split('/')))
|
||||||
else:
|
else:
|
||||||
candidates = self.get_main_ebook_dir()
|
candidates = self.get_main_ebook_dir(for_upload=True)
|
||||||
if isinstance(candidates, basestring):
|
if isinstance(candidates, basestring):
|
||||||
candidates = [candidates]
|
candidates = [candidates]
|
||||||
candidates = [
|
candidates = [
|
||||||
@ -780,7 +790,7 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
def filename_callback(self, default, mi):
|
def filename_callback(self, default, mi):
|
||||||
'''
|
'''
|
||||||
Callback to allow drivers to change the default file name
|
Callback to allow drivers to change the default file name
|
||||||
set by :method:`create_upload_path`.
|
set by :meth:`create_upload_path`.
|
||||||
'''
|
'''
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
@ -33,6 +33,10 @@ def debug_print(*args):
|
|||||||
# CLI must come before Device as it implements the CLI functions that
|
# CLI must come before Device as it implements the CLI functions that
|
||||||
# are inherited from the device interface in Device.
|
# are inherited from the device interface in Device.
|
||||||
class USBMS(CLI, Device):
|
class USBMS(CLI, Device):
|
||||||
|
'''
|
||||||
|
The base class for all USBMS devices. Implements the logic for
|
||||||
|
sending/getting/updating metadata/caching metadata/etc.
|
||||||
|
'''
|
||||||
|
|
||||||
description = _('Communicate with an eBook reader.')
|
description = _('Communicate with an eBook reader.')
|
||||||
author = _('John Schember')
|
author = _('John Schember')
|
||||||
@ -58,7 +62,7 @@ class USBMS(CLI, Device):
|
|||||||
|
|
||||||
debug_print ('USBMS: Fetching list of books from device. oncard=', oncard)
|
debug_print ('USBMS: Fetching list of books from device. oncard=', oncard)
|
||||||
|
|
||||||
dummy_bl = BookList(None, None, None)
|
dummy_bl = self.booklist_class(None, None, None)
|
||||||
|
|
||||||
if oncard == 'carda' and not self._card_a_prefix:
|
if oncard == 'carda' and not self._card_a_prefix:
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
self.report_progress(1.0, _('Getting list of books on device...'))
|
||||||
@ -78,6 +82,8 @@ class USBMS(CLI, Device):
|
|||||||
self.EBOOK_DIR_CARD_B if oncard == 'cardb' else \
|
self.EBOOK_DIR_CARD_B if oncard == 'cardb' else \
|
||||||
self.get_main_ebook_dir()
|
self.get_main_ebook_dir()
|
||||||
|
|
||||||
|
debug_print ('USBMS: dirs are:', prefix, ebook_dirs)
|
||||||
|
|
||||||
# get the metadata cache
|
# get the metadata cache
|
||||||
bl = self.booklist_class(oncard, prefix, self.settings)
|
bl = self.booklist_class(oncard, prefix, self.settings)
|
||||||
need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE)
|
need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE)
|
||||||
@ -193,10 +199,13 @@ class USBMS(CLI, Device):
|
|||||||
|
|
||||||
def upload_cover(self, path, filename, metadata):
|
def upload_cover(self, path, filename, metadata):
|
||||||
'''
|
'''
|
||||||
:path: the full path were the associated book is located.
|
Upload book cover to the device. Default implementation does nothing.
|
||||||
:filename: the name of the book file without the extension.
|
|
||||||
:metadata: metadata belonging to the book. Use metadata.thumbnail
|
:param path: the full path were the associated book is located.
|
||||||
for cover
|
:param filename: the name of the book file without the extension.
|
||||||
|
:param metadata: metadata belonging to the book. Use metadata.thumbnail
|
||||||
|
for cover
|
||||||
|
|
||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -177,6 +177,7 @@ class CHMInput(InputFormatPlugin):
|
|||||||
|
|
||||||
chapter_path = None
|
chapter_path = None
|
||||||
if match_string(node.tag, 'object') and match_string(node.attrib['type'], 'text/sitemap'):
|
if match_string(node.tag, 'object') and match_string(node.attrib['type'], 'text/sitemap'):
|
||||||
|
chapter_title = None
|
||||||
for child in node:
|
for child in node:
|
||||||
if match_string(child.tag,'param') and match_string(child.attrib['name'], 'name'):
|
if match_string(child.tag,'param') and match_string(child.attrib['name'], 'name'):
|
||||||
chapter_title = child.attrib['value']
|
chapter_title = child.attrib['value']
|
||||||
|
@ -8,7 +8,6 @@ Based on ideas from comiclrf created by FangornUK.
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
import os, shutil, traceback, textwrap, time, codecs
|
import os, shutil, traceback, textwrap, time, codecs
|
||||||
from ctypes import byref
|
|
||||||
from Queue import Empty
|
from Queue import Empty
|
||||||
|
|
||||||
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
|
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
|
||||||
@ -71,141 +70,119 @@ class PageProcessor(list):
|
|||||||
|
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
import calibre.utils.PythonMagickWand as pw
|
from calibre.utils.magick import Image
|
||||||
img = pw.NewMagickWand()
|
img = Image()
|
||||||
if img < 0:
|
img.open(self.path_to_page)
|
||||||
raise RuntimeError('Cannot create wand.')
|
width, height = img.size
|
||||||
if not pw.MagickReadImage(img, self.path_to_page):
|
|
||||||
severity = pw.ExceptionType(0)
|
|
||||||
msg = pw.MagickGetException(img, byref(severity))
|
|
||||||
raise IOError('Failed to read image from: %s: %s'
|
|
||||||
%(self.path_to_page, msg))
|
|
||||||
width = pw.MagickGetImageWidth(img)
|
|
||||||
height = pw.MagickGetImageHeight(img)
|
|
||||||
if self.num == 0: # First image so create a thumbnail from it
|
if self.num == 0: # First image so create a thumbnail from it
|
||||||
thumb = pw.CloneMagickWand(img)
|
thumb = img.clone
|
||||||
if thumb < 0:
|
thumb.thumbnail(60, 80)
|
||||||
raise RuntimeError('Cannot create wand.')
|
thumb.save(os.path.join(self.dest, 'thumbnail.png'))
|
||||||
pw.MagickThumbnailImage(thumb, 60, 80)
|
|
||||||
pw.MagickWriteImage(thumb, os.path.join(self.dest, 'thumbnail.png'))
|
|
||||||
pw.DestroyMagickWand(thumb)
|
|
||||||
self.pages = [img]
|
self.pages = [img]
|
||||||
if width > height:
|
if width > height:
|
||||||
if self.opts.landscape:
|
if self.opts.landscape:
|
||||||
self.rotate = True
|
self.rotate = True
|
||||||
else:
|
else:
|
||||||
split1, split2 = map(pw.CloneMagickWand, (img, img))
|
split1, split2 = img.clone, img.clone
|
||||||
pw.DestroyMagickWand(img)
|
half = int(width/2)
|
||||||
if split1 < 0 or split2 < 0:
|
split1.crop(half-1, height, 0, 0)
|
||||||
raise RuntimeError('Cannot create wand.')
|
split2.crop(half-1, height, half, 0)
|
||||||
pw.MagickCropImage(split1, (width/2)-1, height, 0, 0)
|
|
||||||
pw.MagickCropImage(split2, (width/2)-1, height, width/2, 0 )
|
|
||||||
self.pages = [split2, split1] if self.opts.right2left else [split1, split2]
|
self.pages = [split2, split1] if self.opts.right2left else [split1, split2]
|
||||||
self.process_pages()
|
self.process_pages()
|
||||||
|
|
||||||
def process_pages(self):
|
def process_pages(self):
|
||||||
import calibre.utils.PythonMagickWand as p
|
from calibre.utils.magick import PixelWand
|
||||||
for i, wand in enumerate(self.pages):
|
for i, wand in enumerate(self.pages):
|
||||||
pw = p.NewPixelWand()
|
pw = PixelWand()
|
||||||
try:
|
pw.color = 'white'
|
||||||
if pw < 0:
|
|
||||||
raise RuntimeError('Cannot create wand.')
|
|
||||||
p.PixelSetColor(pw, 'white')
|
|
||||||
|
|
||||||
p.MagickSetImageBorderColor(wand, pw)
|
wand.set_border_color(pw)
|
||||||
if self.rotate:
|
if self.rotate:
|
||||||
p.MagickRotateImage(wand, pw, -90)
|
wand.rotate(pw, -90)
|
||||||
|
|
||||||
# 25 percent fuzzy trim?
|
# 25 percent fuzzy trim?
|
||||||
if not self.opts.disable_trim:
|
if not self.opts.disable_trim:
|
||||||
p.MagickTrimImage(wand, 25*65535/100)
|
wand.trim(25*65535/100)
|
||||||
p.MagickSetImagePage(wand, 0,0,0,0) #Clear page after trim, like a "+repage"
|
wand.set_page(0, 0, 0, 0) #Clear page after trim, like a "+repage"
|
||||||
# Do the Photoshop "Auto Levels" equivalent
|
# Do the Photoshop "Auto Levels" equivalent
|
||||||
if not self.opts.dont_normalize:
|
if not self.opts.dont_normalize:
|
||||||
p.MagickNormalizeImage(wand)
|
wand.normalize()
|
||||||
sizex = p.MagickGetImageWidth(wand)
|
sizex, sizey = wand.size
|
||||||
sizey = p.MagickGetImageHeight(wand)
|
|
||||||
|
|
||||||
SCRWIDTH, SCRHEIGHT = self.opts.output_profile.comic_screen_size
|
SCRWIDTH, SCRHEIGHT = self.opts.output_profile.comic_screen_size
|
||||||
|
|
||||||
if self.opts.keep_aspect_ratio:
|
if self.opts.keep_aspect_ratio:
|
||||||
# Preserve the aspect ratio by adding border
|
# Preserve the aspect ratio by adding border
|
||||||
aspect = float(sizex) / float(sizey)
|
aspect = float(sizex) / float(sizey)
|
||||||
if aspect <= (float(SCRWIDTH) / float(SCRHEIGHT)):
|
if aspect <= (float(SCRWIDTH) / float(SCRHEIGHT)):
|
||||||
newsizey = SCRHEIGHT
|
newsizey = SCRHEIGHT
|
||||||
newsizex = int(newsizey * aspect)
|
newsizex = int(newsizey * aspect)
|
||||||
deltax = (SCRWIDTH - newsizex) / 2
|
deltax = (SCRWIDTH - newsizex) / 2
|
||||||
deltay = 0
|
deltay = 0
|
||||||
else:
|
|
||||||
newsizex = SCRWIDTH
|
|
||||||
newsizey = int(newsizex / aspect)
|
|
||||||
deltax = 0
|
|
||||||
deltay = (SCRHEIGHT - newsizey) / 2
|
|
||||||
p.MagickResizeImage(wand, newsizex, newsizey, p.CatromFilter, 1.0)
|
|
||||||
p.MagickSetImageBorderColor(wand, pw)
|
|
||||||
p.MagickBorderImage(wand, pw, deltax, deltay)
|
|
||||||
elif self.opts.wide:
|
|
||||||
# Keep aspect and Use device height as scaled image width so landscape mode is clean
|
|
||||||
aspect = float(sizex) / float(sizey)
|
|
||||||
screen_aspect = float(SCRWIDTH) / float(SCRHEIGHT)
|
|
||||||
# Get dimensions of the landscape mode screen
|
|
||||||
# Add 25px back to height for the battery bar.
|
|
||||||
wscreenx = SCRHEIGHT + 25
|
|
||||||
wscreeny = int(wscreenx / screen_aspect)
|
|
||||||
if aspect <= screen_aspect:
|
|
||||||
newsizey = wscreeny
|
|
||||||
newsizex = int(newsizey * aspect)
|
|
||||||
deltax = (wscreenx - newsizex) / 2
|
|
||||||
deltay = 0
|
|
||||||
else:
|
|
||||||
newsizex = wscreenx
|
|
||||||
newsizey = int(newsizex / aspect)
|
|
||||||
deltax = 0
|
|
||||||
deltay = (wscreeny - newsizey) / 2
|
|
||||||
p.MagickResizeImage(wand, newsizex, newsizey, p.CatromFilter, 1.0)
|
|
||||||
p.MagickSetImageBorderColor(wand, pw)
|
|
||||||
p.MagickBorderImage(wand, pw, deltax, deltay)
|
|
||||||
else:
|
else:
|
||||||
p.MagickResizeImage(wand, SCRWIDTH, SCRHEIGHT, p.CatromFilter, 1.0)
|
newsizex = SCRWIDTH
|
||||||
|
newsizey = int(newsizex / aspect)
|
||||||
|
deltax = 0
|
||||||
|
deltay = (SCRHEIGHT - newsizey) / 2
|
||||||
|
wand.size = (newsizex, newsizey)
|
||||||
|
wand.set_border_color(pw)
|
||||||
|
wand.add_border(pw, deltax, deltay)
|
||||||
|
elif self.opts.wide:
|
||||||
|
# Keep aspect and Use device height as scaled image width so landscape mode is clean
|
||||||
|
aspect = float(sizex) / float(sizey)
|
||||||
|
screen_aspect = float(SCRWIDTH) / float(SCRHEIGHT)
|
||||||
|
# Get dimensions of the landscape mode screen
|
||||||
|
# Add 25px back to height for the battery bar.
|
||||||
|
wscreenx = SCRHEIGHT + 25
|
||||||
|
wscreeny = int(wscreenx / screen_aspect)
|
||||||
|
if aspect <= screen_aspect:
|
||||||
|
newsizey = wscreeny
|
||||||
|
newsizex = int(newsizey * aspect)
|
||||||
|
deltax = (wscreenx - newsizex) / 2
|
||||||
|
deltay = 0
|
||||||
|
else:
|
||||||
|
newsizex = wscreenx
|
||||||
|
newsizey = int(newsizex / aspect)
|
||||||
|
deltax = 0
|
||||||
|
deltay = (wscreeny - newsizey) / 2
|
||||||
|
wand.size = (newsizex, newsizey)
|
||||||
|
wand.set_border_color(pw)
|
||||||
|
wand.add_border(pw, deltax, deltay)
|
||||||
|
else:
|
||||||
|
wand.size = (SCRWIDTH, SCRHEIGHT)
|
||||||
|
|
||||||
if not self.opts.dont_sharpen:
|
if not self.opts.dont_sharpen:
|
||||||
p.MagickSharpenImage(wand, 0.0, 1.0)
|
wand.sharpen(0.0, 1.0)
|
||||||
|
|
||||||
if not self.opts.dont_grayscale:
|
if not self.opts.dont_grayscale:
|
||||||
p.MagickSetImageType(wand, p.GrayscaleType)
|
wand.type = 'GrayscaleType'
|
||||||
|
|
||||||
if self.opts.despeckle:
|
if self.opts.despeckle:
|
||||||
p.MagickDespeckleImage(wand)
|
wand.despeckle()
|
||||||
|
|
||||||
p.MagickQuantizeImage(wand, self.opts.colors, p.RGBColorspace, 0, 1, 0)
|
wand.quantize(self.opts.colors)
|
||||||
dest = '%d_%d.%s'%(self.num, i, self.opts.output_format)
|
dest = '%d_%d.%s'%(self.num, i, self.opts.output_format)
|
||||||
dest = os.path.join(self.dest, dest)
|
dest = os.path.join(self.dest, dest)
|
||||||
p.MagickWriteImage(wand, dest+'8')
|
wand.save(dest+'8')
|
||||||
os.rename(dest+'8', dest)
|
os.rename(dest+'8', dest)
|
||||||
self.append(dest)
|
self.append(dest)
|
||||||
finally:
|
|
||||||
if pw > 0:
|
|
||||||
p.DestroyPixelWand(pw)
|
|
||||||
p.DestroyMagickWand(wand)
|
|
||||||
|
|
||||||
def render_pages(tasks, dest, opts, notification=lambda x, y: x):
|
def render_pages(tasks, dest, opts, notification=lambda x, y: x):
|
||||||
'''
|
'''
|
||||||
Entry point for the job server.
|
Entry point for the job server.
|
||||||
'''
|
'''
|
||||||
failures, pages = [], []
|
failures, pages = [], []
|
||||||
from calibre.utils.PythonMagickWand import ImageMagick
|
for num, path in tasks:
|
||||||
with ImageMagick():
|
try:
|
||||||
for num, path in tasks:
|
pages.extend(PageProcessor(path, dest, opts, num))
|
||||||
try:
|
msg = _('Rendered %s')%path
|
||||||
pages.extend(PageProcessor(path, dest, opts, num))
|
except:
|
||||||
msg = _('Rendered %s')%path
|
failures.append(path)
|
||||||
except:
|
msg = _('Failed %s')%path
|
||||||
failures.append(path)
|
if opts.verbose:
|
||||||
msg = _('Failed %s')%path
|
msg += '\n' + traceback.format_exc()
|
||||||
if opts.verbose:
|
prints(msg)
|
||||||
msg += '\n' + traceback.format_exc()
|
notification(0.5, msg)
|
||||||
prints(msg)
|
|
||||||
notification(0.5, msg)
|
|
||||||
|
|
||||||
return pages, failures
|
return pages, failures
|
||||||
|
|
||||||
@ -226,9 +203,6 @@ def process_pages(pages, opts, update, tdir):
|
|||||||
'''
|
'''
|
||||||
Render all identified comic pages.
|
Render all identified comic pages.
|
||||||
'''
|
'''
|
||||||
from calibre.utils.PythonMagickWand import ImageMagick
|
|
||||||
ImageMagick
|
|
||||||
|
|
||||||
progress = Progress(len(pages), update)
|
progress = Progress(len(pages), update)
|
||||||
server = Server()
|
server = Server()
|
||||||
jobs = []
|
jobs = []
|
||||||
|
@ -5,7 +5,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, posixpath, urllib, sys
|
import os, posixpath, urllib, sys, re
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
@ -160,8 +160,26 @@ class Container(object):
|
|||||||
mt = mimetype.lower()
|
mt = mimetype.lower()
|
||||||
if mt.endswith('+xml'):
|
if mt.endswith('+xml'):
|
||||||
parser = etree.XMLParser(no_network=True, huge_tree=not iswindows)
|
parser = etree.XMLParser(no_network=True, huge_tree=not iswindows)
|
||||||
return etree.fromstring(xml_to_unicode(raw,
|
raw = xml_to_unicode(raw,
|
||||||
strip_encoding_pats=True, assume_utf8=True)[0], parser=parser)
|
strip_encoding_pats=True, assume_utf8=True,
|
||||||
|
resolve_entities=True)[0].strip()
|
||||||
|
idx = raw.find('<html')
|
||||||
|
if idx == -1:
|
||||||
|
idx = raw.find('<HTML')
|
||||||
|
if idx > -1:
|
||||||
|
pre = raw[:idx]
|
||||||
|
raw = raw[idx:]
|
||||||
|
if '<!DOCTYPE' in pre:
|
||||||
|
user_entities = {}
|
||||||
|
for match in re.finditer(r'<!ENTITY\s+(\S+)\s+([^>]+)', pre):
|
||||||
|
val = match.group(2)
|
||||||
|
if val.startswith('"') and val.endswith('"'):
|
||||||
|
val = val[1:-1]
|
||||||
|
user_entities[match.group(1)] = val
|
||||||
|
if user_entities:
|
||||||
|
pat = re.compile(r'&(%s);'%('|'.join(user_entities.keys())))
|
||||||
|
raw = pat.sub(lambda m:user_entities[m.group(1)], raw)
|
||||||
|
return etree.fromstring(raw, parser=parser)
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
def write(self, path):
|
def write(self, path):
|
||||||
|
@ -21,7 +21,7 @@ class Epubcheck(ePubFixer):
|
|||||||
def long_description(self):
|
def long_description(self):
|
||||||
return _('Workarounds for bugs in the latest release of epubcheck. '
|
return _('Workarounds for bugs in the latest release of epubcheck. '
|
||||||
'epubcheck reports many things as errors that are not '
|
'epubcheck reports many things as errors that are not '
|
||||||
'actually errors. %prog will try to detect these and replace '
|
'actually errors. epub-fix will try to detect these and replace '
|
||||||
'them with constructs that epubcheck likes. This may cause '
|
'them with constructs that epubcheck likes. This may cause '
|
||||||
'significant changes to your epub, complain to the epubcheck '
|
'significant changes to your epub, complain to the epubcheck '
|
||||||
'project.')
|
'project.')
|
||||||
|
@ -18,7 +18,7 @@ class Unmanifested(ePubFixer):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def long_description(self):
|
def long_description(self):
|
||||||
return _('Fix unmanifested files. %prog can either add them to '
|
return _('Fix unmanifested files. epub-fix can either add them to '
|
||||||
'the manifest or delete them as specified by the '
|
'the manifest or delete them as specified by the '
|
||||||
'delete unmanifested option.')
|
'delete unmanifested option.')
|
||||||
|
|
||||||
|
@ -60,6 +60,9 @@ class FB2Input(InputFormatPlugin):
|
|||||||
|
|
||||||
transform = etree.XSLT(styledoc)
|
transform = etree.XSLT(styledoc)
|
||||||
result = transform(doc)
|
result = transform(doc)
|
||||||
|
for img in result.xpath('//img[@src]'):
|
||||||
|
src = img.get('src')
|
||||||
|
img.set('src', self.binary_map.get(src, src))
|
||||||
open('index.xhtml', 'wb').write(transform.tostring(result))
|
open('index.xhtml', 'wb').write(transform.tostring(result))
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
mi = get_metadata(stream, 'fb2')
|
mi = get_metadata(stream, 'fb2')
|
||||||
@ -83,9 +86,15 @@ class FB2Input(InputFormatPlugin):
|
|||||||
return os.path.join(os.getcwd(), 'metadata.opf')
|
return os.path.join(os.getcwd(), 'metadata.opf')
|
||||||
|
|
||||||
def extract_embedded_content(self, doc):
|
def extract_embedded_content(self, doc):
|
||||||
|
self.binary_map = {}
|
||||||
for elem in doc.xpath('./*'):
|
for elem in doc.xpath('./*'):
|
||||||
if 'binary' in elem.tag and elem.attrib.has_key('id'):
|
if 'binary' in elem.tag and elem.attrib.has_key('id'):
|
||||||
|
ct = elem.get('content-type', '')
|
||||||
fname = elem.attrib['id']
|
fname = elem.attrib['id']
|
||||||
|
ext = ct.rpartition('/')[-1].lower()
|
||||||
|
if ext in ('png', 'jpeg', 'jpg'):
|
||||||
|
fname += '.' + ext
|
||||||
|
self.binary_map[elem.get('id')] = fname
|
||||||
data = b64decode(elem.text.strip())
|
data = b64decode(elem.text.strip())
|
||||||
open(fname, 'wb').write(data)
|
open(fname, 'wb').write(data)
|
||||||
|
|
||||||
|
@ -368,7 +368,15 @@ class LRFInput(InputFormatPlugin):
|
|||||||
if options.verbose > 2:
|
if options.verbose > 2:
|
||||||
open('lrs.xml', 'wb').write(xml.encode('utf-8'))
|
open('lrs.xml', 'wb').write(xml.encode('utf-8'))
|
||||||
parser = etree.XMLParser(no_network=True, huge_tree=True)
|
parser = etree.XMLParser(no_network=True, huge_tree=True)
|
||||||
doc = etree.fromstring(xml, parser=parser)
|
try:
|
||||||
|
doc = etree.fromstring(xml, parser=parser)
|
||||||
|
except:
|
||||||
|
self.log.warn('Failed to parse XML. Trying to recover')
|
||||||
|
parser = etree.XMLParser(no_network=True, huge_tree=True,
|
||||||
|
recover=True)
|
||||||
|
doc = etree.fromstring(xml, parser=parser)
|
||||||
|
|
||||||
|
|
||||||
char_button_map = {}
|
char_button_map = {}
|
||||||
for x in doc.xpath('//CharButton[@refobj]'):
|
for x in doc.xpath('//CharButton[@refobj]'):
|
||||||
ro = x.get('refobj')
|
ro = x.get('refobj')
|
||||||
|
@ -46,6 +46,7 @@ def authors_to_sort_string(authors):
|
|||||||
return ' & '.join(map(author_to_author_sort, authors))
|
return ' & '.join(map(author_to_author_sort, authors))
|
||||||
|
|
||||||
_title_pat = re.compile('^(A|The|An)\s+', re.IGNORECASE)
|
_title_pat = re.compile('^(A|The|An)\s+', re.IGNORECASE)
|
||||||
|
|
||||||
def title_sort(title):
|
def title_sort(title):
|
||||||
match = _title_pat.search(title)
|
match = _title_pat.search(title)
|
||||||
if match:
|
if match:
|
||||||
@ -268,10 +269,12 @@ class MetaInformation(object):
|
|||||||
):
|
):
|
||||||
prints(x, getattr(self, x, 'None'))
|
prints(x, getattr(self, x, 'None'))
|
||||||
|
|
||||||
def smart_update(self, mi, replace_tags=False):
|
def smart_update(self, mi, replace_metadata=False):
|
||||||
'''
|
'''
|
||||||
Merge the information in C{mi} into self. In case of conflicts, the information
|
Merge the information in C{mi} into self. In case of conflicts, the
|
||||||
in C{mi} takes precedence, unless the information in mi is NULL.
|
information in C{mi} takes precedence, unless the information in mi is
|
||||||
|
NULL. If replace_metadata is True, then the information in mi always
|
||||||
|
takes precedence.
|
||||||
'''
|
'''
|
||||||
if mi.title and mi.title != _('Unknown'):
|
if mi.title and mi.title != _('Unknown'):
|
||||||
self.title = mi.title
|
self.title = mi.title
|
||||||
@ -285,16 +288,18 @@ class MetaInformation(object):
|
|||||||
'cover', 'guide', 'book_producer',
|
'cover', 'guide', 'book_producer',
|
||||||
'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', 'rights',
|
'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', 'rights',
|
||||||
'publication_type', 'uuid'):
|
'publication_type', 'uuid'):
|
||||||
if hasattr(mi, attr):
|
if replace_metadata:
|
||||||
|
setattr(self, attr, getattr(mi, attr, 1.0 if \
|
||||||
|
attr == 'series_index' else None))
|
||||||
|
elif hasattr(mi, attr):
|
||||||
val = getattr(mi, attr)
|
val = getattr(mi, attr)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
setattr(self, attr, val)
|
setattr(self, attr, val)
|
||||||
|
|
||||||
if mi.tags:
|
if replace_metadata:
|
||||||
if replace_tags:
|
self.tags = mi.tags
|
||||||
self.tags = mi.tags
|
elif mi.tags:
|
||||||
else:
|
self.tags += mi.tags
|
||||||
self.tags += mi.tags
|
|
||||||
self.tags = list(set(self.tags))
|
self.tags = list(set(self.tags))
|
||||||
|
|
||||||
if mi.author_sort_map:
|
if mi.author_sort_map:
|
||||||
@ -308,14 +313,17 @@ class MetaInformation(object):
|
|||||||
if len(other_cover) > len(self_cover):
|
if len(other_cover) > len(self_cover):
|
||||||
self.cover_data = mi.cover_data
|
self.cover_data = mi.cover_data
|
||||||
|
|
||||||
my_comments = getattr(self, 'comments', '')
|
if replace_metadata:
|
||||||
other_comments = getattr(mi, 'comments', '')
|
self.comments = getattr(mi, 'comments', '')
|
||||||
if not my_comments:
|
else:
|
||||||
my_comments = ''
|
my_comments = getattr(self, 'comments', '')
|
||||||
if not other_comments:
|
other_comments = getattr(mi, 'comments', '')
|
||||||
other_comments = ''
|
if not my_comments:
|
||||||
if len(other_comments.strip()) > len(my_comments.strip()):
|
my_comments = ''
|
||||||
self.comments = other_comments
|
if not other_comments:
|
||||||
|
other_comments = ''
|
||||||
|
if len(other_comments.strip()) > len(my_comments.strip()):
|
||||||
|
self.comments = other_comments
|
||||||
|
|
||||||
other_lang = getattr(mi, 'language', None)
|
other_lang = getattr(mi, 'language', None)
|
||||||
if other_lang and other_lang.lower() != 'und':
|
if other_lang and other_lang.lower() != 'und':
|
||||||
|
@ -82,7 +82,7 @@ class Metadata(object):
|
|||||||
def print_all_attributes(self):
|
def print_all_attributes(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def smart_update(self, other):
|
def smart_update(self, other, replace_metadata=False):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def format_series_index(self):
|
def format_series_index(self):
|
||||||
|
@ -5,11 +5,253 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import traceback, socket, re, sys
|
||||||
|
from functools import partial
|
||||||
|
from threading import Thread, Event
|
||||||
|
from Queue import Queue, Empty
|
||||||
|
|
||||||
|
import mechanize
|
||||||
|
|
||||||
from calibre.customize import Plugin
|
from calibre.customize import Plugin
|
||||||
|
from calibre import browser, prints
|
||||||
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
from calibre.constants import preferred_encoding, DEBUG
|
||||||
|
|
||||||
class CoverDownload(Plugin):
|
class CoverDownload(Plugin):
|
||||||
|
'''
|
||||||
|
These plugins are used to download covers for books.
|
||||||
|
'''
|
||||||
|
|
||||||
supported_platforms = ['windows', 'osx', 'linux']
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
type = _('Cover download')
|
type = _('Cover download')
|
||||||
|
|
||||||
|
def has_cover(self, mi, ans, timeout=5.):
|
||||||
|
'''
|
||||||
|
Check if the book described by mi has a cover. Call ans.set() if it
|
||||||
|
does. Do nothing if it doesn't.
|
||||||
|
|
||||||
|
:param mi: MetaInformation object
|
||||||
|
:param timeout: timeout in seconds
|
||||||
|
:param ans: A threading.Event object
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def get_covers(self, mi, result_queue, abort, timeout=5.):
|
||||||
|
'''
|
||||||
|
Download covers for books described by the mi object. Downloaded covers
|
||||||
|
must be put into the result_queue. If more than one cover is available,
|
||||||
|
the plugin should continue downloading them and putting them into
|
||||||
|
result_queue until abort.is_set() returns True.
|
||||||
|
|
||||||
|
:param mi: MetaInformation object
|
||||||
|
:param result_queue: A multithreaded Queue
|
||||||
|
:param abort: A threading.Event object
|
||||||
|
:param timeout: timeout in seconds
|
||||||
|
'''
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def exception_to_string(self, ex):
|
||||||
|
try:
|
||||||
|
return unicode(ex)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
return str(ex).decode(preferred_encoding, 'replace')
|
||||||
|
except:
|
||||||
|
return repr(ex)
|
||||||
|
|
||||||
|
def debug(self, *args, **kwargs):
|
||||||
|
if DEBUG:
|
||||||
|
prints('\t'+self.name+':', *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HeadRequest(mechanize.Request):
|
||||||
|
|
||||||
|
def get_method(self):
|
||||||
|
return 'HEAD'
|
||||||
|
|
||||||
|
class OpenLibraryCovers(CoverDownload): # {{{
|
||||||
|
'Download covers from openlibrary.org'
|
||||||
|
|
||||||
|
OPENLIBRARY = 'http://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false'
|
||||||
|
name = 'openlibrary.org covers'
|
||||||
|
description = _('Download covers from openlibrary.org')
|
||||||
|
author = 'Kovid Goyal'
|
||||||
|
|
||||||
|
def has_cover(self, mi, ans, timeout=5.):
|
||||||
|
if not mi.isbn:
|
||||||
|
return False
|
||||||
|
br = browser()
|
||||||
|
br.set_handle_redirect(False)
|
||||||
|
try:
|
||||||
|
br.open_novisit(HeadRequest(self.OPENLIBRARY%mi.isbn), timeout=timeout)
|
||||||
|
self.debug('cover for', mi.isbn, 'found')
|
||||||
|
ans.set()
|
||||||
|
except Exception, e:
|
||||||
|
if callable(getattr(e, 'getcode', None)) and e.getcode() == 302:
|
||||||
|
self.debug('cover for', mi.isbn, 'found')
|
||||||
|
ans.set()
|
||||||
|
else:
|
||||||
|
self.debug(e)
|
||||||
|
|
||||||
|
def get_covers(self, mi, result_queue, abort, timeout=5.):
|
||||||
|
if not mi.isbn:
|
||||||
|
return
|
||||||
|
br = browser()
|
||||||
|
try:
|
||||||
|
ans = br.open(self.OPENLIBRARY%mi.isbn, timeout=timeout).read()
|
||||||
|
result_queue.put((True, ans, 'jpg', self.name))
|
||||||
|
except Exception, e:
|
||||||
|
if callable(getattr(e, 'getcode', None)) and e.getcode() == 404:
|
||||||
|
result_queue.put((False, _('ISBN: %s not found')%mi.isbn, '', self.name))
|
||||||
|
else:
|
||||||
|
result_queue.put((False, self.exception_to_string(e),
|
||||||
|
traceback.format_exc(), self.name))
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class LibraryThingCovers(CoverDownload): # {{{
|
||||||
|
|
||||||
|
name = 'librarything.com covers'
|
||||||
|
description = _('Download covers from librarything.com')
|
||||||
|
author = 'Kovid Goyal'
|
||||||
|
|
||||||
|
LIBRARYTHING = 'http://www.librarything.com/isbn/'
|
||||||
|
|
||||||
|
def get_cover_url(self, isbn, br, timeout=5.):
|
||||||
|
try:
|
||||||
|
src = br.open_novisit('http://www.librarything.com/isbn/'+isbn,
|
||||||
|
timeout=timeout).read().decode('utf-8', 'replace')
|
||||||
|
except Exception, err:
|
||||||
|
if isinstance(getattr(err, 'args', [None])[0], socket.timeout):
|
||||||
|
err = Exception(_('LibraryThing.com timed out. Try again later.'))
|
||||||
|
raise err
|
||||||
|
else:
|
||||||
|
s = BeautifulSoup(src)
|
||||||
|
url = s.find('td', attrs={'class':'left'})
|
||||||
|
if url is None:
|
||||||
|
if s.find('div', attrs={'class':'highloadwarning'}) is not None:
|
||||||
|
raise Exception(_('Could not fetch cover as server is experiencing high load. Please try again later.'))
|
||||||
|
raise Exception(_('ISBN: %s not found')%isbn)
|
||||||
|
url = url.find('img')
|
||||||
|
if url is None:
|
||||||
|
raise Exception(_('LibraryThing.com server error. Try again later.'))
|
||||||
|
url = re.sub(r'_S[XY]\d+', '', url['src'])
|
||||||
|
return url
|
||||||
|
|
||||||
|
def has_cover(self, mi, ans, timeout=5.):
|
||||||
|
if not mi.isbn:
|
||||||
|
return False
|
||||||
|
br = browser()
|
||||||
|
try:
|
||||||
|
self.get_cover_url(mi.isbn, br, timeout=timeout)
|
||||||
|
self.debug('cover for', mi.isbn, 'found')
|
||||||
|
ans.set()
|
||||||
|
except Exception, e:
|
||||||
|
self.debug(e)
|
||||||
|
|
||||||
|
def get_covers(self, mi, result_queue, abort, timeout=5.):
|
||||||
|
if not mi.isbn:
|
||||||
|
return
|
||||||
|
br = browser()
|
||||||
|
try:
|
||||||
|
url = self.get_cover_url(mi.isbn, br, timeout=timeout)
|
||||||
|
cover_data = br.open_novisit(url).read()
|
||||||
|
result_queue.put((True, cover_data, 'jpg', self.name))
|
||||||
|
except Exception, e:
|
||||||
|
result_queue.put((False, self.exception_to_string(e),
|
||||||
|
traceback.format_exc(), self.name))
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
def check_for_cover(mi, timeout=5.): # {{{
|
||||||
|
from calibre.customize.ui import cover_sources
|
||||||
|
ans = Event()
|
||||||
|
checkers = [partial(p.has_cover, mi, ans, timeout=timeout) for p in
|
||||||
|
cover_sources()]
|
||||||
|
workers = [Thread(target=c) for c in checkers]
|
||||||
|
for w in workers:
|
||||||
|
w.daemon = True
|
||||||
|
w.start()
|
||||||
|
while not ans.is_set():
|
||||||
|
ans.wait(0.1)
|
||||||
|
if sum([int(w.is_alive()) for w in workers]) == 0:
|
||||||
|
break
|
||||||
|
return ans.is_set()
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
def download_covers(mi, result_queue, max_covers=50, timeout=5.): # {{{
|
||||||
|
from calibre.customize.ui import cover_sources
|
||||||
|
abort = Event()
|
||||||
|
temp = Queue()
|
||||||
|
getters = [partial(p.get_covers, mi, temp, abort, timeout=timeout) for p in
|
||||||
|
cover_sources()]
|
||||||
|
workers = [Thread(target=c) for c in getters]
|
||||||
|
for w in workers:
|
||||||
|
w.daemon = True
|
||||||
|
w.start()
|
||||||
|
count = 0
|
||||||
|
while count < max_covers:
|
||||||
|
try:
|
||||||
|
result = temp.get_nowait()
|
||||||
|
if result[0]:
|
||||||
|
count += 1
|
||||||
|
result_queue.put(result)
|
||||||
|
except Empty:
|
||||||
|
pass
|
||||||
|
if sum([int(w.is_alive()) for w in workers]) == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
abort.set()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
result = temp.get_nowait()
|
||||||
|
count += 1
|
||||||
|
result_queue.put(result)
|
||||||
|
except Empty:
|
||||||
|
break
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
def download_cover(mi, timeout=5.): # {{{
|
||||||
|
results = Queue()
|
||||||
|
download_covers(mi, results, max_covers=1, timeout=timeout)
|
||||||
|
errors, ans = [], None
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
x = results.get_nowait()
|
||||||
|
if x[0]:
|
||||||
|
ans = x[1]
|
||||||
|
else:
|
||||||
|
errors.append(x)
|
||||||
|
except Empty:
|
||||||
|
break
|
||||||
|
return ans, errors
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
def test(isbns): # {{{
|
||||||
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
|
mi = MetaInformation('test', ['test'])
|
||||||
|
for isbn in isbns:
|
||||||
|
prints('Testing ISBN:', isbn)
|
||||||
|
mi.isbn = isbn
|
||||||
|
found = check_for_cover(mi)
|
||||||
|
prints('Has cover:', found)
|
||||||
|
ans, errors = download_cover(mi)
|
||||||
|
if ans is not None:
|
||||||
|
prints('Cover downloaded')
|
||||||
|
else:
|
||||||
|
prints('Download failed:')
|
||||||
|
for err in errors:
|
||||||
|
prints('\t', err[-1]+':', err[1])
|
||||||
|
print '\n'
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
isbns = sys.argv[1:] + ['9781591025412', '9780307272119']
|
||||||
|
test(isbns)
|
||||||
|
@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
'''Read meta information from epub files'''
|
'''Read meta information from epub files'''
|
||||||
|
|
||||||
import os, re
|
import os, re, posixpath, shutil
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ from calibre.utils.zipfile import ZipFile, BadZipfile, safe_replace
|
|||||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
from calibre.ebooks.metadata.opf2 import OPF
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory, PersistentTemporaryFile
|
||||||
from calibre import CurrentDir
|
from calibre import CurrentDir
|
||||||
|
|
||||||
class EPubException(Exception):
|
class EPubException(Exception):
|
||||||
@ -126,7 +126,6 @@ class OCFDirReader(OCFReader):
|
|||||||
return open(os.path.join(self.root, path), *args, **kwargs)
|
return open(os.path.join(self.root, path), *args, **kwargs)
|
||||||
|
|
||||||
def get_cover(opf, opf_path, stream, reader=None):
|
def get_cover(opf, opf_path, stream, reader=None):
|
||||||
import posixpath
|
|
||||||
from calibre.ebooks import render_html_svg_workaround
|
from calibre.ebooks import render_html_svg_workaround
|
||||||
from calibre.utils.logging import default_log
|
from calibre.utils.logging import default_log
|
||||||
raster_cover = opf.raster_cover
|
raster_cover = opf.raster_cover
|
||||||
@ -185,7 +184,45 @@ def get_quick_metadata(stream):
|
|||||||
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
reader = OCFZipReader(stream, root=os.getcwdu())
|
reader = OCFZipReader(stream, root=os.getcwdu())
|
||||||
|
raster_cover = reader.opf.raster_cover
|
||||||
mi = MetaInformation(mi)
|
mi = MetaInformation(mi)
|
||||||
|
new_cdata = None
|
||||||
|
replacements = {}
|
||||||
|
try:
|
||||||
|
new_cdata = mi.cover_data[1]
|
||||||
|
if not new_cdata:
|
||||||
|
raise Exception('no cover')
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
new_cdata = open(mi.cover, 'rb').read()
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if new_cdata and raster_cover:
|
||||||
|
try:
|
||||||
|
cpath = posixpath.join(posixpath.dirname(reader.opf_path),
|
||||||
|
raster_cover)
|
||||||
|
cover_replacable = not reader.encryption_meta.is_encrypted(cpath) and \
|
||||||
|
os.path.splitext(cpath)[1].lower() in ('.png', '.jpg', '.jpeg')
|
||||||
|
if cover_replacable:
|
||||||
|
from calibre.utils.magick.draw import save_cover_data_to, \
|
||||||
|
identify
|
||||||
|
new_cover = PersistentTemporaryFile(suffix=os.path.splitext(cpath)[1])
|
||||||
|
resize_to = None
|
||||||
|
if False: # Resize new cover to same size as old cover
|
||||||
|
shutil.copyfileobj(reader.open(cpath), new_cover)
|
||||||
|
new_cover.close()
|
||||||
|
width, height, fmt = identify(new_cover.name)
|
||||||
|
resize_to = (width, height)
|
||||||
|
else:
|
||||||
|
new_cover.close()
|
||||||
|
save_cover_data_to(new_cdata, new_cover.name,
|
||||||
|
resize_to=resize_to)
|
||||||
|
replacements[cpath] = open(new_cover.name, 'rb')
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
for x in ('guide', 'toc', 'manifest', 'spine'):
|
for x in ('guide', 'toc', 'manifest', 'spine'):
|
||||||
setattr(mi, x, None)
|
setattr(mi, x, None)
|
||||||
reader.opf.smart_update(mi)
|
reader.opf.smart_update(mi)
|
||||||
@ -200,5 +237,6 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
|||||||
reader.opf.timestamp = mi.timestamp
|
reader.opf.timestamp = mi.timestamp
|
||||||
|
|
||||||
newopf = StringIO(reader.opf.render())
|
newopf = StringIO(reader.opf.render())
|
||||||
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf)
|
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf,
|
||||||
|
extra_replacements=replacements)
|
||||||
|
|
||||||
|
@ -10,11 +10,27 @@ from calibre import prints
|
|||||||
from calibre.utils.config import OptionParser
|
from calibre.utils.config import OptionParser
|
||||||
from calibre.utils.logging import default_log
|
from calibre.utils.logging import default_log
|
||||||
from calibre.customize import Plugin
|
from calibre.customize import Plugin
|
||||||
from calibre.ebooks.metadata.library_thing import check_for_cover
|
from calibre.ebooks.metadata.covers import check_for_cover
|
||||||
|
|
||||||
metadata_config = None
|
metadata_config = None
|
||||||
|
|
||||||
class MetadataSource(Plugin): # {{{
|
class MetadataSource(Plugin): # {{{
|
||||||
|
'''
|
||||||
|
Represents a source to query for metadata. Subclasses must implement
|
||||||
|
at least the fetch method.
|
||||||
|
|
||||||
|
When :meth:`fetch` is called, the `self` object will have the following
|
||||||
|
useful attributes (each of which may be None)::
|
||||||
|
|
||||||
|
title, book_author, publisher, isbn, log, verbose and extra
|
||||||
|
|
||||||
|
Use these attributes to construct the search query. extra is reserved for
|
||||||
|
future use.
|
||||||
|
|
||||||
|
The fetch method must store the results in `self.results` as a list of
|
||||||
|
:class:`MetaInformation` objects. If there is an error, it should be stored
|
||||||
|
in `self.exception` and `self.tb` (for the traceback).
|
||||||
|
'''
|
||||||
|
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
|
|
||||||
@ -273,11 +289,10 @@ def filter_metadata_results(item):
|
|||||||
|
|
||||||
def do_cover_check(item):
|
def do_cover_check(item):
|
||||||
item.has_cover = False
|
item.has_cover = False
|
||||||
if item.isbn:
|
try:
|
||||||
try:
|
item.has_cover = check_for_cover(item)
|
||||||
item.has_cover = check_for_cover(item.isbn)
|
except:
|
||||||
except:
|
pass # Cover not found
|
||||||
pass # Cover not found
|
|
||||||
|
|
||||||
def check_for_covers(items):
|
def check_for_covers(items):
|
||||||
threads = [Thread(target=do_cover_check, args=(item,)) for item in items]
|
threads = [Thread(target=do_cover_check, args=(item,)) for item in items]
|
||||||
|
@ -12,11 +12,15 @@ import re
|
|||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
from calibre.ebooks.chardet import xml_to_unicode
|
from calibre.ebooks.chardet import xml_to_unicode
|
||||||
from calibre import entity_to_unicode
|
from calibre import entity_to_unicode
|
||||||
|
from calibre.utils.date import parse_date
|
||||||
|
|
||||||
def get_metadata(stream):
|
def get_metadata(stream):
|
||||||
src = stream.read()
|
src = stream.read()
|
||||||
return get_metadata_(src)
|
return get_metadata_(src)
|
||||||
|
|
||||||
|
def get_meta_regexp_(name):
|
||||||
|
return re.compile('<meta name=[\'"]' + name + '[\'"] content=[\'"](.+?)[\'"]\s*/?>', re.IGNORECASE)
|
||||||
|
|
||||||
def get_metadata_(src, encoding=None):
|
def get_metadata_(src, encoding=None):
|
||||||
if not isinstance(src, unicode):
|
if not isinstance(src, unicode):
|
||||||
if not encoding:
|
if not encoding:
|
||||||
@ -24,6 +28,9 @@ def get_metadata_(src, encoding=None):
|
|||||||
else:
|
else:
|
||||||
src = src.decode(encoding, 'replace')
|
src = src.decode(encoding, 'replace')
|
||||||
|
|
||||||
|
# Meta data definitions as in
|
||||||
|
# http://www.mobileread.com/forums/showpost.php?p=712544&postcount=9
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
title = None
|
title = None
|
||||||
pat = re.compile(r'<!--.*?TITLE=(?P<q>[\'"])(.+?)(?P=q).*?-->', re.DOTALL)
|
pat = re.compile(r'<!--.*?TITLE=(?P<q>[\'"])(.+?)(?P=q).*?-->', re.DOTALL)
|
||||||
@ -35,6 +42,13 @@ def get_metadata_(src, encoding=None):
|
|||||||
match = pat.search(src)
|
match = pat.search(src)
|
||||||
if match:
|
if match:
|
||||||
title = match.group(1)
|
title = match.group(1)
|
||||||
|
if not title:
|
||||||
|
for x in ('Title','DC.title','DCTERMS.title'):
|
||||||
|
pat = get_meta_regexp_(x)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
title = match.group(1)
|
||||||
|
break
|
||||||
|
|
||||||
# Author
|
# Author
|
||||||
author = None
|
author = None
|
||||||
@ -42,7 +56,15 @@ def get_metadata_(src, encoding=None):
|
|||||||
match = pat.search(src)
|
match = pat.search(src)
|
||||||
if match:
|
if match:
|
||||||
author = match.group(2).replace(',', ';')
|
author = match.group(2).replace(',', ';')
|
||||||
|
else:
|
||||||
|
for x in ('Author','DC.creator.aut','DCTERMS.creator.aut'):
|
||||||
|
pat = get_meta_regexp_(x)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
author = match.group(1)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Create MetaInformation with Title and Author
|
||||||
ent_pat = re.compile(r'&(\S+)?;')
|
ent_pat = re.compile(r'&(\S+)?;')
|
||||||
if title:
|
if title:
|
||||||
title = ent_pat.sub(entity_to_unicode, title)
|
title = ent_pat.sub(entity_to_unicode, title)
|
||||||
@ -51,18 +73,158 @@ def get_metadata_(src, encoding=None):
|
|||||||
mi = MetaInformation(title, [author] if author else None)
|
mi = MetaInformation(title, [author] if author else None)
|
||||||
|
|
||||||
# Publisher
|
# Publisher
|
||||||
|
publisher = None
|
||||||
pat = re.compile(r'<!--.*?PUBLISHER=(?P<q>[\'"])(.+?)(?P=q).*?-->', re.DOTALL)
|
pat = re.compile(r'<!--.*?PUBLISHER=(?P<q>[\'"])(.+?)(?P=q).*?-->', re.DOTALL)
|
||||||
match = pat.search(src)
|
match = pat.search(src)
|
||||||
if match:
|
if match:
|
||||||
mi.publisher = match.group(2)
|
publisher = match.group(2)
|
||||||
|
else:
|
||||||
|
for x in ('Publisher','DC.publisher','DCTERMS.publisher'):
|
||||||
|
pat = get_meta_regexp_(x)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
publisher = match.group(1)
|
||||||
|
break
|
||||||
|
if publisher:
|
||||||
|
mi.publisher = ent_pat.sub(entity_to_unicode, publisher)
|
||||||
|
|
||||||
# ISBN
|
# ISBN
|
||||||
|
isbn = None
|
||||||
pat = re.compile(r'<!--.*?ISBN=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
pat = re.compile(r'<!--.*?ISBN=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
||||||
match = pat.search(src)
|
match = pat.search(src)
|
||||||
if match:
|
if match:
|
||||||
isbn = match.group(1)
|
isbn = match.group(1)
|
||||||
|
else:
|
||||||
|
for x in ('ISBN','DC.identifier.ISBN','DCTERMS.identifier.ISBN'):
|
||||||
|
pat = get_meta_regexp_(x)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
isbn = match.group(1)
|
||||||
|
break
|
||||||
|
if isbn:
|
||||||
mi.isbn = re.sub(r'[^0-9xX]', '', isbn)
|
mi.isbn = re.sub(r'[^0-9xX]', '', isbn)
|
||||||
|
|
||||||
|
# LANGUAGE
|
||||||
|
language = None
|
||||||
|
pat = re.compile(r'<!--.*?LANGUAGE=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
language = match.group(1)
|
||||||
|
else:
|
||||||
|
for x in ('DC.language','DCTERMS.language'):
|
||||||
|
pat = get_meta_regexp_(x)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
language = match.group(1)
|
||||||
|
break
|
||||||
|
if language:
|
||||||
|
mi.language = language
|
||||||
|
|
||||||
|
# PUBDATE
|
||||||
|
pubdate = None
|
||||||
|
pat = re.compile(r'<!--.*?PUBDATE=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
pubdate = match.group(1)
|
||||||
|
else:
|
||||||
|
for x in ('Pubdate','Date of publication','DC.date.published','DC.date.publication','DC.date.issued','DCTERMS.issued'):
|
||||||
|
pat = get_meta_regexp_(x)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
pubdate = match.group(1)
|
||||||
|
break
|
||||||
|
if pubdate:
|
||||||
|
try:
|
||||||
|
mi.pubdate = parse_date(pubdate)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# TIMESTAMP
|
||||||
|
timestamp = None
|
||||||
|
pat = re.compile(r'<!--.*?TIMESTAMP=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
timestamp = match.group(1)
|
||||||
|
else:
|
||||||
|
for x in ('Timestamp','Date of creation','DC.date.created','DC.date.creation','DCTERMS.created'):
|
||||||
|
pat = get_meta_regexp_(x)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
timestamp = match.group(1)
|
||||||
|
break
|
||||||
|
if timestamp:
|
||||||
|
try:
|
||||||
|
mi.timestamp = parse_date(timestamp)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# SERIES
|
||||||
|
series = None
|
||||||
|
pat = re.compile(r'<!--.*?SERIES=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
series = match.group(1)
|
||||||
|
else:
|
||||||
|
pat = get_meta_regexp_("Series")
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
series = match.group(1)
|
||||||
|
if series:
|
||||||
|
mi.series = ent_pat.sub(entity_to_unicode, series)
|
||||||
|
|
||||||
|
# RATING
|
||||||
|
rating = None
|
||||||
|
pat = re.compile(r'<!--.*?RATING=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
rating = match.group(1)
|
||||||
|
else:
|
||||||
|
pat = get_meta_regexp_("Rating")
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
rating = match.group(1)
|
||||||
|
if rating:
|
||||||
|
try:
|
||||||
|
mi.rating = float(rating)
|
||||||
|
if mi.rating < 0:
|
||||||
|
mi.rating = 0
|
||||||
|
if mi.rating > 5:
|
||||||
|
mi.rating /= 2.
|
||||||
|
if mi.rating > 5:
|
||||||
|
mi.rating = 0
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# COMMENTS
|
||||||
|
comments = None
|
||||||
|
pat = re.compile(r'<!--.*?COMMENTS=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
comments = match.group(1)
|
||||||
|
else:
|
||||||
|
pat = get_meta_regexp_("Comments")
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
comments = match.group(1)
|
||||||
|
if comments:
|
||||||
|
mi.comments = ent_pat.sub(entity_to_unicode, comments)
|
||||||
|
|
||||||
|
# TAGS
|
||||||
|
tags = None
|
||||||
|
pat = re.compile(r'<!--.*?TAGS=[\'"]([^"\']+)[\'"].*?-->', re.DOTALL)
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
tags = match.group(1)
|
||||||
|
else:
|
||||||
|
pat = get_meta_regexp_("Tags")
|
||||||
|
match = pat.search(src)
|
||||||
|
if match:
|
||||||
|
tags = match.group(1)
|
||||||
|
if tags:
|
||||||
|
mi.tags = [x.strip() for x in ent_pat.sub(entity_to_unicode,
|
||||||
|
tags).split(",")]
|
||||||
|
|
||||||
|
# Ready to return MetaInformation
|
||||||
return mi
|
return mi
|
||||||
|
|
||||||
|
|
||||||
|
@ -916,7 +916,7 @@ class OPF(object):
|
|||||||
raw = '<?xml version="1.0" encoding="%s"?>\n'%encoding.upper()+raw
|
raw = '<?xml version="1.0" encoding="%s"?>\n'%encoding.upper()+raw
|
||||||
return raw
|
return raw
|
||||||
|
|
||||||
def smart_update(self, mi):
|
def smart_update(self, mi, replace_metadata=False):
|
||||||
for attr in ('title', 'authors', 'author_sort', 'title_sort',
|
for attr in ('title', 'authors', 'author_sort', 'title_sort',
|
||||||
'publisher', 'series', 'series_index', 'rating',
|
'publisher', 'series', 'series_index', 'rating',
|
||||||
'isbn', 'language', 'tags', 'category', 'comments',
|
'isbn', 'language', 'tags', 'category', 'comments',
|
||||||
|
@ -14,7 +14,8 @@ except ImportError:
|
|||||||
|
|
||||||
from lxml import html, etree
|
from lxml import html, etree
|
||||||
|
|
||||||
from calibre import xml_entity_to_unicode, CurrentDir, entity_to_unicode
|
from calibre import xml_entity_to_unicode, CurrentDir, entity_to_unicode, \
|
||||||
|
replace_entities
|
||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename
|
||||||
from calibre.utils.date import parse_date
|
from calibre.utils.date import parse_date
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
@ -70,7 +71,7 @@ class EXTHHeader(object):
|
|||||||
#else:
|
#else:
|
||||||
# print 'unknown record', id, repr(content)
|
# print 'unknown record', id, repr(content)
|
||||||
if title:
|
if title:
|
||||||
self.mi.title = title
|
self.mi.title = replace_entities(title)
|
||||||
|
|
||||||
def process_metadata(self, id, content, codec):
|
def process_metadata(self, id, content, codec):
|
||||||
if id == 100:
|
if id == 100:
|
||||||
|
@ -475,7 +475,8 @@ class Style(object):
|
|||||||
value = float(m.group(1))
|
value = float(m.group(1))
|
||||||
unit = m.group(2)
|
unit = m.group(2)
|
||||||
if unit == '%':
|
if unit == '%':
|
||||||
base = base or self.width
|
if base is None:
|
||||||
|
base = self.width
|
||||||
result = (value / 100.0) * base
|
result = (value / 100.0) * base
|
||||||
elif unit == 'px':
|
elif unit == 'px':
|
||||||
result = value * 72.0 / self._profile.dpi
|
result = value * 72.0 / self._profile.dpi
|
||||||
|
@ -98,7 +98,7 @@ class CoverManager(object):
|
|||||||
authors = [unicode(x) for x in m.creator if x.role == 'aut']
|
authors = [unicode(x) for x in m.creator if x.role == 'aut']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from calibre.utils.magick_draw import create_cover_page, TextLine
|
from calibre.utils.magick.draw import create_cover_page, TextLine
|
||||||
lines = [TextLine(title, 44), TextLine(authors_to_string(authors),
|
lines = [TextLine(title, 44), TextLine(authors_to_string(authors),
|
||||||
32)]
|
32)]
|
||||||
img_data = create_cover_page(lines, I('library.png'))
|
img_data = create_cover_page(lines, I('library.png'))
|
||||||
|
@ -262,8 +262,11 @@ class CSSFlattener(object):
|
|||||||
indent = asfloat(style['text-indent'], 0)
|
indent = asfloat(style['text-indent'], 0)
|
||||||
left += margin
|
left += margin
|
||||||
if (left + indent) < 0:
|
if (left + indent) < 0:
|
||||||
percent = (margin - indent) / style['width']
|
try:
|
||||||
cssdict['margin-left'] = "%d%%" % (percent * 100)
|
percent = (margin - indent) / style['width']
|
||||||
|
cssdict['margin-left'] = "%d%%" % (percent * 100)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
pass
|
||||||
left -= indent
|
left -= indent
|
||||||
if 'display' in cssdict and cssdict['display'] == 'in-line':
|
if 'display' in cssdict and cssdict['display'] == 'in-line':
|
||||||
cssdict['display'] = 'inline'
|
cssdict['display'] = 'inline'
|
||||||
|
@ -59,6 +59,29 @@ class PDFOutput(OutputFormatPlugin):
|
|||||||
self.metadata = oeb_book.metadata
|
self.metadata = oeb_book.metadata
|
||||||
self.cover_data = None
|
self.cover_data = None
|
||||||
|
|
||||||
|
# Remove page-break-before on <body> element as it causes
|
||||||
|
# blank pages in PDF Output
|
||||||
|
from calibre.ebooks.oeb.base import OEB_STYLES, XPath
|
||||||
|
stylesheet = None
|
||||||
|
for item in self.oeb.manifest:
|
||||||
|
if item.media_type.lower() in OEB_STYLES:
|
||||||
|
stylesheet = item
|
||||||
|
break
|
||||||
|
if stylesheet is not None:
|
||||||
|
from cssutils.css import CSSRule
|
||||||
|
classes = set(['.calibre'])
|
||||||
|
for x in self.oeb.spine:
|
||||||
|
root = x.data
|
||||||
|
body = XPath('//h:body[@class]')(root)
|
||||||
|
if body:
|
||||||
|
classes.add('.'+body[0].get('class'))
|
||||||
|
|
||||||
|
for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
|
||||||
|
if rule.selectorList.selectorText in classes:
|
||||||
|
rule.style.removeProperty('page-break-before')
|
||||||
|
rule.style.removeProperty('page-break-after')
|
||||||
|
|
||||||
|
|
||||||
if input_plugin.is_image_collection:
|
if input_plugin.is_image_collection:
|
||||||
log.debug('Converting input as an image collection...')
|
log.debug('Converting input as an image collection...')
|
||||||
self.convert_images(input_plugin.get_images())
|
self.convert_images(input_plugin.get_images())
|
||||||
|
@ -120,25 +120,19 @@ class RTFInput(InputFormatPlugin):
|
|||||||
return self.convert_images(imap)
|
return self.convert_images(imap)
|
||||||
|
|
||||||
def convert_images(self, imap):
|
def convert_images(self, imap):
|
||||||
from calibre.utils.PythonMagickWand import ImageMagick
|
for count, val in imap.items():
|
||||||
with ImageMagick():
|
try:
|
||||||
for count, val in imap.items():
|
imap[count] = self.convert_image(val)
|
||||||
try:
|
except:
|
||||||
imap[count] = self.convert_image(val)
|
self.log.exception('Failed to convert', val)
|
||||||
except:
|
|
||||||
self.log.exception('Failed to convert', val)
|
|
||||||
return imap
|
return imap
|
||||||
|
|
||||||
def convert_image(self, name):
|
def convert_image(self, name):
|
||||||
import calibre.utils.PythonMagickWand as p
|
from calibre.utils.magick import Image
|
||||||
img = p.NewMagickWand()
|
img = Image()
|
||||||
if img < 0:
|
img.open(name)
|
||||||
raise RuntimeError('Cannot create wand.')
|
|
||||||
if not p.MagickReadImage(img, name):
|
|
||||||
self.log.warn('Failed to read image:', name)
|
|
||||||
name = name.replace('.wmf', '.jpg')
|
name = name.replace('.wmf', '.jpg')
|
||||||
p.MagickWriteImage(img, name)
|
img.save(name)
|
||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
@ -192,12 +186,18 @@ class RTFInput(InputFormatPlugin):
|
|||||||
from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException
|
from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException
|
||||||
self.log = log
|
self.log = log
|
||||||
self.log('Converting RTF to XML...')
|
self.log('Converting RTF to XML...')
|
||||||
|
#Name of the preprocesssed RTF file
|
||||||
fname = self.preprocess(stream.name)
|
fname = self.preprocess(stream.name)
|
||||||
try:
|
try:
|
||||||
xml = self.generate_xml(fname)
|
xml = self.generate_xml(fname)
|
||||||
except RtfInvalidCodeException, e:
|
except RtfInvalidCodeException, e:
|
||||||
raise ValueError(_('This RTF file has a feature calibre does not '
|
raise ValueError(_('This RTF file has a feature calibre does not '
|
||||||
'support. Convert it to HTML first and then try it.\n%s')%e)
|
'support. Convert it to HTML first and then try it.\n%s')%e)
|
||||||
|
|
||||||
|
'''dataxml = open('dataxml.xml', 'w')
|
||||||
|
dataxml.write(xml)
|
||||||
|
dataxml.close'''
|
||||||
|
|
||||||
d = glob.glob(os.path.join('*_rtf_pict_dir', 'picts.rtf'))
|
d = glob.glob(os.path.join('*_rtf_pict_dir', 'picts.rtf'))
|
||||||
if d:
|
if d:
|
||||||
imap = {}
|
imap = {}
|
||||||
@ -205,6 +205,7 @@ class RTFInput(InputFormatPlugin):
|
|||||||
imap = self.extract_images(d[0])
|
imap = self.extract_images(d[0])
|
||||||
except:
|
except:
|
||||||
self.log.exception('Failed to extract images...')
|
self.log.exception('Failed to extract images...')
|
||||||
|
|
||||||
self.log('Parsing XML...')
|
self.log('Parsing XML...')
|
||||||
parser = etree.XMLParser(recover=True, no_network=True)
|
parser = etree.XMLParser(recover=True, no_network=True)
|
||||||
doc = etree.fromstring(xml, parser=parser)
|
doc = etree.fromstring(xml, parser=parser)
|
||||||
@ -214,10 +215,10 @@ class RTFInput(InputFormatPlugin):
|
|||||||
name = imap.get(num, None)
|
name = imap.get(num, None)
|
||||||
if name is not None:
|
if name is not None:
|
||||||
pict.set('num', name)
|
pict.set('num', name)
|
||||||
|
|
||||||
self.log('Converting XML to HTML...')
|
self.log('Converting XML to HTML...')
|
||||||
inline_class = InlineClass(self.log)
|
inline_class = InlineClass(self.log)
|
||||||
styledoc = etree.fromstring(P('templates/rtf.xsl', data=True))
|
styledoc = etree.fromstring(P('templates/rtf.xsl', data=True))
|
||||||
|
|
||||||
extensions = { ('calibre', 'inline-class') : inline_class }
|
extensions = { ('calibre', 'inline-class') : inline_class }
|
||||||
transform = etree.XSLT(styledoc, extensions=extensions)
|
transform = etree.XSLT(styledoc, extensions=extensions)
|
||||||
result = transform(doc)
|
result = transform(doc)
|
||||||
|