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"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
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:svg="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:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
width="48"
|
||||
height="48"
|
||||
id="svg2160"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.45.1"
|
||||
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" />
|
||||
width="128"
|
||||
height="128"
|
||||
id="svg4486"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="epub.svg">
|
||||
<metadata
|
||||
id="metadata7">
|
||||
id="metadata52">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</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
|
||||
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
|
||||
id="layer1">
|
||||
<image
|
||||
id="image2226"
|
||||
width="48"
|
||||
y="0"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI
|
||||
WXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAwAAAAMADO7oxXAAALJUlEQVRo3u2ae3DTVRbHP3k1
|
||||
v6ZJk4a+W1NalAJSy4qtvKS4iCKOYtcHvvCxDuuMozu62vVRdGcRfOzsODoq7o4jyoyy49aFqYoO
|
||||
7rJjaREsKi2F0sC2CNImado82ib5/fJo9o+0vzS2pe2i4B97/rq553fu7/u9597zu+fcKB56+OFo
|
||||
bnY2fr8fKRgkIIpIosiwaAUBAK/HA8A0s1nW6fV6RsrGTZsUnGNRPL9pU/T3TzyZ0OnxuEe0PUiS
|
||||
hBiIkero6MDb50UMBJCCQex2OwFRRK1UyjY+vx+320NamglRFDGaTLz+2ms/CTn1WJ0mU9qYbYDS
|
||||
efPGHWyY+GPbtuNtP8ZTa29HCkp8unMn66uroz+Fh9RnP0RcPm5u5s26Iyj1M3nghhky2dbWVg63
|
||||
tPzY2M+ewMleD0fajnGk8yQ72rpR6meSlrsU6/F/smzWHT8J4EkR2NnUwvYvGznaI57RWKmfiV6n
|
||||
J90wl5yCKGIogj8cpaLAiEGrPX8ENn20h7WrbuayEX2u/hAAPilMMDRIIDSIFB5EDEUIhOPgHQMe
|
||||
VllSzwl4AOVkHposeF84QtD5NeuuvurnQ2Aq4AfEfioKjOcM/IQEpgLeGx7E2dPOQ1cv+3kQmDL4
|
||||
cJQbLIMUppsn/fKflMBUwfedbuaxFVeeU/BnJDAl8JFB7pkJuRnxr7YoipMCcNYE/H7/mIqpgKdj
|
||||
d8LsnyvwcAYPTBq8388Lyy9khiX350VgUuCBctURfrv6+kRbSTr/BCYDPs19hDfuWDnaNvAz8MBE
|
||||
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" />
|
||||
transform="matrix(1.0408163,0,0,0.6302428,-1.5714269,43.690218)"
|
||||
id="g2478">
|
||||
<rect
|
||||
width="83.299995"
|
||||
height="9.5201406"
|
||||
x="21.349998"
|
||||
y="113.14653"
|
||||
id="rect2879"
|
||||
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" />
|
||||
<path
|
||||
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"
|
||||
id="path2881"
|
||||
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" />
|
||||
<path
|
||||
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"
|
||||
id="path2883"
|
||||
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" />
|
||||
</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>
|
||||
|
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'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
sp.rian.ru
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Ria_eng(BasicNewsRecipe):
|
||||
class Ria_esp(BasicNewsRecipe):
|
||||
title = 'Ria Novosti'
|
||||
__author__ = 'Darko Miletic'
|
||||
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_after = dict(name='div',attrs={'class':'text'})
|
||||
|
||||
|
||||
feeds = [(u'Noticias', u'http://sp.rian.ru/export/rss2/index.xml')]
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('.html','-print.html')
|
||||
feeds = [(u'Noticias', u'http://rss.feedsportal.com/c/860/fe.ed/sp.rian.ru/export/rss2/index.xml')]
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class TagesspiegelRSS(BasicNewsRecipe):
|
||||
title = u'Der Tagesspiegel'
|
||||
__author__ = 'ipaschke'
|
||||
__author__ = 'Ingo Paschke'
|
||||
language = 'de'
|
||||
oldest_article = 7
|
||||
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='div', attrs={'class':["hcf-jump-to-comments","hcf-clear","hcf-magnify hcf-media-control"] }),
|
||||
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):
|
||||
soup = self.index_to_soup('http://www.tagesspiegel.de/zeitung/')
|
||||
|
||||
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 = {}
|
||||
key = None
|
||||
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':
|
||||
try:
|
||||
key = string.capwords(feed_title(div.em.a))
|
||||
articles[key] = []
|
||||
ans.append(key)
|
||||
except:
|
||||
continue
|
||||
|
||||
elif div['class'] == 'hcf-teaser' and getattr(div.contents[0],'name','') == 'h2':
|
||||
a = div.find('a', href=True)
|
||||
@ -84,3 +89,4 @@ class TagesspiegelRSS(BasicNewsRecipe):
|
||||
|
||||
return ans
|
||||
|
||||
|
||||
|
@ -50,12 +50,14 @@ class cdnet(BasicNewsRecipe):
|
||||
dict(name='div', attrs={'class':'greyBoxR clearfix'}),
|
||||
dict(name='div', attrs={'class':'greyBoxL clearfix'}),
|
||||
dict(name='div', attrs={'class':'greyBox clearfix'}),
|
||||
dict(name='div', attrs={'class':'labelized'}),
|
||||
dict(id='')]
|
||||
#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'})]
|
||||
|
||||
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'})]
|
||||
|
||||
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' )]
|
||||
|
||||
|
@ -22,7 +22,7 @@ class weltDe(BasicNewsRecipe):
|
||||
remove_stylesheets = True
|
||||
remove_javascript = True
|
||||
encoding = 'utf-8'
|
||||
html2epub_options = 'linearize_tables = True\nbase_font_size2=10'
|
||||
html2epub_options = 'base_font_size=10'
|
||||
BasicNewsRecipe.summary_length = 100
|
||||
|
||||
|
||||
@ -83,10 +83,9 @@ class weltDe(BasicNewsRecipe):
|
||||
dict(name='div', attrs={'class':'articleOptions clear'}),
|
||||
dict(name='div', attrs={'class':'noPrint galleryIndex'}),
|
||||
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 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':'jqmWindow'}),
|
||||
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':'print'}),
|
||||
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='a', attrs={'class':'commentLink'}),
|
||||
dict(name='h2', attrs={'class':'jumpHeading'}),
|
||||
@ -110,7 +109,7 @@ class weltDe(BasicNewsRecipe):
|
||||
dict(name='table', attrs={'class':'textGallery'}),
|
||||
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 = '''
|
||||
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;} '''
|
||||
|
||||
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'),
|
||||
('Finanzen', 'http://welt.de/finanzen/?service=Rss'),
|
||||
('Sport', 'http://welt.de/sport/?service=Rss'),
|
||||
@ -137,3 +137,4 @@ class weltDe(BasicNewsRecipe):
|
||||
def print_version(self, url):
|
||||
return url.replace ('.html', '.html?print=true')
|
||||
|
||||
|
||||
|
@ -6,88 +6,105 @@ Fetch Die Zeit.
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
from calibre.ebooks.BeautifulSoup import Tag
|
||||
|
||||
class ZeitDe(BasicNewsRecipe):
|
||||
|
||||
title = 'Die Zeit Nachrichten'
|
||||
description = 'Die Zeit - Online Nachrichten'
|
||||
title = 'ZEIT Online Reader Edition'
|
||||
description = 'ZEIT Online'
|
||||
language = 'de'
|
||||
lang = 'de_DE'
|
||||
|
||||
__author__ = 'Martin Pitt and Sujata Raman'
|
||||
__author__ = 'Martin Pitt, Sujata Raman and Ingo Paschke'
|
||||
use_embedded_content = False
|
||||
max_articles_per_feed = 40
|
||||
max_articles_per_feed = 100
|
||||
remove_empty_feeds = True
|
||||
no_stylesheets = True
|
||||
no_javascript = True
|
||||
encoding = 'utf-8'
|
||||
|
||||
delay = 0
|
||||
|
||||
feeds = [
|
||||
('Politik', 'http://newsfeed.zeit.de/politik/index'),
|
||||
('Wirtschaft', 'http://newsfeed.zeit.de/wirtschaft/index'),
|
||||
('Meinung', 'http://newsfeed.zeit.de/meinung/index'),
|
||||
('Gesellschaft', 'http://newsfeed.zeit.de/gesellschaft/index'),
|
||||
('Kultur', 'http://newsfeed.zeit.de/kultur/index'),
|
||||
('Wissen', 'http://newsfeed.zeit.de/wissen/index'),
|
||||
('Seite 1', 'http://newsfeed.zeit.de/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'),
|
||||
('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'),
|
||||
('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'),
|
||||
('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'),
|
||||
('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 = '''
|
||||
.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;}
|
||||
.title{font-family:Arial,Helvetica,sans-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;clear:right;}
|
||||
.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;}
|
||||
.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}
|
||||
.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/']
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':["article"]}) ,
|
||||
dict(name='ul', attrs={'class':["tools"]}) ,
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name='link'), dict(name='iframe'),dict(name='style'),
|
||||
dict(name='div', attrs={'class':["pagination block","pagenav","inline link"] }),
|
||||
dict(name='div', attrs={'id':["place_5","place_4"]})
|
||||
dict(name='link'), dict(name='iframe'),dict(name='style'),dict(name='meta'),
|
||||
dict(name='div', attrs={'class':["pagination block","pagenav","inline link", "copyright"] }),
|
||||
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):
|
||||
ans = article.get('link',None)
|
||||
ans += "?page=all"
|
||||
|
||||
ans = article.get('guid',None)
|
||||
|
||||
try:
|
||||
self.log('Looking for full story link in', ans)
|
||||
soup = self.index_to_soup(ans)
|
||||
x = soup.find(text="Auf einer Seite lesen")
|
||||
|
||||
if x is not None:
|
||||
|
||||
a = x.parent
|
||||
if a and a.has_key('href'):
|
||||
ans = a['href']
|
||||
self.log('Found full story link', ans)
|
||||
except:
|
||||
pass
|
||||
|
||||
if 'video' in ans or 'quiz' in ans :
|
||||
|
||||
if 'video' in ans or 'quiz' or 'blog.zeit.de/' in ans :
|
||||
ans = None
|
||||
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):
|
||||
soup.html['xml:lang'] = self.lang
|
||||
soup.html['lang'] = self.lang
|
||||
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
|
||||
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
|
||||
|
||||
|
||||
#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)
|
||||
if not os.path.exists('.build'+os.sep+'html'):
|
||||
os.makedirs('.build'+os.sep+'html')
|
||||
os.environ['__appname__']= __appname__
|
||||
os.environ['__version__']= __version__
|
||||
subprocess.check_call(['sphinx-build', '-b', 'custom', '-t', 'online',
|
||||
os.environ['__appname__'] = __appname__
|
||||
os.environ['__version__'] = __version__
|
||||
subprocess.check_call(['sphinx-build', '-b', 'html', '-t', 'online',
|
||||
'-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'])
|
||||
shutil.copyfile(self.j('.build', 'epub', 'calibre.epub'), self.j('.build',
|
||||
'html', 'calibre.epub'))
|
||||
|
@ -262,31 +262,21 @@ class CatalogPlugin(Plugin):
|
||||
|
||||
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
|
||||
#: Option = namedtuple('Option', 'option, default, dest, help')
|
||||
#: cli_options = [Option('--catalog-title',
|
||||
#: from collections import namedtuple
|
||||
#: Option = namedtuple('Option', 'option, default, dest, help')
|
||||
#: cli_options = [Option('--catalog-title',
|
||||
#: default = 'My Catalog',
|
||||
#: dest = 'catalog_title',
|
||||
#: help = (_('Title of generated catalog. \nDefault:') + " '" +
|
||||
#: '%default' + "'"))]
|
||||
#: cli_options parsed in library.cli:catalog_option_parser()
|
||||
|
||||
#: cli_options parsed in library.cli:catalog_option_parser()
|
||||
cli_options = []
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if opts.sort_by:
|
||||
@ -349,8 +339,7 @@ class CatalogPlugin(Plugin):
|
||||
It should generate the catalog in the format specified
|
||||
in file_types, returning the absolute path to the
|
||||
generated catalog file. If an error is encountered
|
||||
it should raise an Exception and return None. The default
|
||||
implementation simply returns None.
|
||||
it should raise an Exception.
|
||||
|
||||
The generated catalog file should be created with the
|
||||
:meth:`temporary_file` method.
|
||||
@ -358,9 +347,6 @@ class CatalogPlugin(Plugin):
|
||||
:param path_to_output: Absolute path to the generated catalog file.
|
||||
:param opts: A dictionary of keyword arguments
|
||||
:param db: A LibraryDatabase2 object
|
||||
|
||||
:return: None
|
||||
|
||||
'''
|
||||
# Default implementation does nothing
|
||||
raise NotImplementedError('CatalogPlugin.generate_catalog() default '
|
||||
|
@ -28,7 +28,7 @@ class ConversionOption(object):
|
||||
|
||||
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:
|
||||
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
|
||||
HTML+OPF+CSS+etc.
|
||||
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')
|
||||
@ -109,7 +109,7 @@ class InputFormatPlugin(Plugin):
|
||||
|
||||
#: If True, this input plugin generates a collection of images,
|
||||
#: 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
|
||||
|
||||
#: If set to True, the input plugin will perform special processing
|
||||
@ -117,7 +117,7 @@ class InputFormatPlugin(Plugin):
|
||||
for_viewer = False
|
||||
|
||||
#: 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`.
|
||||
common_options = set([
|
||||
OptionRecommendation(name='input_encoding',
|
||||
@ -173,7 +173,6 @@ class InputFormatPlugin(Plugin):
|
||||
returns.
|
||||
|
||||
:param stream: A file like object that contains the input file.
|
||||
|
||||
:param options: Options to customize the conversion process.
|
||||
Guaranteed to have attributes corresponding
|
||||
to all the options declared by this plugin. In
|
||||
@ -182,14 +181,11 @@ class InputFormatPlugin(Plugin):
|
||||
mean be more verbose. Another useful attribute is
|
||||
``input_profile`` that is an instance of
|
||||
:class:`calibre.customize.profiles.InputProfile`.
|
||||
|
||||
:param file_ext: The extension (without the .) of the input file. It
|
||||
is guaranteed to be one of the `file_types` supported
|
||||
by this plugin.
|
||||
|
||||
:param log: A :class:`calibre.utils.logging.Log` object. All output
|
||||
should use this object.
|
||||
|
||||
:param accelarators: A dictionary of various information that the input
|
||||
plugin can get easily that would speed up the
|
||||
subsequent stages of the conversion.
|
||||
@ -235,7 +231,7 @@ class OutputFormatPlugin(Plugin):
|
||||
(OPF+HTML) into an output ebook.
|
||||
|
||||
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')
|
||||
@ -247,7 +243,7 @@ class OutputFormatPlugin(Plugin):
|
||||
file_type = None
|
||||
|
||||
#: 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`.
|
||||
common_options = set([
|
||||
OptionRecommendation(name='pretty_print',
|
||||
@ -277,17 +273,15 @@ class OutputFormatPlugin(Plugin):
|
||||
: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
|
||||
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
|
||||
object, the output plugin should write its output into the file.
|
||||
|
||||
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
|
||||
object, the output plugin should write its output into the file.
|
||||
: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
|
||||
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.
|
||||
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -1,10 +1,5 @@
|
||||
__license__ = 'GPL v3'
|
||||
__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
|
||||
from collections import namedtuple
|
||||
|
||||
@ -15,32 +10,38 @@ class DevicePlugin(Plugin):
|
||||
"""
|
||||
Defines the interface that should be implemented by backends that
|
||||
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')
|
||||
|
||||
# Ordered list of supported formats
|
||||
#: Ordered list of supported formats
|
||||
FORMATS = ["lrf", "rtf", "pdf", "txt"]
|
||||
|
||||
#: 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
|
||||
#: {
|
||||
#: integer_vendor_id : { product_id : [list of BCDs], ... },
|
||||
#: ...
|
||||
#: }
|
||||
#: If it is a dictionary, it must be a dictionary of dictionaries,
|
||||
#: of the form::
|
||||
#:
|
||||
#: {
|
||||
#: integer_vendor_id : { product_id : [list of BCDs], ... },
|
||||
#: ...
|
||||
#: }
|
||||
#:
|
||||
VENDOR_ID = 0x0000
|
||||
|
||||
#: An integer or a list of integers
|
||||
PRODUCT_ID = 0x0000
|
||||
# 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.
|
||||
#: 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.
|
||||
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
|
||||
|
||||
#: Path separator for paths to books on device
|
||||
path_sep = os.sep
|
||||
|
||||
#: Icon for this device
|
||||
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.
|
||||
|
||||
:param devices_on_system: List of devices currently connected
|
||||
|
||||
'''
|
||||
if iswindows:
|
||||
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,
|
||||
detected_device=None) :
|
||||
"""
|
||||
:key: The key to unlock the device
|
||||
:log_packets: If true the packet stream to/from the device is logged
|
||||
:report_progress: Function that is called with a % progress
|
||||
:param key: The key to unlock the device
|
||||
:param log_packets: If true the packet stream to/from the device is logged
|
||||
:param report_progress: Function that is called with a % progress
|
||||
(number between 0 and 100) for various tasks
|
||||
If it is called with -1 that means that the
|
||||
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()
|
||||
|
||||
@ -174,19 +177,21 @@ class DevicePlugin(Plugin):
|
||||
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
|
||||
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
|
||||
``(vendor_id, product_id, bcd)``.
|
||||
``(vendor_id, product_id, bcd)``.
|
||||
|
||||
'''
|
||||
return True
|
||||
|
||||
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,
|
||||
serial number)
|
||||
serial number)
|
||||
|
||||
'''
|
||||
|
||||
return True
|
||||
@ -198,7 +203,8 @@ class DevicePlugin(Plugin):
|
||||
For example: For devices that present themselves as USB Mass storage
|
||||
devices, this method would be responsible for mounting the device or
|
||||
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
|
||||
devices.
|
||||
'''
|
||||
@ -219,17 +225,20 @@ class DevicePlugin(Plugin):
|
||||
|
||||
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
|
||||
If it is called with -1 that means that the
|
||||
task does not have any progress information
|
||||
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_device_information(self, end_session=True):
|
||||
"""
|
||||
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()
|
||||
|
||||
@ -252,8 +261,9 @@ class DevicePlugin(Plugin):
|
||||
2. Memory Card A
|
||||
3. Memory Card B
|
||||
|
||||
@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.
|
||||
: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.
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -264,19 +274,23 @@ class DevicePlugin(Plugin):
|
||||
2. Card A
|
||||
3. Card B
|
||||
|
||||
@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.
|
||||
: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.
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def books(self, oncard=None, end_session=True):
|
||||
"""
|
||||
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
|
||||
in main memory of device. If a card is specified and no
|
||||
books are on the card return empty list.
|
||||
@return: A BookList.
|
||||
|
||||
:return: A BookList.
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -285,25 +299,27 @@ class DevicePlugin(Plugin):
|
||||
'''
|
||||
Upload a list of books to the device. If a file already
|
||||
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
|
||||
word "card" if C{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
|
||||
attribute, original_file_path pointing to the originals. They may have
|
||||
another optional attribute, deleted_after_upload which if True means
|
||||
that the file pointed to by original_file_path will be deleted after
|
||||
being uploaded to the device.
|
||||
:names: A list of file names that the books should have
|
||||
once uploaded to the device. len(names) == len(files)
|
||||
word "card" if ``on_card`` is not None otherwise it must contain the word "memory".
|
||||
|
||||
:param 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
|
||||
attribute, original_file_path pointing to the originals. They may have
|
||||
another optional attribute, deleted_after_upload which if True means
|
||||
that the file pointed to by original_file_path will be deleted after
|
||||
being uploaded to the device.
|
||||
: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
|
||||
to L{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).
|
||||
to :meth:`add_books_to_metadata`.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -312,12 +328,15 @@ class DevicePlugin(Plugin):
|
||||
'''
|
||||
Add locations to the booklists. This function must not communicate with
|
||||
the device.
|
||||
@param locations: Result of a call to L{upload_books}
|
||||
@param metadata: List of MetaInformation objects, same as for
|
||||
:method:`upload_books`.
|
||||
@param booklists: A tuple containing the result of calls to
|
||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
||||
L{books}(oncard='cardb')).
|
||||
|
||||
:param locations: Result of a call to L{upload_books}
|
||||
:param metadata: List of :class:`MetaInformation` objects, same as for
|
||||
:meth:`upload_books`.
|
||||
:param booklists: A tuple containing the result of calls to
|
||||
(:meth:`books(oncard=None)`,
|
||||
:meth:`books(oncard='carda')`,
|
||||
:meth`books(oncard='cardb')`).
|
||||
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@ -332,26 +351,35 @@ class DevicePlugin(Plugin):
|
||||
'''
|
||||
Remove books from the metadata list. This function must not communicate
|
||||
with the device.
|
||||
@param paths: paths to books on the device.
|
||||
@param booklists: A tuple containing the result of calls to
|
||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
||||
L{books}(oncard='cardb')).
|
||||
|
||||
:param paths: paths to books on the device.
|
||||
:param booklists: A tuple containing the result of calls to
|
||||
(:meth:`books(oncard=None)`,
|
||||
:meth:`books(oncard='carda')`,
|
||||
:meth`books(oncard='cardb')`).
|
||||
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def sync_booklists(self, booklists, end_session=True):
|
||||
'''
|
||||
Update metadata on device.
|
||||
@param booklists: A tuple containing the result of calls to
|
||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
||||
L{books}(oncard='cardb')).
|
||||
|
||||
:param booklists: A tuple containing the result of calls to
|
||||
(:meth:`books(oncard=None)`,
|
||||
:meth:`books(oncard='carda')`,
|
||||
:meth`books(oncard='cardb')`).
|
||||
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_file(self, path, outfile, end_session=True):
|
||||
'''
|
||||
Read the file at C{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
|
||||
Read the file at ``path`` on the device and write it to outfile.
|
||||
|
||||
:param outfile: file object like ``sys.stdout`` or the result of an
|
||||
:func:`open` call.
|
||||
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -365,8 +393,8 @@ class DevicePlugin(Plugin):
|
||||
@classmethod
|
||||
def save_settings(cls, settings_widget):
|
||||
'''
|
||||
Should save settings to disk. Takes the widget created in config_widget
|
||||
and saves all settings to disk.
|
||||
Should save settings to disk. Takes the widget created in
|
||||
:meth:`config_widget` and saves all settings to disk.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -381,16 +409,18 @@ class DevicePlugin(Plugin):
|
||||
|
||||
class BookList(list):
|
||||
'''
|
||||
A list of books. Each Book object must have the fields:
|
||||
1. title
|
||||
2. authors
|
||||
3. size (file size of the book)
|
||||
4. datetime (a UTC time tuple)
|
||||
5. path (path on the device to the book)
|
||||
6. thumbnail (can be None) thumbnail is either a str/bytes object with the
|
||||
A list of books. Each Book object must have the fields
|
||||
|
||||
#. title
|
||||
#. authors
|
||||
#. size (file size of the book)
|
||||
#. datetime (a UTC time tuple)
|
||||
#. 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
|
||||
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
|
||||
@ -427,6 +457,7 @@ class BookList(list):
|
||||
created from series, in which case series_index is used.
|
||||
|
||||
:param collection_attributes: A list of attributes of the Book object
|
||||
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
|
@ -46,7 +46,11 @@ def strptime(src):
|
||||
return time.strptime(' '.join(src), '%w, %d %m %Y %H:%M:%S %Z')
|
||||
|
||||
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[2] = INVERSE_MONTH_MAP[int(src[2])]
|
||||
return ' '.join(src)
|
||||
@ -328,7 +332,10 @@ class XMLCache(object):
|
||||
'descendant::*[local-name()="jpeg"]|'
|
||||
'descendant::*[local-name()="png"]'):
|
||||
if img.text:
|
||||
raw = b64decode(img.text.strip())
|
||||
try:
|
||||
raw = b64decode(img.text.strip())
|
||||
except:
|
||||
continue
|
||||
book.thumbnail = raw
|
||||
break
|
||||
break
|
||||
|
@ -47,8 +47,8 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
|
||||
'''
|
||||
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
|
||||
class.
|
||||
as USB Mass Storage devices. Provides implementations for mounting/ejecting
|
||||
of USBMS devices on all platforms.
|
||||
'''
|
||||
|
||||
VENDOR_ID = 0x0
|
||||
@ -57,9 +57,19 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
|
||||
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
|
||||
|
||||
#: 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
|
||||
|
||||
#: 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
|
||||
|
||||
# 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_B_MEM = None
|
||||
|
||||
# Used by the new driver detection to disambiguate main memory from
|
||||
# storage cards. Should be a regular expression that matches the
|
||||
# main memory mount point assigned by OS X
|
||||
#: Used by the new driver detection to disambiguate main memory from
|
||||
#: storage cards. Should be a regular expression that matches the
|
||||
#: main memory mount point assigned by OS X
|
||||
OSX_MAIN_MEM_VOL_PAT = None
|
||||
OSX_EJECT_COMMAND = ['diskutil', 'eject']
|
||||
|
||||
@ -780,7 +790,7 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
def filename_callback(self, default, mi):
|
||||
'''
|
||||
Callback to allow drivers to change the default file name
|
||||
set by :method:`create_upload_path`.
|
||||
set by :meth:`create_upload_path`.
|
||||
'''
|
||||
return default
|
||||
|
||||
|
@ -33,6 +33,10 @@ def debug_print(*args):
|
||||
# CLI must come before Device as it implements the CLI functions that
|
||||
# are inherited from the device interface in 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.')
|
||||
author = _('John Schember')
|
||||
@ -195,10 +199,13 @@ class USBMS(CLI, Device):
|
||||
|
||||
def upload_cover(self, path, filename, metadata):
|
||||
'''
|
||||
:path: the full path were the associated book is located.
|
||||
:filename: the name of the book file without the extension.
|
||||
:metadata: metadata belonging to the book. Use metadata.thumbnail
|
||||
for cover
|
||||
Upload book cover to the device. Default implementation does nothing.
|
||||
|
||||
:param path: the full path were the associated book is located.
|
||||
:param filename: the name of the book file without the extension.
|
||||
:param metadata: metadata belonging to the book. Use metadata.thumbnail
|
||||
for cover
|
||||
|
||||
'''
|
||||
pass
|
||||
|
||||
|
@ -15,6 +15,22 @@ from calibre.ebooks.metadata.library_thing import check_for_cover
|
||||
metadata_config = None
|
||||
|
||||
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'
|
||||
|
||||
|
@ -119,10 +119,11 @@ class RTFMLizer(object):
|
||||
output += '{\\page } '
|
||||
for item in self.oeb_book.spine:
|
||||
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.find(XHTML('body')), encoding=unicode))
|
||||
content = unicode(etree.tostring(item.data, encoding=unicode))
|
||||
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.insert_images(output)
|
||||
output = self.clean_text(output)
|
||||
|
@ -512,9 +512,18 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
||||
idx = i
|
||||
self.opt_toolbar_text.addItem(x[0], x[1])
|
||||
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))
|
||||
|
||||
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):
|
||||
port = self.port.value()
|
||||
if port < 1025:
|
||||
|
@ -89,8 +89,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>724</width>
|
||||
<height>683</height>
|
||||
<width>720</width>
|
||||
<height>679</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
@ -222,6 +222,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="reset_confirmation_button">
|
||||
<property name="text">
|
||||
<string>Reset all disabled &confirmation dialogs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
|
@ -5,7 +5,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
from calibre.gui2 import dynamic
|
||||
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):
|
||||
return name + '_again'
|
||||
@ -18,15 +18,17 @@ class Dialog(QDialog, Ui_Dialog):
|
||||
|
||||
self.msg.setText(msg)
|
||||
self.name = name
|
||||
self.connect(self.again, SIGNAL('stateChanged(int)'), self.toggle)
|
||||
self.again.stateChanged.connect(self.toggle)
|
||||
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
||||
|
||||
|
||||
def toggle(self, x):
|
||||
def toggle(self, *args):
|
||||
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):
|
||||
return True
|
||||
d = Dialog(msg, name, parent)
|
||||
d.label.setPixmap(QPixmap(I(pixmap)))
|
||||
d.setWindowIcon(QIcon(I(pixmap)))
|
||||
return d.exec_() == d.Accepted
|
||||
|
@ -26,7 +26,10 @@ class ThrobbingButton(QToolButton):
|
||||
def set_normal_icon_size(self, w, h):
|
||||
self.normal_icon_size = QSize(w, h)
|
||||
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):
|
||||
self.setIconSize(self.normal_icon_size)
|
||||
|
@ -3,12 +3,14 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
''' Post installation script for linux '''
|
||||
|
||||
import sys, os, shutil, cPickle, textwrap, stat
|
||||
import sys, os, cPickle, textwrap, stat
|
||||
from subprocess import check_call
|
||||
|
||||
from calibre import __appname__, prints, guess_type
|
||||
from calibre.constants import islinux, isfreebsd
|
||||
from calibre.customize.ui import all_input_formats
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre import CurrentDir
|
||||
|
||||
|
||||
entry_points = {
|
||||
@ -39,6 +41,7 @@ entry_points = {
|
||||
],
|
||||
}
|
||||
|
||||
# Uninstall script {{{
|
||||
UNINSTALL = '''\
|
||||
#!{python}
|
||||
euid = {euid}
|
||||
@ -79,6 +82,8 @@ for f in mr:
|
||||
os.remove(os.path.abspath(__file__))
|
||||
'''
|
||||
|
||||
# }}}
|
||||
|
||||
class PostInstall:
|
||||
|
||||
def task_failed(self, msg):
|
||||
@ -171,7 +176,7 @@ class PostInstall:
|
||||
self.task_failed('Creating uninstaller failed')
|
||||
|
||||
|
||||
def setup_completion(self):
|
||||
def setup_completion(self): # {{{
|
||||
try:
|
||||
self.info('Setting up bash completion...')
|
||||
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:
|
||||
raise
|
||||
self.task_failed('Setting up completion failed')
|
||||
# }}}
|
||||
|
||||
def install_man_pages(self):
|
||||
def install_man_pages(self): # {{{
|
||||
try:
|
||||
from calibre.utils.help2man import create_man_page
|
||||
if isfreebsd:
|
||||
@ -318,73 +324,69 @@ class PostInstall:
|
||||
if self.opts.fatal_errors:
|
||||
raise
|
||||
self.task_failed('Installing MAN pages failed')
|
||||
# }}}
|
||||
|
||||
def setup_desktop_integration(self):
|
||||
def setup_desktop_integration(self): # {{{
|
||||
try:
|
||||
from PyQt4.QtCore import QFile
|
||||
from tempfile import mkdtemp
|
||||
|
||||
self.info('Setting up desktop integration...')
|
||||
|
||||
|
||||
tdir = mkdtemp()
|
||||
cwd = os.getcwdu()
|
||||
try:
|
||||
os.chdir(tdir)
|
||||
render_svg(QFile(I('mimetypes/lrf.svg')), os.path.join(tdir, 'calibre-lrf.png'))
|
||||
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png application-lrf', shell=True)
|
||||
self.icon_resources.append(('mimetypes', 'application-lrf', '128'))
|
||||
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png text-lrs', shell=True)
|
||||
self.icon_resources.append(('mimetypes', 'application-lrs',
|
||||
'128'))
|
||||
QFile(I('library.png')).copy(os.path.join(tdir, 'calibre-gui.png'))
|
||||
check_call('xdg-icon-resource install --noupdate --size 128 calibre-gui.png calibre-gui', shell=True)
|
||||
self.icon_resources.append(('apps', 'calibre-gui', '128'))
|
||||
render_svg(QFile(I('viewer.svg')), os.path.join(tdir, 'calibre-viewer.png'))
|
||||
check_call('xdg-icon-resource install --size 128 calibre-viewer.png calibre-viewer', shell=True)
|
||||
self.icon_resources.append(('apps', 'calibre-viewer', '128'))
|
||||
with TemporaryDirectory() as tdir:
|
||||
with CurrentDir(tdir):
|
||||
render_img('mimetypes/lrf.svg', 'calibre-lrf.png')
|
||||
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png application-lrf', shell=True)
|
||||
self.icon_resources.append(('mimetypes', 'application-lrf', '128'))
|
||||
check_call('xdg-icon-resource install --noupdate --context mimetypes --size 128 calibre-lrf.png text-lrs', shell=True)
|
||||
self.icon_resources.append(('mimetypes', 'application-lrs',
|
||||
'128'))
|
||||
render_img('lt.png', 'calibre-gui.png')
|
||||
check_call('xdg-icon-resource install --noupdate --size 128 calibre-gui.png calibre-gui', shell=True)
|
||||
self.icon_resources.append(('apps', 'calibre-gui', '128'))
|
||||
render_img('viewer.svg', 'calibre-viewer.png')
|
||||
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([])
|
||||
for x in all_input_formats():
|
||||
mt = guess_type('dummy.'+x)[0]
|
||||
if mt and 'chemical' not in mt:
|
||||
mimetypes.add(mt)
|
||||
mimetypes = set([])
|
||||
for x in all_input_formats():
|
||||
mt = guess_type('dummy.'+x)[0]
|
||||
if mt and 'chemical' not in mt:
|
||||
mimetypes.add(mt)
|
||||
|
||||
def write_mimetypes(f):
|
||||
f.write('MimeType=%s;\n'%';'.join(mimetypes))
|
||||
def write_mimetypes(f):
|
||||
f.write('MimeType=%s;\n'%';'.join(mimetypes))
|
||||
|
||||
f = open('calibre-lrfviewer.desktop', 'wb')
|
||||
f.write(VIEWER)
|
||||
f.close()
|
||||
f = open('calibre-ebook-viewer.desktop', 'wb')
|
||||
f.write(EVIEWER)
|
||||
write_mimetypes(f)
|
||||
f.close()
|
||||
f = open('calibre-gui.desktop', 'wb')
|
||||
f.write(GUI)
|
||||
write_mimetypes(f)
|
||||
f.close()
|
||||
des = ('calibre-gui.desktop', 'calibre-lrfviewer.desktop',
|
||||
'calibre-ebook-viewer.desktop')
|
||||
for x in des:
|
||||
cmd = ['xdg-desktop-menu', 'install', './'+x]
|
||||
if x != des[-1]:
|
||||
cmd.insert(2, '--noupdate')
|
||||
check_call(' '.join(cmd), shell=True)
|
||||
self.menu_resources.append(x)
|
||||
f = open('calibre-mimetypes', 'wb')
|
||||
f.write(MIME)
|
||||
f.close()
|
||||
self.mime_resources.append('calibre-mimetypes')
|
||||
check_call('xdg-mime install ./calibre-mimetypes', shell=True)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
shutil.rmtree(tdir)
|
||||
f = open('calibre-lrfviewer.desktop', 'wb')
|
||||
f.write(VIEWER)
|
||||
f.close()
|
||||
f = open('calibre-ebook-viewer.desktop', 'wb')
|
||||
f.write(EVIEWER)
|
||||
write_mimetypes(f)
|
||||
f.close()
|
||||
f = open('calibre-gui.desktop', 'wb')
|
||||
f.write(GUI)
|
||||
write_mimetypes(f)
|
||||
f.close()
|
||||
des = ('calibre-gui.desktop', 'calibre-lrfviewer.desktop',
|
||||
'calibre-ebook-viewer.desktop')
|
||||
for x in des:
|
||||
cmd = ['xdg-desktop-menu', 'install', './'+x]
|
||||
if x != des[-1]:
|
||||
cmd.insert(2, '--noupdate')
|
||||
check_call(' '.join(cmd), shell=True)
|
||||
self.menu_resources.append(x)
|
||||
f = open('calibre-mimetypes', 'wb')
|
||||
f.write(MIME)
|
||||
f.close()
|
||||
self.mime_resources.append('calibre-mimetypes')
|
||||
check_call('xdg-mime install ./calibre-mimetypes', shell=True)
|
||||
except Exception:
|
||||
if self.opts.fatal_errors:
|
||||
raise
|
||||
self.task_failed('Setting up desktop integration failed')
|
||||
|
||||
# }}}
|
||||
|
||||
def option_parser():
|
||||
from calibre.utils.config import OptionParser
|
||||
parser = OptionParser()
|
||||
@ -542,21 +544,10 @@ MIME = '''\
|
||||
</mime-info>
|
||||
'''
|
||||
|
||||
def render_svg(image, dest, width=128, height=128):
|
||||
from PyQt4.QtGui import QPainter, QImage
|
||||
from PyQt4.QtSvg import QSvgRenderer
|
||||
image = image.readAll() if hasattr(image, 'readAll') else image
|
||||
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 render_img(image, dest, width=128, height=128):
|
||||
from PyQt4.Qt import QImage, Qt
|
||||
img = QImage(I(image)).scaled(width, height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
||||
img.save(dest)
|
||||
|
||||
def main():
|
||||
p = option_parser()
|
||||
|
@ -25,7 +25,7 @@ clean:
|
||||
|
||||
html:
|
||||
mkdir -p .build/html .build/doctrees
|
||||
$(SPHINXBUILD) -b custom $(ALLSPHINXOPTS) .build/html
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in .build/html."
|
||||
|
||||
@ -37,7 +37,7 @@ qthelp:
|
||||
|
||||
epub:
|
||||
mkdir -p .build/qthelp .build/doctrees
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) .build/epub
|
||||
$(SPHINXBUILD) -b myepub $(ALLSPHINXOPTS) .build/epub
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
|
||||
|
@ -23,9 +23,11 @@ custom
|
||||
# General configuration
|
||||
# ---------------------
|
||||
|
||||
needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# 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.
|
||||
templates_path = ['templates']
|
||||
@ -36,6 +38,9 @@ source_suffix = '.rst'
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# The language
|
||||
language = 'en'
|
||||
|
||||
# General substitutions.
|
||||
project = __appname__
|
||||
copyright = '2008, Kovid Goyal'
|
||||
@ -81,7 +86,6 @@ pygments_style = 'sphinx'
|
||||
# given in html_static_path.
|
||||
html_theme = 'default'
|
||||
html_theme_options = {'stickysidebar':'true', 'relbarbgcolor':'black'}
|
||||
html_style = 'calibre.css'
|
||||
|
||||
# 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,
|
||||
@ -100,8 +104,16 @@ html_use_smartypants = True
|
||||
html_title = 'calibre User Manual'
|
||||
html_short_title = 'Start'
|
||||
html_logo = 'resources/logo.png'
|
||||
|
||||
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.
|
||||
#html_sidebars = {}
|
||||
|
@ -3,29 +3,17 @@
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__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.extensions_location = '../plugins'
|
||||
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.ext.autodoc import prepare_docstring
|
||||
from docutils.statemachine import ViewList
|
||||
from docutils import nodes
|
||||
|
||||
sys.path.append(os.path.abspath('../../../'))
|
||||
from calibre.linux import entry_points
|
||||
|
||||
class CustomBuilder(StandaloneHTMLBuilder):
|
||||
name = 'custom'
|
||||
|
||||
class CustomQtBuild(QtHelpBuilder):
|
||||
name = 'customqt'
|
||||
from epub import EPUBHelpBuilder
|
||||
|
||||
def substitute(app, doctree):
|
||||
pass
|
||||
@ -252,64 +240,9 @@ def cli_docs(app):
|
||||
raw += '\n'+'\n'.join(lines)
|
||||
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):
|
||||
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_directive('automember', auto_member, 1, (1, 0, 1))
|
||||
app.connect('doctree-read', substitute)
|
||||
app.connect('builder-inited', cli_docs)
|
||||
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|.
|
||||
|
||||
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
|
||||
: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>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, mimetypes, uuid, shutil
|
||||
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
|
||||
import os, time
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.builders.epub import EpubBuilder
|
||||
|
||||
NCX = '''\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
'''
|
||||
class EPUBHelpBuilder(EpubBuilder):
|
||||
name = 'myepub'
|
||||
|
||||
OPF = '''\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package version="2.0"
|
||||
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>
|
||||
'''
|
||||
def add_cover(self, outdir, cover_fname):
|
||||
href = '_static/'+cover_fname
|
||||
opf = os.path.join(self.outdir, 'content.opf')
|
||||
|
||||
CONTAINER='''\
|
||||
<?xml version="1.0"?>
|
||||
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||
<rootfiles>
|
||||
<rootfile full-path="{0}" media-type="application/oebps-package+xml"/>
|
||||
</rootfiles>
|
||||
</container>
|
||||
'''
|
||||
|
||||
SVG_TEMPLATE = '''\
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="calibre:cover" content="true" />
|
||||
<title>Cover</title>
|
||||
<style type="text/css" title="override_css">
|
||||
@page {padding: 0pt; margin:0pt}
|
||||
body { text-align: center; padding:0pt; margin: 0pt; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
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
|
||||
cover = '''\
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="calibre:cover" content="true" />
|
||||
<title>Cover</title>
|
||||
<style type="text/css" title="override_css">
|
||||
@page {padding: 0pt; margin:0pt}
|
||||
body { text-align: center; padding:0pt; margin: 0pt; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="100%%" height="100%%" viewBox="0 0 600 800"
|
||||
preserveAspectRatio="none">
|
||||
<image width="600" height="800" xlink:href="%s"/>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
'''%href
|
||||
self.files.append('epub_titlepage.html')
|
||||
open(os.path.join(outdir, self.files[-1]), 'wb').write(cover)
|
||||
|
||||
|
||||
class EPUBHelpBuilder(StandaloneHTMLBuilder):
|
||||
"""
|
||||
Builder that also outputs Qt help project, contents and index files.
|
||||
"""
|
||||
name = 'epub'
|
||||
|
||||
# don't copy the reST source
|
||||
copysource = False
|
||||
|
||||
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'])
|
||||
|
||||
raw = open(opf, 'rb').read()
|
||||
raw = raw.replace('</metadata>',
|
||||
('<meta name="cover" content="%s"/>\n'
|
||||
'<dc:date>%s</dc:date>\n</metadata>') %
|
||||
(href.replace('/', '_'), time.strftime('%Y-%m-%d')))
|
||||
raw = raw.replace('</manifest>',
|
||||
('<item id="{0}" href="{0}" media-type="application/xhtml+xml"/>\n</manifest>').\
|
||||
format('epub_titlepage.html'))
|
||||
open(opf, 'wb').write(raw)
|
||||
|
||||
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
|
||||
: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
|
||||
subclasses are:
|
||||
The API for writing recipes is defined by the :class:`BasicNewsRecipe`
|
||||
|
||||
.. contents::
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
BasicNewsRecipe
|
||||
-----------------
|
||||
|
||||
.. class:: BasicNewsRecipe
|
||||
|
||||
Abstract base class that contains a number of members and methods to customize the fetching of contents in your recipes. All
|
||||
recipes must inherit from this class or a subclass of it.
|
||||
|
||||
The members and methods are organized as follows:
|
||||
|
||||
.. contents::
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
|
||||
|
||||
Customizing e-book download
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automember:: BasicNewsRecipe.title
|
||||
|
||||
.. automember:: BasicNewsRecipe.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
|
||||
.. autoclass:: BasicNewsRecipe
|
||||
:members:
|
||||
:member-order: groupwise
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
API Documentation for plugins
|
||||
===============================
|
||||
|
||||
.. module:: calibre.customize.__init__
|
||||
.. module:: calibre.customize
|
||||
: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
|
||||
@ -20,113 +20,136 @@ classes are:
|
||||
Plugin
|
||||
-----------------
|
||||
|
||||
.. class:: Plugin
|
||||
|
||||
Abstract base class that contains a number of members and methods to create your plugin. All
|
||||
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
|
||||
.. autoclass:: Plugin
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. _pluginsFTPlugin:
|
||||
|
||||
FileTypePlugin
|
||||
-----------------
|
||||
|
||||
.. class:: Plugin
|
||||
|
||||
Abstract base class that contains a number of members and methods to create your file type plugin. All file type
|
||||
plugins must inherit from this class or a subclass of it.
|
||||
|
||||
The members and methods are:
|
||||
|
||||
.. automember:: FileTypePlugin.file_types
|
||||
|
||||
.. automember:: FileTypePlugin.on_import
|
||||
|
||||
.. automember:: FileTypePlugin.on_preprocess
|
||||
|
||||
.. automember:: FileTypePlugin.on_postprocess
|
||||
|
||||
.. automethod:: FileTypePlugin.run
|
||||
.. autoclass:: FileTypePlugin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. _pluginsMetadataPlugin:
|
||||
|
||||
Metadata plugins
|
||||
-------------------
|
||||
|
||||
.. class:: MetadataReaderPlugin
|
||||
|
||||
Abstract base class that contains a number of members and methods to create your metadata reader plugin. All metadata
|
||||
reader plugins must inherit from this class or a subclass of it.
|
||||
|
||||
The members and methods are:
|
||||
|
||||
.. automember:: MetadataReaderPlugin.file_types
|
||||
|
||||
.. automethod:: MetadataReaderPlugin.get_metadata
|
||||
.. autoclass:: MetadataReaderPlugin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
|
||||
.. class:: MetadataWriterPlugin
|
||||
|
||||
Abstract base class that contains a number of members and methods to create your metadata writer plugin. All metadata
|
||||
writer plugins must inherit from this class or a subclass of it.
|
||||
|
||||
The members and methods are:
|
||||
|
||||
.. automember:: MetadataWriterPlugin.file_types
|
||||
|
||||
.. automethod:: MetadataWriterPlugin.set_metadata
|
||||
|
||||
.. autoclass:: MetadataWriterPlugin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. _pluginsMetadataSource:
|
||||
|
||||
Catalog plugins
|
||||
----------------
|
||||
|
||||
.. autoclass:: CatalogPlugin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
|
||||
Metadata download plugins
|
||||
--------------------------
|
||||
|
||||
.. class:: calibre.ebooks.metadata.fetch.MetadataSource
|
||||
.. module:: calibre.ebooks.metadata.fetch
|
||||
|
||||
Represents a source to query for metadata. Subclasses must implement
|
||||
at least the fetch method.
|
||||
.. autoclass:: MetadataSource
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
When :meth:`fetch` is called, the `self` object will have the following
|
||||
useful attributes (each of which may be None)::
|
||||
Conversion plugins
|
||||
--------------------
|
||||
|
||||
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
|
||||
future use.
|
||||
.. autoclass:: InputFormatPlugin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
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).
|
||||
.. autoclass:: OutputFormatPlugin
|
||||
:show-inheritance:
|
||||
: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):
|
||||
options = {'cPickle':cPickle}
|
||||
if not isinstance(src, unicode):
|
||||
src = src.decode('utf-8')
|
||||
if src is not None:
|
||||
try:
|
||||
if not isinstance(src, unicode):
|
||||
src = src.decode('utf-8')
|
||||
exec src in options
|
||||
except:
|
||||
print 'Failed to parse options string:'
|
||||
|
@ -115,7 +115,7 @@ class Feed(object):
|
||||
max_articles_per_feed=100):
|
||||
entries = feed.entries
|
||||
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', '')
|
||||
image = feed.get('image', {})
|
||||
self.image_url = image.get('href', None)
|
||||
|
@ -37,7 +37,10 @@ class DownloadDenied(ValueError):
|
||||
|
||||
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
|
||||
@ -127,7 +130,7 @@ class BasicNewsRecipe(Recipe):
|
||||
#: embedded content.
|
||||
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.
|
||||
articles_are_obfuscated = False
|
||||
|
||||
@ -147,7 +150,7 @@ class BasicNewsRecipe(Recipe):
|
||||
#: If True empty feeds are removed from the output.
|
||||
#: This option has no effect if parse_index is overriden in
|
||||
#: 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
|
||||
|
||||
#: 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.
|
||||
|
||||
This method is typically useful for sites that try to make it difficult to
|
||||
access article content automatically. See for example the
|
||||
:module:`calibre.web.recipes.iht` recipe.
|
||||
access article content automatically.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@ -700,8 +702,7 @@ class BasicNewsRecipe(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.
|
||||
Calling it more than once will lead to undefined behavior.
|
||||
@return: Path to index.html
|
||||
@rtype: string
|
||||
:return: Path to index.html
|
||||
'''
|
||||
try:
|
||||
res = self.build_index()
|
||||
@ -1359,7 +1360,7 @@ class BasicNewsRecipe(Recipe):
|
||||
'''
|
||||
If your recipe when converted to EPUB has problems with images when
|
||||
viewed in Adobe Digital Editions, call this method from within
|
||||
:method:`postprocess_html`.
|
||||
:meth:`postprocess_html`.
|
||||
'''
|
||||
for item in soup.findAll('img'):
|
||||
for attrib in ['height','width','border','align','style']:
|
||||
|
@ -103,6 +103,32 @@ class IndexTemplate(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):
|
||||
feed = feeds[f]
|
||||
head = HEAD(TITLE(feed.title))
|
||||
@ -111,6 +137,8 @@ class FeedTemplate(Template):
|
||||
if extra_css:
|
||||
head.append(STYLE(extra_css, type='text/css'))
|
||||
body = BODY(style='page-break-before:always')
|
||||
body.append(self.get_navbar(f, feeds))
|
||||
|
||||
div = DIV(
|
||||
H2(feed.title,
|
||||
CLASS('calibre_feed_title', 'calibre_rescale_160')),
|
||||
@ -144,12 +172,7 @@ class FeedTemplate(Template):
|
||||
CLASS('article_description', 'calibre_rescale_70')))
|
||||
ul.append(li)
|
||||
div.append(ul)
|
||||
navbar = DIV('| ', CLASS('calibre_navbar', 'calibre_rescale_70'))
|
||||
link = A('Up one level', href="../index.html")
|
||||
link.tail = ' |'
|
||||
navbar.append(link)
|
||||
div.append(navbar)
|
||||
|
||||
div.append(self.get_navbar(f, feeds, top=False))
|
||||
self.root = HTML(head, body)
|
||||
|
||||
class NavBarTemplate(Template):
|
||||
|
Loading…
x
Reference in New Issue
Block a user