mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
c00c253d65
@ -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 |
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 19 KiB |
@ -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')
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
@ -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':
|
||||||
|
try:
|
||||||
key = string.capwords(feed_title(div.em.a))
|
key = string.capwords(feed_title(div.em.a))
|
||||||
articles[key] = []
|
articles[key] = []
|
||||||
ans.append(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)
|
||||||
@ -84,3 +89,4 @@ class TagesspiegelRSS(BasicNewsRecipe):
|
|||||||
|
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
@ -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' )]
|
||||||
|
|
||||||
|
@ -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 Reader Edition'
|
||||||
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 = 100
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
no_javascript = True
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
|
delay = 0
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
('Politik', 'http://newsfeed.zeit.de/politik/index'),
|
('Seite 1', 'http://newsfeed.zeit.de/index'),
|
||||||
('Wirtschaft', 'http://newsfeed.zeit.de/wirtschaft/index'),
|
('Politik', 'http://www.zeit.de/solr/select/?q=ressort:%22Politik%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
('Meinung', 'http://newsfeed.zeit.de/meinung/index'),
|
('Wirtschaft', 'http://www.zeit.de/solr/select/?q=ressort:%22Wirtschaft%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
('Gesellschaft', 'http://newsfeed.zeit.de/gesellschaft/index'),
|
('Meinung', 'http://www.zeit.de/solr/select/?q=ressort:%22Meinung%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
('Kultur', 'http://newsfeed.zeit.de/kultur/index'),
|
('Gesellschaft', 'http://www.zeit.de/solr/select/?q=ressort:%22Gesellschaft%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
('Wissen', 'http://newsfeed.zeit.de/wissen/index'),
|
('Kultur', 'http://www.zeit.de/solr/select/?q=ressort:%22Kultur%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
|
('Wissen', 'http://www.zeit.de/solr/select/?q=ressort:%22Wissen%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
|
('Digital', 'http://www.zeit.de/solr/select/?q=ressort:%22Digital%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
|
('Studium', 'http://www.zeit.de/solr/select/?q=ressort:%22Studium%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
|
('Karriere', 'http://www.zeit.de/solr/select/?q=ressort:%22Karriere%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
|
('Lebensart', 'http://www.zeit.de/solr/select/?q=ressort:%22Lebensart%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
|
('Reisen', 'http://www.zeit.de/solr/select/?q=ressort:%22Reisen%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
|
('Auto', 'http://www.zeit.de/solr/select/?q=ressort:%22Auto%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
|
('Sport', 'http://www.zeit.de/solr/select/?q=ressort:%22Sport%22%20type:article&version=2.2&start=0&rows=50&sort=date-first-released%20desc&indent=on&wt=xslt&tr=solr2rss.xsl'),
|
||||||
]
|
]
|
||||||
|
|
||||||
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 += "?page=all"
|
||||||
|
|
||||||
ans = article.get('guid',None)
|
if 'video' in ans or 'quiz' or 'blog.zeit.de/' in ans :
|
||||||
|
|
||||||
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 :
|
|
||||||
|
|
||||||
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', '')
|
|
||||||
|
|
||||||
|
@ -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'))
|
||||||
|
@ -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 '
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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')
|
||||||
|
|
||||||
@ -121,6 +122,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 +159,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 +177,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 +203,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 +225,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 +261,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 +274,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 +299,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 +328,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 +351,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 +393,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 +409,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 +457,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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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']
|
||||||
|
|
||||||
@ -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')
|
||||||
@ -195,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
|
||||||
|
|
||||||
|
@ -15,6 +15,22 @@ from calibre.ebooks.metadata.library_thing 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'
|
||||||
|
|
||||||
|
@ -119,10 +119,11 @@ class RTFMLizer(object):
|
|||||||
output += '{\\page } '
|
output += '{\\page } '
|
||||||
for item in self.oeb_book.spine:
|
for item in self.oeb_book.spine:
|
||||||
self.log.debug('Converting %s to RTF markup...' % item.href)
|
self.log.debug('Converting %s to RTF markup...' % item.href)
|
||||||
stylizer = Stylizer(item.data, item.href, self.oeb_book, self.opts, self.opts.output_profile)
|
content = unicode(etree.tostring(item.data, encoding=unicode))
|
||||||
content = unicode(etree.tostring(item.data.find(XHTML('body')), encoding=unicode))
|
|
||||||
content = self.remove_newlines(content)
|
content = self.remove_newlines(content)
|
||||||
output += self.dump_text(etree.fromstring(content), stylizer)
|
content = etree.fromstring(content)
|
||||||
|
stylizer = Stylizer(content, item.href, self.oeb_book, self.opts, self.opts.output_profile)
|
||||||
|
output += self.dump_text(content.find(XHTML('body')), stylizer)
|
||||||
output += self.footer()
|
output += self.footer()
|
||||||
output = self.insert_images(output)
|
output = self.insert_images(output)
|
||||||
output = self.clean_text(output)
|
output = self.clean_text(output)
|
||||||
|
@ -512,9 +512,18 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
|||||||
idx = i
|
idx = i
|
||||||
self.opt_toolbar_text.addItem(x[0], x[1])
|
self.opt_toolbar_text.addItem(x[0], x[1])
|
||||||
self.opt_toolbar_text.setCurrentIndex(idx)
|
self.opt_toolbar_text.setCurrentIndex(idx)
|
||||||
|
self.reset_confirmation_button.clicked.connect(self.reset_confirmation)
|
||||||
|
|
||||||
self.category_view.setCurrentIndex(self.category_view.model().index_for_name(initial_category))
|
self.category_view.setCurrentIndex(self.category_view.model().index_for_name(initial_category))
|
||||||
|
|
||||||
|
def reset_confirmation(self):
|
||||||
|
from calibre.gui2 import dynamic
|
||||||
|
for key in dynamic.keys():
|
||||||
|
if key.endswith('_again') and dynamic[key] is False:
|
||||||
|
dynamic[key] = True
|
||||||
|
info_dialog(self, _('Done'),
|
||||||
|
_('Confirmation dialogs have all been reset'), show=True)
|
||||||
|
|
||||||
def check_port_value(self, *args):
|
def check_port_value(self, *args):
|
||||||
port = self.port.value()
|
port = self.port.value()
|
||||||
if port < 1025:
|
if port < 1025:
|
||||||
|
@ -89,8 +89,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>724</width>
|
<width>720</width>
|
||||||
<height>683</height>
|
<height>679</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_7">
|
<layout class="QGridLayout" name="gridLayout_7">
|
||||||
@ -222,6 +222,13 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="reset_confirmation_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Reset all disabled &confirmation dialogs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_5">
|
<widget class="QGroupBox" name="groupBox_5">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -5,7 +5,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
from calibre.gui2 import dynamic
|
from calibre.gui2 import dynamic
|
||||||
from calibre.gui2.dialogs.confirm_delete_ui import Ui_Dialog
|
from calibre.gui2.dialogs.confirm_delete_ui import Ui_Dialog
|
||||||
from PyQt4.Qt import QDialog, SIGNAL, Qt
|
from PyQt4.Qt import QDialog, Qt, QPixmap, QIcon
|
||||||
|
|
||||||
def _config_name(name):
|
def _config_name(name):
|
||||||
return name + '_again'
|
return name + '_again'
|
||||||
@ -18,15 +18,17 @@ class Dialog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
self.msg.setText(msg)
|
self.msg.setText(msg)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.connect(self.again, SIGNAL('stateChanged(int)'), self.toggle)
|
self.again.stateChanged.connect(self.toggle)
|
||||||
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
|
|
||||||
def toggle(self, x):
|
def toggle(self, *args):
|
||||||
dynamic[_config_name(self.name)] = self.again.isChecked()
|
dynamic[_config_name(self.name)] = self.again.isChecked()
|
||||||
|
|
||||||
def confirm(msg, name, parent=None):
|
def confirm(msg, name, parent=None, pixmap='dialog_warning.svg'):
|
||||||
if not dynamic.get(_config_name(name), True):
|
if not dynamic.get(_config_name(name), True):
|
||||||
return True
|
return True
|
||||||
d = Dialog(msg, name, parent)
|
d = Dialog(msg, name, parent)
|
||||||
|
d.label.setPixmap(QPixmap(I(pixmap)))
|
||||||
|
d.setWindowIcon(QIcon(I(pixmap)))
|
||||||
return d.exec_() == d.Accepted
|
return d.exec_() == d.Accepted
|
||||||
|
@ -26,7 +26,10 @@ class ThrobbingButton(QToolButton):
|
|||||||
def set_normal_icon_size(self, w, h):
|
def set_normal_icon_size(self, w, h):
|
||||||
self.normal_icon_size = QSize(w, h)
|
self.normal_icon_size = QSize(w, h)
|
||||||
self.setIconSize(self.normal_icon_size)
|
self.setIconSize(self.normal_icon_size)
|
||||||
self.setMinimumSize(self.sizeHint())
|
try:
|
||||||
|
self.setMinimumSize(self.sizeHint())
|
||||||
|
except:
|
||||||
|
self.setMinimumSize(QSize(w+5, h+5))
|
||||||
|
|
||||||
def animation_finished(self):
|
def animation_finished(self):
|
||||||
self.setIconSize(self.normal_icon_size)
|
self.setIconSize(self.normal_icon_size)
|
||||||
|
@ -3,12 +3,14 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
''' Post installation script for linux '''
|
''' Post installation script for linux '''
|
||||||
|
|
||||||
import sys, os, shutil, cPickle, textwrap, stat
|
import sys, os, cPickle, textwrap, stat
|
||||||
from subprocess import check_call
|
from subprocess import check_call
|
||||||
|
|
||||||
from calibre import __appname__, prints, guess_type
|
from calibre import __appname__, prints, guess_type
|
||||||
from calibre.constants import islinux, isfreebsd
|
from calibre.constants import islinux, isfreebsd
|
||||||
from calibre.customize.ui import all_input_formats
|
from calibre.customize.ui import all_input_formats
|
||||||
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
|
from calibre import CurrentDir
|
||||||
|
|
||||||
|
|
||||||
entry_points = {
|
entry_points = {
|
||||||
@ -39,6 +41,7 @@ entry_points = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Uninstall script {{{
|
||||||
UNINSTALL = '''\
|
UNINSTALL = '''\
|
||||||
#!{python}
|
#!{python}
|
||||||
euid = {euid}
|
euid = {euid}
|
||||||
@ -79,6 +82,8 @@ for f in mr:
|
|||||||
os.remove(os.path.abspath(__file__))
|
os.remove(os.path.abspath(__file__))
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class PostInstall:
|
class PostInstall:
|
||||||
|
|
||||||
def task_failed(self, msg):
|
def task_failed(self, msg):
|
||||||
@ -171,7 +176,7 @@ class PostInstall:
|
|||||||
self.task_failed('Creating uninstaller failed')
|
self.task_failed('Creating uninstaller failed')
|
||||||
|
|
||||||
|
|
||||||
def setup_completion(self):
|
def setup_completion(self): # {{{
|
||||||
try:
|
try:
|
||||||
self.info('Setting up bash completion...')
|
self.info('Setting up bash completion...')
|
||||||
from calibre.ebooks.metadata.cli import option_parser as metaop, filetypes as meta_filetypes
|
from calibre.ebooks.metadata.cli import option_parser as metaop, filetypes as meta_filetypes
|
||||||
@ -287,8 +292,9 @@ class PostInstall:
|
|||||||
if self.opts.fatal_errors:
|
if self.opts.fatal_errors:
|
||||||
raise
|
raise
|
||||||
self.task_failed('Setting up completion failed')
|
self.task_failed('Setting up completion failed')
|
||||||
|
# }}}
|
||||||
|
|
||||||
def install_man_pages(self):
|
def install_man_pages(self): # {{{
|
||||||
try:
|
try:
|
||||||
from calibre.utils.help2man import create_man_page
|
from calibre.utils.help2man import create_man_page
|
||||||
if isfreebsd:
|
if isfreebsd:
|
||||||
@ -318,73 +324,69 @@ class PostInstall:
|
|||||||
if self.opts.fatal_errors:
|
if self.opts.fatal_errors:
|
||||||
raise
|
raise
|
||||||
self.task_failed('Installing MAN pages failed')
|
self.task_failed('Installing MAN pages failed')
|
||||||
|
# }}}
|
||||||
|
|
||||||
def setup_desktop_integration(self):
|
def setup_desktop_integration(self): # {{{
|
||||||
try:
|
try:
|
||||||
from PyQt4.QtCore import QFile
|
|
||||||
from tempfile import mkdtemp
|
|
||||||
|
|
||||||
self.info('Setting up desktop integration...')
|
self.info('Setting up desktop integration...')
|
||||||
|
|
||||||
|
|
||||||
tdir = mkdtemp()
|
with TemporaryDirectory() as tdir:
|
||||||
cwd = os.getcwdu()
|
with CurrentDir(tdir):
|
||||||
try:
|
render_img('mimetypes/lrf.svg', 'calibre-lrf.png')
|
||||||
os.chdir(tdir)
|
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png application-lrf', shell=True)
|
||||||
render_svg(QFile(I('mimetypes/lrf.svg')), os.path.join(tdir, 'calibre-lrf.png'))
|
self.icon_resources.append(('mimetypes', 'application-lrf', '128'))
|
||||||
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png application-lrf', shell=True)
|
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png text-lrs', shell=True)
|
||||||
self.icon_resources.append(('mimetypes', 'application-lrf', '128'))
|
self.icon_resources.append(('mimetypes', 'application-lrs',
|
||||||
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png text-lrs', shell=True)
|
'128'))
|
||||||
self.icon_resources.append(('mimetypes', 'application-lrs',
|
render_img('lt.png', 'calibre-gui.png')
|
||||||
'128'))
|
check_call('xdg-icon-resource install --noupdate --size 128 calibre-gui.png calibre-gui', shell=True)
|
||||||
QFile(I('library.png')).copy(os.path.join(tdir, 'calibre-gui.png'))
|
self.icon_resources.append(('apps', 'calibre-gui', '128'))
|
||||||
check_call('xdg-icon-resource install --noupdate --size 128 calibre-gui.png calibre-gui', shell=True)
|
render_img('viewer.svg', 'calibre-viewer.png')
|
||||||
self.icon_resources.append(('apps', 'calibre-gui', '128'))
|
check_call('xdg-icon-resource install --size 128 calibre-viewer.png calibre-viewer', shell=True)
|
||||||
render_svg(QFile(I('viewer.svg')), os.path.join(tdir, 'calibre-viewer.png'))
|
self.icon_resources.append(('apps', 'calibre-viewer', '128'))
|
||||||
check_call('xdg-icon-resource install --size 128 calibre-viewer.png calibre-viewer', shell=True)
|
|
||||||
self.icon_resources.append(('apps', 'calibre-viewer', '128'))
|
|
||||||
|
|
||||||
mimetypes = set([])
|
mimetypes = set([])
|
||||||
for x in all_input_formats():
|
for x in all_input_formats():
|
||||||
mt = guess_type('dummy.'+x)[0]
|
mt = guess_type('dummy.'+x)[0]
|
||||||
if mt and 'chemical' not in mt:
|
if mt and 'chemical' not in mt:
|
||||||
mimetypes.add(mt)
|
mimetypes.add(mt)
|
||||||
|
|
||||||
def write_mimetypes(f):
|
def write_mimetypes(f):
|
||||||
f.write('MimeType=%s;\n'%';'.join(mimetypes))
|
f.write('MimeType=%s;\n'%';'.join(mimetypes))
|
||||||
|
|
||||||
f = open('calibre-lrfviewer.desktop', 'wb')
|
f = open('calibre-lrfviewer.desktop', 'wb')
|
||||||
f.write(VIEWER)
|
f.write(VIEWER)
|
||||||
f.close()
|
f.close()
|
||||||
f = open('calibre-ebook-viewer.desktop', 'wb')
|
f = open('calibre-ebook-viewer.desktop', 'wb')
|
||||||
f.write(EVIEWER)
|
f.write(EVIEWER)
|
||||||
write_mimetypes(f)
|
write_mimetypes(f)
|
||||||
f.close()
|
f.close()
|
||||||
f = open('calibre-gui.desktop', 'wb')
|
f = open('calibre-gui.desktop', 'wb')
|
||||||
f.write(GUI)
|
f.write(GUI)
|
||||||
write_mimetypes(f)
|
write_mimetypes(f)
|
||||||
f.close()
|
f.close()
|
||||||
des = ('calibre-gui.desktop', 'calibre-lrfviewer.desktop',
|
des = ('calibre-gui.desktop', 'calibre-lrfviewer.desktop',
|
||||||
'calibre-ebook-viewer.desktop')
|
'calibre-ebook-viewer.desktop')
|
||||||
for x in des:
|
for x in des:
|
||||||
cmd = ['xdg-desktop-menu', 'install', './'+x]
|
cmd = ['xdg-desktop-menu', 'install', './'+x]
|
||||||
if x != des[-1]:
|
if x != des[-1]:
|
||||||
cmd.insert(2, '--noupdate')
|
cmd.insert(2, '--noupdate')
|
||||||
check_call(' '.join(cmd), shell=True)
|
check_call(' '.join(cmd), shell=True)
|
||||||
self.menu_resources.append(x)
|
self.menu_resources.append(x)
|
||||||
f = open('calibre-mimetypes', 'wb')
|
f = open('calibre-mimetypes', 'wb')
|
||||||
f.write(MIME)
|
f.write(MIME)
|
||||||
f.close()
|
f.close()
|
||||||
self.mime_resources.append('calibre-mimetypes')
|
self.mime_resources.append('calibre-mimetypes')
|
||||||
check_call('xdg-mime install ./calibre-mimetypes', shell=True)
|
check_call('xdg-mime install ./calibre-mimetypes', shell=True)
|
||||||
finally:
|
|
||||||
os.chdir(cwd)
|
|
||||||
shutil.rmtree(tdir)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
if self.opts.fatal_errors:
|
if self.opts.fatal_errors:
|
||||||
raise
|
raise
|
||||||
self.task_failed('Setting up desktop integration failed')
|
self.task_failed('Setting up desktop integration failed')
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
def option_parser():
|
def option_parser():
|
||||||
from calibre.utils.config import OptionParser
|
from calibre.utils.config import OptionParser
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
@ -542,21 +544,10 @@ MIME = '''\
|
|||||||
</mime-info>
|
</mime-info>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def render_svg(image, dest, width=128, height=128):
|
def render_img(image, dest, width=128, height=128):
|
||||||
from PyQt4.QtGui import QPainter, QImage
|
from PyQt4.Qt import QImage, Qt
|
||||||
from PyQt4.QtSvg import QSvgRenderer
|
img = QImage(I(image)).scaled(width, height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
||||||
image = image.readAll() if hasattr(image, 'readAll') else image
|
img.save(dest)
|
||||||
svg = QSvgRenderer(image)
|
|
||||||
painter = QPainter()
|
|
||||||
image = QImage(width, height, QImage.Format_ARGB32)
|
|
||||||
painter.begin(image)
|
|
||||||
painter.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform|QPainter.HighQualityAntialiasing)
|
|
||||||
painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
|
|
||||||
svg.render(painter)
|
|
||||||
painter.end()
|
|
||||||
if dest is None:
|
|
||||||
return image
|
|
||||||
image.save(dest)
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
p = option_parser()
|
p = option_parser()
|
||||||
|
@ -25,7 +25,7 @@ clean:
|
|||||||
|
|
||||||
html:
|
html:
|
||||||
mkdir -p .build/html .build/doctrees
|
mkdir -p .build/html .build/doctrees
|
||||||
$(SPHINXBUILD) -b custom $(ALLSPHINXOPTS) .build/html
|
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html
|
||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The HTML pages are in .build/html."
|
@echo "Build finished. The HTML pages are in .build/html."
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ qthelp:
|
|||||||
|
|
||||||
epub:
|
epub:
|
||||||
mkdir -p .build/qthelp .build/doctrees
|
mkdir -p .build/qthelp .build/doctrees
|
||||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) .build/epub
|
$(SPHINXBUILD) -b myepub $(ALLSPHINXOPTS) .build/epub
|
||||||
@echo
|
@echo
|
||||||
@echo "Build finished."
|
@echo "Build finished."
|
||||||
|
|
||||||
|
@ -23,9 +23,11 @@ custom
|
|||||||
# General configuration
|
# General configuration
|
||||||
# ---------------------
|
# ---------------------
|
||||||
|
|
||||||
|
needs_sphinx = '1.0'
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
|
||||||
extensions = ['sphinx.ext.autodoc', 'custom']
|
extensions = ['sphinx.ext.autodoc', 'custom', 'sphinx.ext.viewcode']
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['templates']
|
templates_path = ['templates']
|
||||||
@ -36,6 +38,9 @@ source_suffix = '.rst'
|
|||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# The language
|
||||||
|
language = 'en'
|
||||||
|
|
||||||
# General substitutions.
|
# General substitutions.
|
||||||
project = __appname__
|
project = __appname__
|
||||||
copyright = '2008, Kovid Goyal'
|
copyright = '2008, Kovid Goyal'
|
||||||
@ -81,7 +86,6 @@ pygments_style = 'sphinx'
|
|||||||
# given in html_static_path.
|
# given in html_static_path.
|
||||||
html_theme = 'default'
|
html_theme = 'default'
|
||||||
html_theme_options = {'stickysidebar':'true', 'relbarbgcolor':'black'}
|
html_theme_options = {'stickysidebar':'true', 'relbarbgcolor':'black'}
|
||||||
html_style = 'calibre.css'
|
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
@ -100,8 +104,16 @@ html_use_smartypants = True
|
|||||||
html_title = 'calibre User Manual'
|
html_title = 'calibre User Manual'
|
||||||
html_short_title = 'Start'
|
html_short_title = 'Start'
|
||||||
html_logo = 'resources/logo.png'
|
html_logo = 'resources/logo.png'
|
||||||
|
|
||||||
epub_author = 'Kovid Goyal'
|
epub_author = 'Kovid Goyal'
|
||||||
epub_cover = 'resources/epub_cover.jpg'
|
epub_cover = 'epub_cover.jpg'
|
||||||
|
epub_publisher = 'Kovid Goyal'
|
||||||
|
epub_identifier = 'http://calibre-ebook.com/user_manual'
|
||||||
|
epub_scheme = 'url'
|
||||||
|
epub_uid = 'S54a88f8e9d42455e9c6db000e989225f'
|
||||||
|
epub_tocdepth = 4
|
||||||
|
epub_tocdup = True
|
||||||
|
epub_pre_files = [('epub_titlepage.html', 'Cover')]
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
#html_sidebars = {}
|
#html_sidebars = {}
|
||||||
|
@ -3,29 +3,17 @@
|
|||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
import sys, os, inspect, re, textwrap
|
import sys, os, re, textwrap
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('../../'))
|
sys.path.insert(0, os.path.abspath('../../'))
|
||||||
sys.extensions_location = '../plugins'
|
sys.extensions_location = '../plugins'
|
||||||
sys.resources_location = '../../../resources'
|
sys.resources_location = '../../../resources'
|
||||||
|
|
||||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
|
||||||
from qthelp import QtHelpBuilder
|
|
||||||
from epub import EPUBHelpBuilder
|
|
||||||
from sphinx.util import rpartition
|
|
||||||
from sphinx.util.console import bold
|
from sphinx.util.console import bold
|
||||||
from sphinx.ext.autodoc import prepare_docstring
|
|
||||||
from docutils.statemachine import ViewList
|
|
||||||
from docutils import nodes
|
|
||||||
|
|
||||||
sys.path.append(os.path.abspath('../../../'))
|
sys.path.append(os.path.abspath('../../../'))
|
||||||
from calibre.linux import entry_points
|
from calibre.linux import entry_points
|
||||||
|
from epub import EPUBHelpBuilder
|
||||||
class CustomBuilder(StandaloneHTMLBuilder):
|
|
||||||
name = 'custom'
|
|
||||||
|
|
||||||
class CustomQtBuild(QtHelpBuilder):
|
|
||||||
name = 'customqt'
|
|
||||||
|
|
||||||
def substitute(app, doctree):
|
def substitute(app, doctree):
|
||||||
pass
|
pass
|
||||||
@ -252,64 +240,9 @@ def cli_docs(app):
|
|||||||
raw += '\n'+'\n'.join(lines)
|
raw += '\n'+'\n'.join(lines)
|
||||||
update_cli_doc(os.path.join('cli', cmd+'.rst'), raw, info)
|
update_cli_doc(os.path.join('cli', cmd+'.rst'), raw, info)
|
||||||
|
|
||||||
def auto_member(dirname, arguments, options, content, lineno,
|
|
||||||
content_offset, block_text, state, state_machine):
|
|
||||||
name = arguments[0]
|
|
||||||
env = state.document.settings.env
|
|
||||||
|
|
||||||
mod_cls, obj = rpartition(name, '.')
|
|
||||||
if not mod_cls and hasattr(env, 'autodoc_current_class'):
|
|
||||||
mod_cls = env.autodoc_current_class
|
|
||||||
if not mod_cls:
|
|
||||||
mod_cls = env.currclass
|
|
||||||
mod, cls = rpartition(mod_cls, '.')
|
|
||||||
if not mod and hasattr(env, 'autodoc_current_module'):
|
|
||||||
mod = env.autodoc_current_module
|
|
||||||
if not mod:
|
|
||||||
mod = env.currmodule
|
|
||||||
|
|
||||||
module = __import__(mod, None, None, ['foo'])
|
|
||||||
cls = getattr(module, cls)
|
|
||||||
lines = inspect.getsourcelines(cls)[0]
|
|
||||||
|
|
||||||
comment_lines = []
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
if re.search(r'%s\s*=\s*\S+'%obj, line) and not line.strip().startswith('#:'):
|
|
||||||
for j in range(i-1, 0, -1):
|
|
||||||
raw = lines[j].strip()
|
|
||||||
if not raw.startswith('#:'):
|
|
||||||
break
|
|
||||||
comment_lines.append(raw[2:])
|
|
||||||
break
|
|
||||||
comment_lines.reverse()
|
|
||||||
docstring = '\n'.join(comment_lines)
|
|
||||||
|
|
||||||
if module is not None and docstring is not None:
|
|
||||||
docstring = docstring.decode('utf-8')
|
|
||||||
|
|
||||||
result = ViewList()
|
|
||||||
result.append('.. attribute:: %s.%s'%(cls.__name__, obj), '<autodoc>')
|
|
||||||
result.append('', '<autodoc>')
|
|
||||||
|
|
||||||
docstring = prepare_docstring(docstring)
|
|
||||||
for i, line in enumerate(docstring):
|
|
||||||
result.append(' ' + line, '<docstring of %s>' % name, i)
|
|
||||||
|
|
||||||
result.append('', '')
|
|
||||||
result.append(' **Default**: ``%s``'%repr(getattr(cls, obj, None)), '<default memeber value>')
|
|
||||||
result.append('', '')
|
|
||||||
node = nodes.paragraph()
|
|
||||||
state.nested_parse(result, content_offset, node)
|
|
||||||
|
|
||||||
return list(node)
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
app.add_config_value('epub_cover', None, False)
|
app.add_config_value('epub_cover', None, False)
|
||||||
app.add_config_value('epub_author', '', False)
|
|
||||||
app.add_builder(CustomBuilder)
|
|
||||||
app.add_builder(CustomQtBuild)
|
|
||||||
app.add_builder(EPUBHelpBuilder)
|
app.add_builder(EPUBHelpBuilder)
|
||||||
app.add_directive('automember', auto_member, 1, (1, 0, 1))
|
|
||||||
app.connect('doctree-read', substitute)
|
app.connect('doctree-read', substitute)
|
||||||
app.connect('builder-inited', cli_docs)
|
app.connect('builder-inited', cli_docs)
|
||||||
app.connect('build-finished', finished)
|
app.connect('build-finished', finished)
|
||||||
|
@ -45,7 +45,8 @@ All static resources are stored in the resources sub-folder of the calibre insta
|
|||||||
from the calibre website it will be :file:`/opt/calibre/resources`. These paths can change depending on where you choose to install |app|.
|
from the calibre website it will be :file:`/opt/calibre/resources`. These paths can change depending on where you choose to install |app|.
|
||||||
|
|
||||||
You should not change the files in this resources folder, as your changes will get overwritten the next time you update |app|. Instead, go to
|
You should not change the files in this resources folder, as your changes will get overwritten the next time you update |app|. Instead, go to
|
||||||
:guilabel:`Preferences->Advanced` and click :guilabel:`Open calibre configuration directory`. In this configuration directory, create a sub-folder called resources and place the files you want to override in it. |app| will automatically use your custom file in preference to the builtin one the next time it is started.
|
:guilabel:`Preferences->Advanced` and click :guilabel:`Open calibre configuration directory`. In this configuration directory, create a sub-folder called resources and place the files you want to override in it. Place the files in the appropriate sub folders, for example place images in :file:`resources/images`, etc.
|
||||||
|
|app| will automatically use your custom file in preference to the builtin one the next time it is started.
|
||||||
|
|
||||||
For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the builtin resources folder and see that the relevant file is
|
For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the builtin resources folder and see that the relevant file is
|
||||||
:file:`resources/images/trash.svg`. Assuming you have an alternate icon in svg format called :file:`mytrash.svg` you would save it in the configuration directory as :file:`resources/images/trash.svg`. All the icons used by the calibre user interface are in :file:`resources/images` and its sub-folders.
|
:file:`resources/images/trash.svg`. Assuming you have an alternate icon in svg format called :file:`mytrash.svg` you would save it in the configuration directory as :file:`resources/images/trash.svg`. All the icons used by the calibre user interface are in :file:`resources/images` and its sub-folders.
|
||||||
|
@ -6,298 +6,53 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, mimetypes, uuid, shutil
|
import os, time
|
||||||
from datetime import datetime
|
|
||||||
from docutils import nodes
|
|
||||||
from xml.sax.saxutils import escape, quoteattr
|
|
||||||
from urlparse import urldefrag
|
|
||||||
from zipfile import ZipFile, ZIP_STORED, ZipInfo
|
|
||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx.builders.epub import EpubBuilder
|
||||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
|
||||||
|
|
||||||
NCX = '''\
|
class EPUBHelpBuilder(EpubBuilder):
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
name = 'myepub'
|
||||||
<ncx version="2005-1"
|
|
||||||
xml:lang="en"
|
|
||||||
xmlns="http://www.daisy.org/z3986/2005/ncx/"
|
|
||||||
xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata"
|
|
||||||
>
|
|
||||||
<head>
|
|
||||||
<meta name="dtb:uid" content="{uid}"/>
|
|
||||||
<meta name="dtb:depth" content="{depth}"/>
|
|
||||||
<meta name="dtb:generator" content="sphinx"/>
|
|
||||||
<meta name="dtb:totalPageCount" content="0"/>
|
|
||||||
<meta name="dtb:maxPageNumber" content="0"/>
|
|
||||||
</head>
|
|
||||||
<docTitle><text>Table of Contents</text></docTitle>
|
|
||||||
<navMap>
|
|
||||||
{navpoints}
|
|
||||||
</navMap>
|
|
||||||
</ncx>
|
|
||||||
'''
|
|
||||||
|
|
||||||
OPF = '''\
|
def add_cover(self, outdir, cover_fname):
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
href = '_static/'+cover_fname
|
||||||
<package version="2.0"
|
opf = os.path.join(self.outdir, 'content.opf')
|
||||||
xmlns="http://www.idpf.org/2007/opf"
|
|
||||||
unique-identifier="sphinx_id"
|
|
||||||
>
|
|
||||||
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata">
|
|
||||||
<dc:title>{title}</dc:title>
|
|
||||||
<dc:creator opf:role="aut">{author}</dc:creator>
|
|
||||||
<dc:contributor opf:role="bkp">Sphinx</dc:contributor>
|
|
||||||
<dc:identifier opf:scheme="sphinx" id="sphinx_id">{uid}</dc:identifier>
|
|
||||||
<dc:date>{date}</dc:date>
|
|
||||||
<meta name="calibre:publication_type" content="sphinx_manual" />
|
|
||||||
<meta name="cover" content="cover"/>
|
|
||||||
</metadata>
|
|
||||||
<manifest>
|
|
||||||
{manifest}
|
|
||||||
</manifest>
|
|
||||||
<spine toc="ncx">
|
|
||||||
{spine}
|
|
||||||
</spine>
|
|
||||||
<guide>
|
|
||||||
{guide}
|
|
||||||
</guide>
|
|
||||||
</package>
|
|
||||||
'''
|
|
||||||
|
|
||||||
CONTAINER='''\
|
cover = '''\
|
||||||
<?xml version="1.0"?>
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||||
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
<head>
|
||||||
<rootfiles>
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<rootfile full-path="{0}" media-type="application/oebps-package+xml"/>
|
<meta name="calibre:cover" content="true" />
|
||||||
</rootfiles>
|
<title>Cover</title>
|
||||||
</container>
|
<style type="text/css" title="override_css">
|
||||||
'''
|
@page {padding: 0pt; margin:0pt}
|
||||||
|
body { text-align: center; padding:0pt; margin: 0pt; }
|
||||||
SVG_TEMPLATE = '''\
|
</style>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
</head>
|
||||||
<head>
|
<body>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
<meta name="calibre:cover" content="true" />
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
<title>Cover</title>
|
width="100%%" height="100%%" viewBox="0 0 600 800"
|
||||||
<style type="text/css" title="override_css">
|
preserveAspectRatio="none">
|
||||||
@page {padding: 0pt; margin:0pt}
|
<image width="600" height="800" xlink:href="%s"/>
|
||||||
body { text-align: center; padding:0pt; margin: 0pt; }
|
</svg>
|
||||||
</style>
|
</body>
|
||||||
</head>
|
</html>
|
||||||
<body>
|
'''%href
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
|
self.files.append('epub_titlepage.html')
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
open(os.path.join(outdir, self.files[-1]), 'wb').write(cover)
|
||||||
width="100%%" height="100%%" viewBox="0 0 600 800"
|
|
||||||
preserveAspectRatio="none">
|
|
||||||
<image width="600" height="800" xlink:href="%s"/>
|
|
||||||
</svg>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
'''
|
|
||||||
|
|
||||||
class TOC(list):
|
|
||||||
|
|
||||||
def __init__(self, title=None, href=None):
|
|
||||||
list.__init__(self)
|
|
||||||
self.title, self.href = title, href
|
|
||||||
|
|
||||||
def create_child(self, title, href):
|
|
||||||
self.append(TOC(title, href))
|
|
||||||
return self[-1]
|
|
||||||
|
|
||||||
def depth(self):
|
|
||||||
try:
|
|
||||||
return max(node.depth() for node in self)+1
|
|
||||||
except ValueError:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
class EPUBHelpBuilder(StandaloneHTMLBuilder):
|
raw = open(opf, 'rb').read()
|
||||||
"""
|
raw = raw.replace('</metadata>',
|
||||||
Builder that also outputs Qt help project, contents and index files.
|
('<meta name="cover" content="%s"/>\n'
|
||||||
"""
|
'<dc:date>%s</dc:date>\n</metadata>') %
|
||||||
name = 'epub'
|
(href.replace('/', '_'), time.strftime('%Y-%m-%d')))
|
||||||
|
raw = raw.replace('</manifest>',
|
||||||
# don't copy the reST source
|
('<item id="{0}" href="{0}" media-type="application/xhtml+xml"/>\n</manifest>').\
|
||||||
copysource = False
|
format('epub_titlepage.html'))
|
||||||
|
open(opf, 'wb').write(raw)
|
||||||
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
|
|
||||||
'image/jpeg']
|
|
||||||
|
|
||||||
# don't add links
|
|
||||||
add_permalinks = False
|
|
||||||
# don't add sidebar etc.
|
|
||||||
embedded = True
|
|
||||||
|
|
||||||
def init(self):
|
|
||||||
StandaloneHTMLBuilder.init(self)
|
|
||||||
self.out_suffix = '.html'
|
|
||||||
self.link_suffix = '.html'
|
|
||||||
self.html_outdir = self.outdir = os.path.join(self.outdir, 'src')
|
|
||||||
self.conf = self.config
|
|
||||||
|
|
||||||
def finish(self):
|
|
||||||
StandaloneHTMLBuilder.finish(self)
|
|
||||||
self.create_titlepage()
|
|
||||||
self.outdir = os.path.dirname(self.outdir)
|
|
||||||
cwd = os.getcwd()
|
|
||||||
os.chdir(self.html_outdir)
|
|
||||||
try:
|
|
||||||
self.generate_manifest()
|
|
||||||
self.generate_toc()
|
|
||||||
self.render_opf()
|
|
||||||
self.render_epub()
|
|
||||||
finally:
|
|
||||||
os.chdir(cwd)
|
|
||||||
|
|
||||||
def render_epub(self):
|
|
||||||
container = CONTAINER.format('content.opf')
|
|
||||||
path = os.path.abspath('..'+os.sep+self.conf.project+'.epub')
|
|
||||||
zf = ZipFile(path, 'w')
|
|
||||||
zi = ZipInfo('mimetype')
|
|
||||||
zi.compress_type = ZIP_STORED
|
|
||||||
zf.writestr(zi, 'application/epub+zip')
|
|
||||||
zf.writestr('META-INF/container.xml', container)
|
|
||||||
for url in self.manifest:
|
|
||||||
fp = os.path.join(self.html_outdir, *url.split('/'))
|
|
||||||
zf.write(fp, url)
|
|
||||||
zf.close()
|
|
||||||
self.info('EPUB created at: '+path)
|
|
||||||
|
|
||||||
|
|
||||||
def render_opf(self):
|
|
||||||
manifest = []
|
|
||||||
for href in self.manifest:
|
|
||||||
mt, id = self.manifest[href]
|
|
||||||
manifest.append(' '*8 + '<item id=%s href=%s media-type=%s />'%\
|
|
||||||
tuple(map(quoteattr, (id, href, mt))))
|
|
||||||
manifest = '\n'.join(manifest)
|
|
||||||
spine = [' '*8+'<itemref idref=%s />'%quoteattr(x) for x in self.spine]
|
|
||||||
spine = '\n'.join(spine)
|
|
||||||
guide = ''
|
|
||||||
|
|
||||||
opf = OPF.format(title=escape(self.conf.html_title),
|
|
||||||
author=escape(self.conf.epub_author), uid=str(uuid.uuid4()),
|
|
||||||
date=datetime.now().isoformat(), manifest=manifest, spine=spine,
|
|
||||||
guide=guide)
|
|
||||||
open('content.opf', 'wb').write(opf)
|
|
||||||
self.manifest['content.opf'] = ('application/oebps-package+xml', 'opf')
|
|
||||||
|
|
||||||
def create_titlepage(self):
|
|
||||||
self.cover_image_url = None
|
|
||||||
if self.conf.epub_cover:
|
|
||||||
img = '_static/'+os.path.basename(self.conf.epub_cover)
|
|
||||||
shutil.copyfile(self.conf.epub_cover, os.path.join(self.html_outdir,
|
|
||||||
*img.split('/')))
|
|
||||||
self.cover_image_url = img
|
|
||||||
tp = SVG_TEMPLATE%img.split('/')[-1]
|
|
||||||
open(os.path.join(self.html_outdir, '_static', 'titlepage.html'),
|
|
||||||
'wb').write(tp)
|
|
||||||
|
|
||||||
def generate_manifest(self):
|
|
||||||
self.manifest = {}
|
|
||||||
id = 1
|
|
||||||
for dirpath, dirnames, filenames in os.walk('.'):
|
|
||||||
for fname in filenames:
|
|
||||||
if fname == '.buildinfo':
|
|
||||||
continue
|
|
||||||
fpath = os.path.abspath(os.path.join(dirpath, fname))
|
|
||||||
url = os.path.relpath(fpath).replace(os.sep, '/')
|
|
||||||
self.manifest[url] = mimetypes.guess_type(url, False)[0]
|
|
||||||
if self.manifest[url] is None:
|
|
||||||
self.warn('Unknown mimetype for: ' + url)
|
|
||||||
self.manifest[url] = 'application/octet-stream'
|
|
||||||
if self.manifest[url] == 'text/html':
|
|
||||||
self.manifest[url] = 'application/xhtml+xml'
|
|
||||||
if self.cover_image_url and url.endswith(self.cover_image_url):
|
|
||||||
id_ = 'cover'
|
|
||||||
else:
|
|
||||||
id_ = 'id'+str(id)
|
|
||||||
id += 1
|
|
||||||
self.manifest[url] = (self.manifest[url], id_)
|
|
||||||
|
|
||||||
def isdocnode(self, node):
|
|
||||||
if not isinstance(node, nodes.list_item):
|
|
||||||
return False
|
|
||||||
if len(node.children) != 2:
|
|
||||||
return False
|
|
||||||
if not isinstance(node.children[0], addnodes.compact_paragraph):
|
|
||||||
return False
|
|
||||||
if not isinstance(node.children[0][0], nodes.reference):
|
|
||||||
return False
|
|
||||||
if not isinstance(node.children[1], nodes.bullet_list):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def generate_toc(self):
|
|
||||||
tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self,
|
|
||||||
prune_toctrees=False)
|
|
||||||
istoctree = lambda node: (
|
|
||||||
isinstance(node, addnodes.compact_paragraph)
|
|
||||||
and node.has_key('toctree'))
|
|
||||||
toc = TOC()
|
|
||||||
for node in tocdoc.traverse(istoctree):
|
|
||||||
self.extend_toc(toc, node)
|
|
||||||
self._parts = []
|
|
||||||
self._po = 0
|
|
||||||
self._po_map = {}
|
|
||||||
self.spine_map = {}
|
|
||||||
self.spine = []
|
|
||||||
self.render_toc(toc)
|
|
||||||
navpoints = '\n'.join(self._parts).strip()
|
|
||||||
ncx = NCX.format(uid=str(uuid.uuid4()), depth=toc.depth(),
|
|
||||||
navpoints=navpoints)
|
|
||||||
open('toc.ncx', 'wb').write(ncx)
|
|
||||||
self.manifest['toc.ncx'] = ('application/x-dtbncx+xml', 'ncx')
|
|
||||||
self.spine.insert(0, self.manifest[self.conf.master_doc+'.html'][1])
|
|
||||||
if self.conf.epub_cover:
|
|
||||||
self.spine.insert(0, self.manifest['_static/titlepage.html'][1])
|
|
||||||
|
|
||||||
def add_to_spine(self, href):
|
|
||||||
href = urldefrag(href)[0]
|
|
||||||
if href not in self.spine_map:
|
|
||||||
for url in self.manifest:
|
|
||||||
if url == href:
|
|
||||||
self.spine_map[href]= self.manifest[url][1]
|
|
||||||
self.spine.append(self.spine_map[href])
|
|
||||||
|
|
||||||
def render_toc(self, toc, level=2):
|
|
||||||
for child in toc:
|
|
||||||
if child.title and child.href:
|
|
||||||
href = child.href
|
|
||||||
self.add_to_spine(href)
|
|
||||||
title = escape(child.title)
|
|
||||||
if isinstance(title, unicode):
|
|
||||||
title = title.encode('utf-8')
|
|
||||||
if child.href in self._po_map:
|
|
||||||
po = self._po_map[child.href]
|
|
||||||
else:
|
|
||||||
self._po += 1
|
|
||||||
po = self._po
|
|
||||||
self._parts.append(' '*(level*4)+
|
|
||||||
'<navPoint id="%s" playOrder="%d">'%(uuid.uuid4(),
|
|
||||||
po))
|
|
||||||
self._parts.append(' '*((level+1)*4)+
|
|
||||||
'<navLabel><text>%s</text></navLabel>'%title)
|
|
||||||
self._parts.append(' '*((level+1)*4)+
|
|
||||||
'<content src=%s />'%quoteattr(href))
|
|
||||||
self.render_toc(child, level+1)
|
|
||||||
self._parts.append(' '*(level*4)+'</navPoint>')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def extend_toc(self, toc, node):
|
|
||||||
if self.isdocnode(node):
|
|
||||||
refnode = node.children[0][0]
|
|
||||||
parent = toc.create_child(refnode.astext(), refnode['refuri'])
|
|
||||||
for subnode in node.children[1]:
|
|
||||||
self.extend_toc(parent, subnode)
|
|
||||||
elif isinstance(node, (nodes.list_item, nodes.bullet_list,
|
|
||||||
addnodes.compact_paragraph)):
|
|
||||||
for subnode in node:
|
|
||||||
self.extend_toc(toc, subnode)
|
|
||||||
elif isinstance(node, nodes.reference):
|
|
||||||
parent = toc.create_child(node.astext(), node['refuri'])
|
|
||||||
|
|
||||||
|
|
||||||
|
def build_epub(self, outdir, *args, **kwargs):
|
||||||
|
if self.config.epub_cover:
|
||||||
|
self.add_cover(outdir, self.config.epub_cover)
|
||||||
|
EpubBuilder.build_epub(self, outdir, *args, **kwargs)
|
||||||
|
@ -6,145 +6,12 @@ API Documentation for recipes
|
|||||||
===============================
|
===============================
|
||||||
|
|
||||||
.. module:: calibre.web.feeds.news
|
.. module:: calibre.web.feeds.news
|
||||||
:synopsis: Defines various abstract base classes that can be subclassed to create powerful news fetching recipes.
|
:synopsis: The API for writing recipes is defined by the :class:`BasicNewsRecipe`
|
||||||
|
|
||||||
Defines various abstract base classes that can be subclassed to create powerful news fetching recipes. The useful
|
The API for writing recipes is defined by the :class:`BasicNewsRecipe`
|
||||||
subclasses are:
|
|
||||||
|
|
||||||
.. contents::
|
.. autoclass:: BasicNewsRecipe
|
||||||
:depth: 1
|
:members:
|
||||||
:local:
|
:member-order: groupwise
|
||||||
|
|
||||||
BasicNewsRecipe
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. class:: BasicNewsRecipe
|
|
||||||
|
|
||||||
Abstract base class that contains a number of members and methods to customize the fetching of contents in your recipes. All
|
|
||||||
recipes must inherit from this class or a subclass of it.
|
|
||||||
|
|
||||||
The members and methods are organized as follows:
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 1
|
|
||||||
:local:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Customizing e-book download
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.title
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.description
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.__author__
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.max_articles_per_feed
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.oldest_article
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.recursions
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.delay
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.simultaneous_downloads
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.timeout
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.timefmt
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.conversion_options
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.feeds
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.no_stylesheets
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.encoding
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.get_browser
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.get_cover_url
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.get_feeds
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.parse_index
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Customizing feed parsing
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.summary_length
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.use_embedded_content
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.get_article_url
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.print_version
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.parse_feeds
|
|
||||||
|
|
||||||
|
|
||||||
Pre/post processing of downloaded HTML
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.extra_css
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.match_regexps
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.filter_regexps
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.remove_tags
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.remove_tags_after
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.remove_tags_before
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.remove_attributes
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.keep_only_tags
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.preprocess_regexps
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.template_css
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.remove_javascript
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.skip_ad_pages
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.preprocess_html
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.postprocess_html
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.populate_article_metadata
|
|
||||||
|
|
||||||
|
|
||||||
Convenience methods
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.cleanup
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.index_to_soup
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.sort_index_by
|
|
||||||
|
|
||||||
.. automethod:: BasicNewsRecipe.tag_to_string
|
|
||||||
|
|
||||||
|
|
||||||
Miscellaneous
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.requires_version
|
|
||||||
|
|
||||||
|
|
||||||
CustomIndexRecipe
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
.. class:: CustomIndexRecipe
|
|
||||||
|
|
||||||
This class is useful for getting content from websites that don't follow the "multiple articles in several feeds" content model.
|
|
||||||
|
|
||||||
.. automethod:: CustomIndexRecipe.custom_index
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
API Documentation for plugins
|
API Documentation for plugins
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
.. module:: calibre.customize.__init__
|
.. module:: calibre.customize
|
||||||
:synopsis: Defines various abstract base classes that can be subclassed to create plugins.
|
:synopsis: Defines various abstract base classes that can be subclassed to create plugins.
|
||||||
|
|
||||||
Defines various abstract base classes that can be subclassed to create powerful plugins. The useful
|
Defines various abstract base classes that can be subclassed to create powerful plugins. The useful
|
||||||
@ -20,113 +20,136 @@ classes are:
|
|||||||
Plugin
|
Plugin
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
.. class:: Plugin
|
.. autoclass:: Plugin
|
||||||
|
:members:
|
||||||
Abstract base class that contains a number of members and methods to create your plugin. All
|
:member-order: bysource
|
||||||
plugins must inherit from this class or a subclass of it.
|
|
||||||
|
|
||||||
The members and methods are:
|
|
||||||
|
|
||||||
.. automember:: Plugin.name
|
|
||||||
|
|
||||||
.. automember:: Plugin.author
|
|
||||||
|
|
||||||
.. automember:: Plugin.description
|
|
||||||
|
|
||||||
.. automember:: Plugin.version
|
|
||||||
|
|
||||||
.. automember:: Plugin.supported_platforms
|
|
||||||
|
|
||||||
.. automember:: Plugin.priority
|
|
||||||
|
|
||||||
.. automember:: Plugin.minimum_calibre_version
|
|
||||||
|
|
||||||
.. automember:: Plugin.can_be_disabled
|
|
||||||
|
|
||||||
.. automethod:: Plugin.initialize
|
|
||||||
|
|
||||||
.. automethod:: Plugin.customization_help
|
|
||||||
|
|
||||||
.. automethod:: Plugin.temporary_file
|
|
||||||
|
|
||||||
.. _pluginsFTPlugin:
|
.. _pluginsFTPlugin:
|
||||||
|
|
||||||
FileTypePlugin
|
FileTypePlugin
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
.. class:: Plugin
|
.. autoclass:: FileTypePlugin
|
||||||
|
:show-inheritance:
|
||||||
Abstract base class that contains a number of members and methods to create your file type plugin. All file type
|
:members:
|
||||||
plugins must inherit from this class or a subclass of it.
|
:member-order: bysource
|
||||||
|
|
||||||
The members and methods are:
|
|
||||||
|
|
||||||
.. automember:: FileTypePlugin.file_types
|
|
||||||
|
|
||||||
.. automember:: FileTypePlugin.on_import
|
|
||||||
|
|
||||||
.. automember:: FileTypePlugin.on_preprocess
|
|
||||||
|
|
||||||
.. automember:: FileTypePlugin.on_postprocess
|
|
||||||
|
|
||||||
.. automethod:: FileTypePlugin.run
|
|
||||||
|
|
||||||
.. _pluginsMetadataPlugin:
|
.. _pluginsMetadataPlugin:
|
||||||
|
|
||||||
Metadata plugins
|
Metadata plugins
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
.. class:: MetadataReaderPlugin
|
.. autoclass:: MetadataReaderPlugin
|
||||||
|
:show-inheritance:
|
||||||
Abstract base class that contains a number of members and methods to create your metadata reader plugin. All metadata
|
:members:
|
||||||
reader plugins must inherit from this class or a subclass of it.
|
:member-order: bysource
|
||||||
|
|
||||||
The members and methods are:
|
|
||||||
|
|
||||||
.. automember:: MetadataReaderPlugin.file_types
|
|
||||||
|
|
||||||
.. automethod:: MetadataReaderPlugin.get_metadata
|
|
||||||
|
|
||||||
|
|
||||||
.. class:: MetadataWriterPlugin
|
.. autoclass:: MetadataWriterPlugin
|
||||||
|
:show-inheritance:
|
||||||
Abstract base class that contains a number of members and methods to create your metadata writer plugin. All metadata
|
:members:
|
||||||
writer plugins must inherit from this class or a subclass of it.
|
:member-order: bysource
|
||||||
|
|
||||||
The members and methods are:
|
|
||||||
|
|
||||||
.. automember:: MetadataWriterPlugin.file_types
|
|
||||||
|
|
||||||
.. automethod:: MetadataWriterPlugin.set_metadata
|
|
||||||
|
|
||||||
|
|
||||||
.. _pluginsMetadataSource:
|
.. _pluginsMetadataSource:
|
||||||
|
|
||||||
|
Catalog plugins
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. autoclass:: CatalogPlugin
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
|
||||||
Metadata download plugins
|
Metadata download plugins
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
.. class:: calibre.ebooks.metadata.fetch.MetadataSource
|
.. module:: calibre.ebooks.metadata.fetch
|
||||||
|
|
||||||
Represents a source to query for metadata. Subclasses must implement
|
.. autoclass:: MetadataSource
|
||||||
at least the fetch method.
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
When :meth:`fetch` is called, the `self` object will have the following
|
Conversion plugins
|
||||||
useful attributes (each of which may be None)::
|
--------------------
|
||||||
|
|
||||||
title, book_author, publisher, isbn, log, verbose and extra
|
.. module:: calibre.customize.conversion
|
||||||
|
|
||||||
Use these attributes to construct the search query. extra is reserved for
|
.. autoclass:: InputFormatPlugin
|
||||||
future use.
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
The fetch method must store the results in `self.results` as a list of
|
.. autoclass:: OutputFormatPlugin
|
||||||
:class:`MetaInformation` objects. If there is an error, it should be stored
|
:show-inheritance:
|
||||||
in `self.exception` and `self.tb` (for the traceback).
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
.. automember:: calibre.ebooks.metadata.fetch.MetadataSource.metadata_type
|
Device Drivers
|
||||||
|
-----------------
|
||||||
|
|
||||||
.. automember:: calibre.ebooks.metadata.fetch.MetadataSource.string_customization_help
|
.. module:: calibre.devices.interface
|
||||||
|
|
||||||
.. automethod:: calibre.ebooks.metadata.fetch.MetadataSource.fetch
|
The base class for all device drivers is :class:`DevicePlugin`. However, if your device exposes itself as a USBMS drive to the operating system, you should use the USBMS class instead as it implements all the logic needed to support these kinds of devices.
|
||||||
|
|
||||||
|
.. autoclass:: DevicePlugin
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
.. autoclass:: BookList
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
|
||||||
|
USB Mass Storage based devices
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The base class for such devices is :class:`calibre.devices.usbms.driver.USBMS`. This class in turn inherits some of its functionality from its bases, documented below. A typical basic USBMS based driver looks like this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
|
class PDNOVEL(USBMS):
|
||||||
|
name = 'Pandigital Novel device interface'
|
||||||
|
gui_name = 'PD Novel'
|
||||||
|
description = _('Communicate with the Pandigital Novel')
|
||||||
|
author = 'Kovid Goyal'
|
||||||
|
supported_platforms = ['windows', 'linux', 'osx']
|
||||||
|
FORMATS = ['epub', 'pdf']
|
||||||
|
|
||||||
|
VENDOR_ID = [0x18d1]
|
||||||
|
PRODUCT_ID = [0xb004]
|
||||||
|
BCD = [0x224]
|
||||||
|
|
||||||
|
VENDOR_NAME = 'ANDROID'
|
||||||
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '__UMS_COMPOSITE'
|
||||||
|
THUMBNAIL_HEIGHT = 144
|
||||||
|
|
||||||
|
EBOOK_DIR_MAIN = 'eBooks'
|
||||||
|
SUPPORTS_SUB_DIRS = False
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
.. autoclass:: calibre.devices.usbms.device.Device
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
.. autoclass:: calibre.devices.usbms.cli.CLI
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
.. autoclass:: calibre.devices.usbms.driver.USBMS
|
||||||
|
:show-inheritance:
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
@import url("default.css");
|
|
||||||
|
|
||||||
table.docutils td, table.docutils th { padding: 1em; border-bottom: 0; }
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 10 KiB |
@ -312,10 +312,10 @@ class OptionSet(object):
|
|||||||
|
|
||||||
def parse_string(self, src):
|
def parse_string(self, src):
|
||||||
options = {'cPickle':cPickle}
|
options = {'cPickle':cPickle}
|
||||||
if not isinstance(src, unicode):
|
|
||||||
src = src.decode('utf-8')
|
|
||||||
if src is not None:
|
if src is not None:
|
||||||
try:
|
try:
|
||||||
|
if not isinstance(src, unicode):
|
||||||
|
src = src.decode('utf-8')
|
||||||
exec src in options
|
exec src in options
|
||||||
except:
|
except:
|
||||||
print 'Failed to parse options string:'
|
print 'Failed to parse options string:'
|
||||||
|
@ -115,7 +115,7 @@ class Feed(object):
|
|||||||
max_articles_per_feed=100):
|
max_articles_per_feed=100):
|
||||||
entries = feed.entries
|
entries = feed.entries
|
||||||
feed = feed.feed
|
feed = feed.feed
|
||||||
self.title = feed.get('title', _('Unknown feed')) if not title else title
|
self.title = feed.get('title', _('Unknown section')) if not title else title
|
||||||
self.description = feed.get('description', '')
|
self.description = feed.get('description', '')
|
||||||
image = feed.get('image', {})
|
image = feed.get('image', {})
|
||||||
self.image_url = image.get('href', None)
|
self.image_url = image.get('href', None)
|
||||||
|
@ -37,7 +37,10 @@ class DownloadDenied(ValueError):
|
|||||||
|
|
||||||
class BasicNewsRecipe(Recipe):
|
class BasicNewsRecipe(Recipe):
|
||||||
'''
|
'''
|
||||||
Abstract base class that contains logic needed in all feed fetchers.
|
Base class that contains logic needed in all recipes. By overriding
|
||||||
|
progressively more of the functionality in this class, you can make
|
||||||
|
progressively more customized/powerful recipes. For a tutorial introduction
|
||||||
|
to creating recipes, see :doc:`news`.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
#: The title to use for the ebook
|
#: The title to use for the ebook
|
||||||
@ -127,7 +130,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
#: embedded content.
|
#: embedded content.
|
||||||
use_embedded_content = None
|
use_embedded_content = None
|
||||||
|
|
||||||
#: Set to True and implement :method:`get_obfuscated_article` to handle
|
#: Set to True and implement :meth:`get_obfuscated_article` to handle
|
||||||
#: websites that try to make it difficult to scrape content.
|
#: websites that try to make it difficult to scrape content.
|
||||||
articles_are_obfuscated = False
|
articles_are_obfuscated = False
|
||||||
|
|
||||||
@ -147,7 +150,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
#: If True empty feeds are removed from the output.
|
#: If True empty feeds are removed from the output.
|
||||||
#: This option has no effect if parse_index is overriden in
|
#: This option has no effect if parse_index is overriden in
|
||||||
#: the sub class. It is meant only for recipes that return a list
|
#: the sub class. It is meant only for recipes that return a list
|
||||||
#: of feeds using `feeds` or :method:`get_feeds`.
|
#: of feeds using `feeds` or :meth:`get_feeds`.
|
||||||
remove_empty_feeds = False
|
remove_empty_feeds = False
|
||||||
|
|
||||||
#: List of regular expressions that determines which links to follow
|
#: List of regular expressions that determines which links to follow
|
||||||
@ -538,8 +541,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
HTML fetching engine, so it can contain links to pages/images on the web.
|
HTML fetching engine, so it can contain links to pages/images on the web.
|
||||||
|
|
||||||
This method is typically useful for sites that try to make it difficult to
|
This method is typically useful for sites that try to make it difficult to
|
||||||
access article content automatically. See for example the
|
access article content automatically.
|
||||||
:module:`calibre.web.recipes.iht` recipe.
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -700,8 +702,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
Download and pre-process all articles from the feeds in this recipe.
|
Download and pre-process all articles from the feeds in this recipe.
|
||||||
This method should be called only once on a particular Recipe instance.
|
This method should be called only once on a particular Recipe instance.
|
||||||
Calling it more than once will lead to undefined behavior.
|
Calling it more than once will lead to undefined behavior.
|
||||||
@return: Path to index.html
|
:return: Path to index.html
|
||||||
@rtype: string
|
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
res = self.build_index()
|
res = self.build_index()
|
||||||
@ -1359,7 +1360,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
'''
|
'''
|
||||||
If your recipe when converted to EPUB has problems with images when
|
If your recipe when converted to EPUB has problems with images when
|
||||||
viewed in Adobe Digital Editions, call this method from within
|
viewed in Adobe Digital Editions, call this method from within
|
||||||
:method:`postprocess_html`.
|
:meth:`postprocess_html`.
|
||||||
'''
|
'''
|
||||||
for item in soup.findAll('img'):
|
for item in soup.findAll('img'):
|
||||||
for attrib in ['height','width','border','align','style']:
|
for attrib in ['height','width','border','align','style']:
|
||||||
|
@ -103,6 +103,32 @@ class IndexTemplate(Template):
|
|||||||
|
|
||||||
class FeedTemplate(Template):
|
class FeedTemplate(Template):
|
||||||
|
|
||||||
|
def get_navbar(self, f, feeds, top=True):
|
||||||
|
if len(feeds) < 2:
|
||||||
|
return DIV()
|
||||||
|
navbar = DIV('| ', CLASS('calibre_navbar', 'calibre_rescale_70',
|
||||||
|
style='text-align:center'))
|
||||||
|
if not top:
|
||||||
|
hr = HR()
|
||||||
|
navbar.append(hr)
|
||||||
|
navbar.text = None
|
||||||
|
hr.tail = '| '
|
||||||
|
|
||||||
|
if f+1 < len(feeds):
|
||||||
|
link = A('Next section', href='../feed_%d/index.html'%(f+1))
|
||||||
|
link.tail = ' | '
|
||||||
|
navbar.append(link)
|
||||||
|
link = A('Main menu', href="../index.html")
|
||||||
|
link.tail = ' | '
|
||||||
|
navbar.append(link)
|
||||||
|
if f > 0:
|
||||||
|
link = A('Previous section', href='../feed_%d/index.html'%(f-1))
|
||||||
|
link.tail = ' |'
|
||||||
|
navbar.append(link)
|
||||||
|
if top:
|
||||||
|
navbar.append(HR())
|
||||||
|
return navbar
|
||||||
|
|
||||||
def _generate(self, f, feeds, cutoff, extra_css=None, style=None):
|
def _generate(self, f, feeds, cutoff, extra_css=None, style=None):
|
||||||
feed = feeds[f]
|
feed = feeds[f]
|
||||||
head = HEAD(TITLE(feed.title))
|
head = HEAD(TITLE(feed.title))
|
||||||
@ -111,6 +137,8 @@ class FeedTemplate(Template):
|
|||||||
if extra_css:
|
if extra_css:
|
||||||
head.append(STYLE(extra_css, type='text/css'))
|
head.append(STYLE(extra_css, type='text/css'))
|
||||||
body = BODY(style='page-break-before:always')
|
body = BODY(style='page-break-before:always')
|
||||||
|
body.append(self.get_navbar(f, feeds))
|
||||||
|
|
||||||
div = DIV(
|
div = DIV(
|
||||||
H2(feed.title,
|
H2(feed.title,
|
||||||
CLASS('calibre_feed_title', 'calibre_rescale_160')),
|
CLASS('calibre_feed_title', 'calibre_rescale_160')),
|
||||||
@ -144,12 +172,7 @@ class FeedTemplate(Template):
|
|||||||
CLASS('article_description', 'calibre_rescale_70')))
|
CLASS('article_description', 'calibre_rescale_70')))
|
||||||
ul.append(li)
|
ul.append(li)
|
||||||
div.append(ul)
|
div.append(ul)
|
||||||
navbar = DIV('| ', CLASS('calibre_navbar', 'calibre_rescale_70'))
|
div.append(self.get_navbar(f, feeds, top=False))
|
||||||
link = A('Up one level', href="../index.html")
|
|
||||||
link.tail = ' |'
|
|
||||||
navbar.append(link)
|
|
||||||
div.append(navbar)
|
|
||||||
|
|
||||||
self.root = HTML(head, body)
|
self.root = HTML(head, body)
|
||||||
|
|
||||||
class NavBarTemplate(Template):
|
class NavBarTemplate(Template):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user