mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Rationalize CLI for ebook metadata. Now contained in the single command ebook-metadata. Also rename the prs500 to ebook-device
This commit is contained in:
commit
83eefa45b8
@ -4,7 +4,7 @@
|
||||
<pydev_project>
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property>
|
||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||
<path>/calibre/src</path>
|
||||
<path>/calibre-pluginize/src</path>
|
||||
</pydev_pathproperty>
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
||||
</pydev_project>
|
||||
|
@ -3,7 +3,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
''' Create an OSX installer '''
|
||||
|
||||
import sys, re, os, shutil, subprocess, stat, glob, zipfile
|
||||
import sys, re, os, shutil, subprocess, stat, glob, zipfile, plistlib
|
||||
l = {}
|
||||
exec open('setup.py').read() in l
|
||||
VERSION = l['VERSION']
|
||||
@ -36,7 +36,7 @@ loader = open(loader_path, 'w')
|
||||
site_packages = glob.glob(resources_dir+'/lib/python*/site-packages.zip')[0]
|
||||
print >>loader, '#!'+python
|
||||
print >>loader, 'import sys'
|
||||
print >>loader, 'sys.path.remove('+repr(dirpath)+')'
|
||||
print >>loader, 'if', repr(dirpath), 'in sys.path: sys.path.remove(', repr(dirpath), ')'
|
||||
print >>loader, 'sys.path.append(', repr(site_packages), ')'
|
||||
print >>loader, 'sys.frozen = "macosx_app"'
|
||||
print >>loader, 'sys.frameworks_dir =', repr(frameworks_dir)
|
||||
@ -294,10 +294,25 @@ sys.frameworks_dir = os.path.join(os.path.dirname(os.environ['RESOURCEPATH']), '
|
||||
f.close()
|
||||
print
|
||||
print 'Adding main scripts to site-packages'
|
||||
f = zipfile.ZipFile(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'lib', 'python2.6', 'site-packages.zip'), 'a', zipfile.ZIP_DEFLATED)
|
||||
f = zipfile.ZipFile(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'lib', 'python'+sys.version[:3], 'site-packages.zip'), 'a', zipfile.ZIP_DEFLATED)
|
||||
for script in scripts['gui']+scripts['console']:
|
||||
f.write(script, script.partition('/')[-1])
|
||||
f.close()
|
||||
print
|
||||
print 'Creating console.app'
|
||||
contents_dir = os.path.dirname(resource_dir)
|
||||
cc_dir = os.path.join(contents_dir, 'console.app', 'Contents')
|
||||
os.makedirs(cc_dir)
|
||||
for x in os.listdir(contents_dir):
|
||||
if x == 'console.app':
|
||||
continue
|
||||
if x == 'Info.plist':
|
||||
plist = plistlib.readPlist(os.path.join(contents_dir, x))
|
||||
plist['LSUIElement'] = '1'
|
||||
plistlib.writePlist(plist, os.path.join(cc_dir, x))
|
||||
else:
|
||||
os.symlink(os.path.join('../..', x),
|
||||
os.path.join(cc_dir, x))
|
||||
print
|
||||
print 'Building disk image'
|
||||
BuildAPP.makedmg(os.path.join(self.dist_dir, APPNAME+'.app'), APPNAME+'-'+VERSION)
|
||||
|
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
||||
import sys, time, subprocess, os, re
|
||||
from calibre import __appname__, __version__
|
||||
|
||||
INSTALLJAMMER = '/home/kovid/installjammer/installjammer'
|
||||
INSTALLJAMMER = '/usr/local/installjammer/installjammer'
|
||||
|
||||
sv = re.sub(r'[a-z]\d+', '', __version__)
|
||||
|
||||
@ -26,6 +26,7 @@ cmdline = [
|
||||
'-DLicense', open(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'LICENSE')).read().replace('\n', '\r\n'),
|
||||
'--output-dir', os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'dist'),
|
||||
'--platform', 'Windows',
|
||||
'--verbose'
|
||||
]
|
||||
|
||||
def run_install_jammer(installer_name='<%AppName%>-<%Version%><%Ext%>', build_for_release=True):
|
||||
@ -43,7 +44,7 @@ def run_install_jammer(installer_name='<%AppName%>-<%Version%><%Ext%>', build_fo
|
||||
subprocess.check_call(cmdline)
|
||||
|
||||
def main(args=sys.argv):
|
||||
run_install_jammer(build_for_release=False)
|
||||
run_install_jammer(build_for_release=True)
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -138,7 +138,7 @@ ProjectID
|
||||
DA98A0C6-9102-73EC-2516-B147E972D3F7
|
||||
|
||||
ProjectVersion
|
||||
1.2.12.0
|
||||
1.2.7.0
|
||||
|
||||
SaveOnlyToplevelDirs
|
||||
No
|
||||
@ -208,12 +208,352 @@ test
|
||||
}
|
||||
FileGroup ::BEF8D398-58BA-1F66-39D6-D4A63D5BEEF9 -setup Install -active Yes -platforms {AIX-ppc FreeBSD-4-x86 FreeBSD-x86 HPUX-hppa Linux-x86 Solaris-sparc Windows TarArchive ZipArchive} -name {Program Files} -parent FileGroups
|
||||
File ::8E5D85A4-7608-47A1-CF7C-309060D5FF40 -filemethod {Always overwrite files} -type dir -directory <%InstallDir%> -name /home/kovid/work/calibre/build/py2exe -parent BEF8D398-58BA-1F66-39D6-D4A63D5BEEF9
|
||||
File ::FC870EE7-667B-481F-113B-B4504DFCCFA5 -type dir -name bin -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::377C588B-B324-CA09-ED49-4DB5F82A15ED -type dir -name etc -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B95D03D4-EA59-F00E-59E1-BA05758879DA -type dir -name imageformats -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5EF561A9-E70B-8F01-A852-C36D28D1FA14 -type dir -name codecs -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::48EA1D8C-F4C8-3D34-229D-B501057802F3 -type dir -name driver -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0AC00D67-8452-CABB-6843-FE6A464E9AE9 -type dir -name plugins -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::01034EB7-C79C-42B9-6FF0-E06C72EF2623 -type dir -name iconengines -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7FC7557D-E1AF-082B-7286-24939CD5EE76 -name IM_MOD_RL_sfw_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EF383C5E-5939-F73E-5F9C-26009B469ADA -name feeds2mobi.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::71A18B69-E38B-AB9F-3B81-BECFD4454D38 -name PyQt4.QtNetwork.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E1BF1A4F-2942-73DB-1770-1ECB02CA9799 -name comic2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EEC00227-DED5-B9BF-A36D-24239AEDE932 -name rtf-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F6C99732-1835-1C2A-02F9-F2BB631BEC81 -name dde.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::3334C5D8-8513-326D-A96C-C86D1C325EBB -name pdf2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::20079501-EBD5-FEFD-8E49-2CF6A63E83E0 -name IM_MOD_RL_rle_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D98A0557-D988-24A1-9F32-67F3E9A5A123 -name opf-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::470243F6-C8E4-DB45-678F-6A314747097E -name sqlite3.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4AA221F6-EBE3-997E-AECF-DD8E03265385 -name html2epub.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0213D597-D9C7-F155-EE44-1BD9E19520E3 -name IM_MOD_RL_tiff_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F782E3D0-C2CA-481C-70C4-2257FEF52EAF -name CORE_RL_libxml_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::414CB1DC-A13E-CE54-60A9-157381D4DDC4 -name oeb2lit.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9306B7C1-11DE-7B54-1AFB-67AFF82B8013 -name _sqlite3.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4D6B4FC9-F900-E0AD-5258-8918CBFEB9AD -name lrs2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::05329209-3CCE-90C0-FFEA-BECEBA6A6AB4 -name lit2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A2AF7001-960E-F5EA-AA09-025E35FD09BC -name IM_MOD_RL_sun_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C472ABCF-2160-C218-41CA-0DDE625AAEC6 -name IM_MOD_RL_msl_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::DC7D8C3A-81C2-7FC5-4AC0-8CDC4069FC97 -name IM_MOD_RL_jp2_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::555AF3FA-9A08-CCFF-4028-3F8891A6D212 -name CORE_RL_ttf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::92F06FE6-C2C5-FF23-0A9B-884AB21D8221 -name PIL._imagingft.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::DA2ABF54-3BC2-87E4-BA86-24C8B2915AC0 -name txt2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C3B86C9F-1A59-9573-DCA2-A2747E7C3B4F -name odt2oeb.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::ED8542BE-DDE4-72B2-B05A-A09D00818994 -name web2disk.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A2154B84-F146-FD50-1DA9-1DB374A5439E -name IM_MOD_RL_exr_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A442B5C3-724F-6542-816D-30C3D3EDD1E3 -name CORE_RL_Magick++_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A18318B3-88EF-11EB-4E14-B02CBD4F0E5E -name calibre-parallel.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E26C9A86-F12B-7F1E-F2E8-8A42C0436321 -name IM_MOD_RL_dib_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2E94EBE2-F588-C484-009C-48C0DA7D1932 -name IM_MOD_RL_preview_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9D75046B-57DC-872C-39F1-4C6E44337227 -name html2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7294280B-8324-B1A0-A054-92F330874F48 -name numpy.fft.fftpack_lite.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5322B2EC-82CF-9446-964E-38CB7C688C1B -name oeb2mobi.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::74DC3F8D-5C56-8FB8-F59A-0C4C5ECB70A0 -name CORE_RL_magick_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::1638722F-A861-F762-1514-5649B2AD08FF -name IM_MOD_RL_svg_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D345F98D-EDB4-53D0-95A6-82968E47E080 -name IM_MOD_RL_mpeg_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::91CA5392-5F4E-12F7-C670-939B1500616B -name ebook-viewer.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4706580D-D101-13EA-DED0-2593D7FBADE8 -name LICENSE -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::63F996B0-07D9-EA5B-2176-F02D9851EAE4 -name feeds2disk.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::89CAC9B2-DA65-DBF2-6295-757553882213 -name numpy.core.umath.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2F90282D-B59F-B6BA-090B-45858AF7F3B2 -name IM_MOD_RL_clipboard_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B512D139-B295-D7C3-F0B4-43775849CF58 -name numpy.core._sort.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A999B024-1024-E709-07C1-22BA67A850BB -name feeds2mobi.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A2C063AC-2F12-9260-501A-0E8BD0B8A932 -name calibre.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::439B6D05-7DE6-061D-4BCC-3F04F4FA2FA2 -name IM_MOD_RL_png_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BA464D11-BBCE-DEDA-C354-0C7BE60FAA05 -name IM_MOD_RL_braille_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::8F9FF823-AF6D-A288-8AE6-7D74F55DCE29 -name CORE_RL_bzlib_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::ABAEA510-B003-29CB-6ADE-F6E1826DD501 -name web2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::57A3F5D5-BFC8-CB38-5A57-548EE0DB033B -name QtNetwork4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4DB7E8DE-905A-822A-AF14-17BD5ACEF915 -name IM_MOD_RL_wmf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::559057ED-0FEA-62A1-68C2-023116B5636C -name win32ui.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::83F3AC3F-A485-B791-D4F0-6E67135FF19D -name IM_MOD_RL_txt_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::FABA7768-743B-08F7-B871-ED5E9082DF38 -name win32gui.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A8D10C63-CDFB-0809-CA84-4CB7A0A451FC -name fb2-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C49805D2-C0B8-01C4-DF6F-674D9C0BFD15 -name IM_MOD_RL_viff_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::1B9F2F00-20A5-B207-5A80-8F75470286AD -name txt2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::826F1915-9F97-59DD-6637-3EEC0744A79C -name IM_MOD_RL_ps2_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::519A6618-8A1F-93A5-93B4-6EEF5A4A3DE9 -name comic2pdf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B0CEAA35-52BF-0DE0-BAC7-7B23157E29BD -name isbndb.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A5F23791-BCDC-A997-4941-5D1F2F227E6D -name type.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0A1C107A-C0AA-3ED6-4F37-A6894386DCBE -name IM_MOD_RL_ps3_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EEBA64E7-6509-EBAF-3E23-1A203216F39A -name epub2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2655F4FC-F682-46D8-B75C-6AF322323EF5 -name IM_MOD_RL_dot_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::176456BB-237C-9EBE-60E1-D8F78AAFFEC8 -name IM_MOD_RL_xwd_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9AE8498B-C89C-8B12-B8A1-35E1B6650469 -name lxml.objectify.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EA37C1C2-57BB-4E7A-C004-0010D79142C2 -name IM_MOD_RL_fits_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::05F5C10D-6988-F1F4-A486-86C96DB20302 -name pywintypes26.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0137A2B1-EB94-EB26-7295-0C7CD941A1DF -name IM_MOD_RL_histogram_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7F199A1F-4FA4-2ABA-DED3-36ECF3C089CA -name epub2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F9F112C9-B61B-E041-1A9D-47641B047135 -name isbndb.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::CF6398D8-2140-53CF-1DA6-421A82E92621 -name any2epub.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::8DFA6C69-360D-FA63-7FF9-860E3DB00B19 -name any2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5BB7579D-9183-412C-81F8-B411B07C57B3 -name IM_MOD_RL_pnm_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::97B8BB83-7772-D87B-C8D1-5215E324AF2C -name library.ico -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::DF1361A2-2580-EFBF-65D2-156AD9919DE1 -name library.zip -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A6419A84-6C22-784E-6D84-D09972770770 -name unicodedata.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E658FBE0-5860-D041-12D3-76ADD18F804B -name servicemanager.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C98A6FC4-E341-7FD4-005C-DA2B384E11D8 -name win32api.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::ADA36EEA-7DE1-447C-B1AB-A4908E65E2CD -name IM_MOD_RL_ipl_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::53C2EC15-850F-8F49-6425-C228FB6E6D0E -name libfontconfig-1.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2BCD9281-2CBC-CF0D-0E12-2CE11F6ED758 -name comic2epub.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EDE6F457-C83F-C5FA-9AF4-38FDFF17D929 -name PIL._imagingtk.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::09D0906E-3611-3DB7-32CF-A140585694A7 -name win32pdh.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4C84F0DC-7157-0C90-2062-180139B03E25 -name IM_MOD_RL_rgb_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F402F507-87C5-BDB1-80AE-AD3FF4A4BCE7 -name bzrlib._patiencediff_c.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A732EDE7-4796-241F-BECA-68E59F88F8AF -name lrs2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::69072379-7D16-B9F7-9F39-3E6403C48267 -name IM_MOD_RL_xbm_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::FBD11D98-D1E7-5DD9-BF02-01CE92518859 -name IM_MOD_RL_otb_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::8D0CFD32-2B7F-2BB3-8FA0-760A8DB24B52 -name win32service.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C7F9AB12-AAF7-2954-3DB6-F2C84F41655B -name win32clipboard.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::76676624-68A9-D9D9-6EC7-40CF201520A4 -name pdfreflow.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::62CD997A-A5C7-D71B-A8F7-54567B36A071 -name msvcp90.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7D76BBE6-FD20-1290-4DBA-93D14FC45B81 -name sip.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::17D27485-128E-E247-14CE-9C3B6988E182 -name pdfreflow.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BF2007D6-5AFE-6D04-4DB0-36A3644D988D -name pdf-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D4354621-69ED-DFCB-068A-0812DB1C09C3 -name IM_MOD_RL_avi_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::109FBE61-82CD-D5AB-AA9D-F9D52947DD22 -name rtf-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9BA20E46-014F-DA6A-DEF3-D78E2AEFDD47 -name odt-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EAFAC6D5-A7E8-1843-6D98-6663D899BB11 -name PyQt4.QtCore.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::155505A4-F0D8-05B2-10AD-149E178976A4 -name calibredb.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::39D5114E-1E70-5402-7E19-D86490678506 -name CORE_RL_lcms_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::75C3F298-B07C-DA98-38B2-40FFD39C32ED -name oeb2lit.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::388E0308-35A8-69B9-6837-383FD72E99E9 -name IM_MOD_RL_xpm_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::3DB88954-1B0D-FDBC-CE29-EFCC01D4121E -name epub-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::43C0DE14-A935-2139-6690-256C49C461C4 -name IM_MOD_RL_xcf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E185A35A-93DB-61BB-E7EE-2C2222FD4939 -name win32security.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::498CCC26-383A-87CA-30C0-626D52555B37 -name librarything.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::404A98F1-84FD-B6D0-B130-354EECD9253C -name IM_MOD_RL_emf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::17034C34-403E-B405-99C1-F80B7F00E27C -name log.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::34E63A2C-65C5-0A84-ACF1-BD6A844D4579 -name pythoncom26.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::CE737360-1B73-DEC3-E511-3FAEC61F5292 -name epub-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2F20484B-53B8-B08E-B691-C5B2D49A9CB4 -name QtWebKit4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::8AF134C8-9189-3F9A-A081-9143FFD44C45 -name freetype6.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E8A4442D-D0D3-31CD-997A-3CEB641CF5B7 -name IM_MOD_RL_mtv_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::56B7883F-B4FE-BE25-BCBA-4AF17CC84C93 -name fb22lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0CA87D0B-5A04-1439-AEE8-C97072D47BA7 -name CORE_RL_tiff_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::AC24F520-88D4-D1CF-5797-27C715CE8ACA -name pyexpat.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::16848F38-71CD-55B8-4D96-1537F6773744 -name IM_MOD_RL_dps_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::33A46CC5-BAC4-5863-C83D-303DCCA0CAA1 -name tk85.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::39E11D64-CC0C-E565-B3CC-882A5AA9F4AF -name html2oeb.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::81116DD3-1715-AA87-472F-544FC616EDAF -name IM_MOD_RL_dcm_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A6EF7DB0-FC94-8794-1F15-394432CD283D -name imp-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::335A4CFB-5C2D-44E4-C438-7018E8244C3D -name ebook-viewer.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::62A16C3B-ED9C-5187-2807-58857DF3A990 -name calibre-debug.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2B8B1DCD-AA68-7612-80A5-C20CAAF06019 -name any2lit.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::55ECA7B7-279A-F51D-81C2-C8DC44CF0E22 -name select.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A6AF5ECC-A981-4CBD-DBEE-303A9340C603 -name IM_MOD_RL_xps_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5BCBF71F-18E7-5C52-E3F5-7D7F3028AD46 -name locale.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2EDE0641-B5DF-220F-9FF3-486E5A081EFC -name rb-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5C9FA94C-B8B0-A94B-548D-1D24FDEA5770 -name CORE_RL_wand_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E39E60BE-DE77-AB8C-42C6-5A7D7DC073E3 -name IM_MOD_RL_ttf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::8F1AF028-E819-4ED6-8B69-704183C3BD1D -name pdf-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D8F566FB-93DA-3128-5DBD-DF1068B3E3ED -name IM_MOD_RL_dpx_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::FFE5F178-A1A4-3691-9C88-E5109D144437 -name IM_MOD_RL_jpeg_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0C9C97F6-5622-D4B5-E7CD-B4E5E9A8634C -name odt-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C05AD359-A7A5-9760-A4C8-310074353C89 -name feeds2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::03665A74-B895-EB3D-EBA0-2D2B6D26DDDA -name PIL._imagingmath.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::81AED538-B97F-6272-5C6E-9B27D7285B35 -name IM_MOD_RL_fax_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BDCCAEC0-4847-B8D8-50B1-1B434B73A01F -name IM_MOD_RL_avs_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::727B9BD6-B55E-386E-D3B9-D99D0D9ADBB1 -name QtGui4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::1C7421A2-1BA7-7807-EB41-67578E0302E8 -name IM_MOD_RL_mvg_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C3466B70-23C3-31C9-3B4F-1B3B56E4D013 -name markdown-calibre.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A190F216-F7F7-A425-5F9D-F6B5C35D3A8F -name lrf-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6ED1C675-C4D5-6BFF-7C8A-9AB4BF39D00C -name _hashlib.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::21F7F333-3063-71E3-85F5-5C88584B15CC -name IM_MOD_RL_tga_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::738CADE4-6C0A-5155-A1BE-8F789C1D92E7 -name feeds2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::26741B21-C241-E100-8BB1-8B679BC3E662 -name configure.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7D491E89-C6D3-1E6E-F4BD-8E55260FE33E -name libexpat.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A4910EB3-0F1C-F6F0-CD2D-16A64BBAA92B -name calibre-fontconfig.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::8711327A-716D-B162-6AC6-2FB4AD071266 -name fb22lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0FDD3A7A-31F3-8089-CE32-D80EAA6F62B2 -name bzrlib._btree_serializer_c.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::476CB977-5155-D56F-26CA-EB243AEBBA99 -name unrar.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2DA1CC8D-AF5C-3B03-2060-301DFE0356CC -name mobi2oeb.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2E2A9EDA-5386-444E-8479-557386794552 -name IM_MOD_RL_uil_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4530C4C8-BCFA-E461-5F72-0EF5B553C7F5 -name pdf2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::29582E09-00E0-B2F9-475C-8C6D2E4BF7E8 -name any2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D3301E15-1B1A-E2AB-1B04-30A601B3FB44 -name IM_MOD_RL_cut_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::181B28B9-CF13-0C28-8380-B39DF6E7397D -name markdown-calibre.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BC1D2D23-48ED-ECFF-2180-37C83554FDED -name Xext.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::15289622-D0DC-4F61-4990-AF6CA73C3F8C -name IM_MOD_RL_scr_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::76594353-4D68-08A0-B2AD-B3BA58FABB75 -name IM_MOD_RL_map_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E0F9749A-2B00-4A6C-ABB0-9AD1910A81A6 -name lxml.etree.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5BFB62EB-32B6-EA16-752E-2F29CC9B6202 -name numpy.random.mtrand.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::452196C8-28B2-4BA4-1F00-27C3C0BDDC04 -name thresholds.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::DE476170-B65E-B429-A23C-F822E4190FFC -name IM_MOD_RL_thumbnail_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::31E46936-560D-1E88-8FC1-F8E590D2FD02 -name IM_MOD_RL_mpc_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0C04692E-5DCF-864F-6640-2FF5BF12BA91 -name IM_MOD_RL_cmyk_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A620FEB9-39FE-B102-6963-FEDA25EBF2F1 -name IM_MOD_RL_pcd_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0A4D27B3-157D-357A-6A86-32D8EB5B30D7 -name lit2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::80D1F9CA-D7DA-578E-CE25-4E0EE988D280 -name IM_MOD_RL_sct_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C85307C1-5F11-01EF-2193-95F37CB49822 -name IM_MOD_RL_pict_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::3ECF2FC0-91AA-9573-42B3-4BD784FA5BC9 -name IM_MOD_RL_gradient_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::544BC0E6-66E4-EB06-CCD5-A172F230C83E -name IM_MOD_RL_icon_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::305631EA-9397-0384-259C-643046B72E44 -name X11.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::CBF83E89-39E0-D7C3-7902-A4A0ADCDEF48 -name IM_MOD_RL_jbig_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::FFF32C7F-BB57-4BB3-A52D-4E4F3496B967 -name calibre-debug.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::80A3C65A-553D-EA27-FAED-4F831578F4F8 -name magic.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E2DF0DC3-3372-3CDF-5177-5B3F3BF84E66 -name IM_MOD_RL_mat_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::955B176E-5F6A-FFFA-9387-893E38C23E33 -name prs500.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4D938678-59AF-F5F6-697B-E3A5BE76B43B -name QtXml4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::FCAEFF6A-1E96-FA23-C8AC-0F5F3297B14A -name rtf2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0A5A70A8-39EC-B733-E807-9C358E5EA7A3 -name IM_MOD_RL_meta_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E6419523-253A-3052-B9B4-0EA792EF4A64 -name pdftohtml.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EF062C24-09D8-1DC8-891A-F9563BBA57C2 -name IM_MOD_RL_gray_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E1D4796B-733B-F45A-2ECC-D498A1AC4EAD -name opf-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0E21E0B1-C2D4-3F6D-E788-9EEB821128C3 -name web2disk.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::12DE2886-5926-F8AD-77C4-EAAC5DD9413C -name rtf2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E96D95C6-2D53-A67B-C704-06BD213BBC86 -name mobi-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A4D76085-EFC7-A237-7BB7-AA8A33BFB849 -name html2epub.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::27127583-9DC6-4397-3E86-052A515ED051 -name lrfviewer.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7229472A-2A5A-2685-0223-2DF2CED0C9D4 -name comic2epub.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::396B4F78-FB45-C0B2-ACB3-97769CF5CD5D -name msvcr90.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::1DE767EE-4891-4E54-422D-67A4DFF8C3B5 -name lrfviewer.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::AFFEC28A-615C-E3E6-0026-CCE2594A6D25 -name calibre-server.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2C64F86B-9366-B52D-F7B2-5BBD51F6982A -name IM_MOD_RL_pwp_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::187D965E-40CE-18AB-5684-18C31A7FD8D4 -name any2epub.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F4B2EF9C-EB18-B865-6E99-75CFB9B60D87 -name IM_MOD_RL_dds_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9D0CC46C-E254-7F11-4B64-A6E8E16CF6FB -name mobi2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::72769321-EF4D-C796-5E76-3D5807772233 -name any2mobi.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::38770D87-6CA9-9E3E-FBA1-A8CCFCD88FB5 -name IM_MOD_RL_fpx_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A31CA43B-4456-BB26-A8EE-73C4C4B1F6FC -name pdftrim.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BE2D7BC3-D294-AF3F-65E7-3B372DEFDE36 -name PIL._imaging.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::200B9AEC-809F-75B7-DC12-A51BFC2A6F93 -name PyQt4.QtSvg.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::3A424677-A66A-CAA0-4FD2-4FE8086DBBA6 -name comic2mobi.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::52132A31-D3AE-C617-7568-BF2AF46B5D74 -name IM_MOD_RL_pcl_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F94472C3-C8D0-950F-5ED9-1611D1CE30E5 -name IM_MOD_RL_inline_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::794E99A2-79AB-BABB-97A1-9E3482002EA8 -name imp-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::055ADB4B-20C5-E071-442F-4DA0A8D6F3C5 -name english.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::DE4F9AD0-3D79-865A-2DD9-4A4CB6886AFC -name lit-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B10B6B91-0C03-642D-90D8-37B607B164AD -name IM_MOD_RL_wpg_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BFBB144B-1794-8304-9772-F103A42F2CA4 -name IM_MOD_RL_pdb_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EAED9280-84D7-9768-9F89-CEC61CE549DF -name mobi-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2FE2FF56-5CF5-15B7-2BD8-32100DF421DC -name _ssl.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::AE47BDA8-A1E5-2E84-28D7-DF8A84B18202 -name Microsoft.VC90.CRT.manifest -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A6B1C213-DC68-B7C3-8EDE-806286829899 -name w9xpopen.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D3E03CFA-4AF0-93FB-E04B-B282926099E0 -name IM_MOD_RL_label_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6F40E97B-430C-08FE-30CA-9E6622EE29EB -name feeds2epub.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::345C74B8-B45D-A370-27F3-78CEA9EAB1E1 -name prs500.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::662783C0-3826-9869-8EB8-326B363D3686 -name IM_MOD_RL_clip_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::442D92A7-97B1-74BC-F150-A9992A925356 -name numpy.core.multiarray.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::54991918-D11F-EEAD-F7E3-0EE28F22249E -name IM_MOD_RL_pix_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::069E7552-C005-26F6-2837-CDDDC0159183 -name win32wnet.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::071C5A61-CAC7-1136-DCD5-B2C0847A004A -name colors.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F209A2F0-ED6A-E241-E351-4CA8A539F2BA -name perfmon.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::74DDFF28-013C-4C41-CCE7-0CAD45F7D6B4 -name IM_MOD_RL_cin_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5CB76241-5682-AE44-82D3-A6C8C66B88D6 -name IM_MOD_RL_bmp_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A843E4DA-8671-C4F7-BA61-998AA1D278C8 -name numpy.linalg.lapack_lite.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::908EA762-5568-17BE-D77D-5DE5551895D0 -name IM_MOD_RL_raw_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::CC186E15-8751-A4AA-B2A7-D2E522F1F9DF -name lrf2lrs.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::36135E27-30B6-A0E9-5423-FDDFBCBD4FA2 -name feeds2disk.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A99CACD7-FE57-8A5E-BAA1-2D8B41925593 -name IM_MOD_RL_cip_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::782BE25A-B08E-D914-73F5-D92430823BE1 -name mobi2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C4649D7A-9312-8984-E7CD-A0AB794C5A8E -name IM_MOD_RL_pdf_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B50467F0-07D0-B634-EB43-C4B721798A84 -name analyze.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5531BA1B-8FDA-01B2-7DEE-6E9A116F7AB3 -name lrf-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::47658BFB-7389-6866-359E-7517C18E768D -name IM_MOD_RL_mpr_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::673BD772-ACE7-1695-EAAC-EDD1A3EA0425 -name pdftrim.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::3F6735E8-D4E6-A6F9-C9F8-4219E6BBC827 -name CORE_RL_jpeg_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::918BE500-1E97-90C0-3FB3-4056FE8D0E15 -name IM_MOD_RL_wbmp_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D59F7D54-24A7-774D-79EB-423A64E8CB21 -name IM_MOD_RL_stegano_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BB8BCD59-9EBA-3D9B-D802-79D9C384AADB -name fb2-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::17F9A868-E52D-F961-9DB7-8AC69BA702EA -name CORE_RL_jp2_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2ABAD420-B987-612E-C654-10BDB18DE638 -name html2oeb.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::81715796-F363-675F-998E-EEEF890009A2 -name calibre-fontconfig.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5CA62422-C207-8C9C-79EB-45260E3DB37B -name odt2oeb.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::878003DC-9B1D-7C58-29F8-14D4565ABA64 -name IM_MOD_RL_yuv_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::602B3244-CF6C-F934-E86C-8161800DB150 -name coder.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C23261F0-19EA-2216-A8D6-45FCC4ECBECE -name win32file.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::100103F9-C269-9B03-A133-7D046DCC5B30 -name IM_MOD_RL_null_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D0461A63-6890-CC43-23D1-48E938BF1E6D -name oeb2mobi.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F3996144-7BA7-6038-FBB2-C210721BD20E -name lit-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9BEE3580-3674-F4AE-C1F3-B8623E0E4FC8 -name IM_MOD_RL_pattern_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::AC4AE65E-78A4-28C0-1C1B-B99239D0A0EC -name rb-meta.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C025F962-60EE-E4EF-6428-93755DD24F7D -name web2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6A41C4F0-802D-9999-2F7D-AF86466BC420 -name feeds2epub.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::58F70BF7-491E-38D1-B836-1BBF340AA602 -name IM_MOD_RL_plasma_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4B6C22CD-D139-FCAB-45FB-E9B7DADFFFED -name win32process.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4F391A40-8D34-DFEA-2CEB-97A45FF5D234 -name lit2oeb.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9698339D-6A1E-7CBC-A772-C9C4B10418BC -name librarything.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A3F1DC1C-BEF6-CB6B-F7AC-39F34F4DFABD -name IM_MOD_RL_pcx_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BE44B72D-2ED1-4983-F652-DE8814401946 -name CORE_RL_xlib_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EC5B5C55-8347-0A8C-E9B5-28E45680E2F2 -name IM_MOD_RL_caption_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::519BA66E-0CD3-00EA-0932-8D225C4C7EC8 -name calibredb.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EF7BD102-32B6-EEAB-1C3F-AC4059EFEB1E -name comic2lrf.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B7E13F99-C8D3-F2FB-1E5F-E8530A77BB71 -name IM_MOD_RL_url_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D0762C72-29E8-85CC-8550-6AC9E9AE39E9 -name _win32sysloader.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4921649D-06BD-A568-3879-BD2D94423FCB -name pdftohtml.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2D4976D6-B0ED-C239-5461-70A711A018BE -name QtCore4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::AA761ACD-B728-2324-AA75-B20A2A79F125 -name lrf2lrs.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::95434C76-22F5-B9CE-6194-6E1B1EE3232D -name IM_MOD_RL_info_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::AAF45D03-322F-5553-63A7-312DB754A20B -name _ctypes.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C3D351CA-A8D8-AB35-55D9-5AACF8DB37D1 -name python26.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::2F90B52F-A728-2CA4-5688-0283674695B7 -name _elementtree.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B50B66A1-FB65-FAD5-1DD7-E894ACC07464 -name QtSvg4.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::906FF13D-D993-7192-7EA5-6D15A5A24BFB -name CORE_RL_png_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5D368661-6BF0-D6AF-7C1A-87646864EB4B -name delegates.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6BB7B9CF-131D-3B0A-3A23-21BF168D78A4 -name win32event.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9BA85EE5-1754-67AF-736D-481CDCC72DD2 -name _imagingft.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6254DD0C-8F2C-D4AE-2107-2597D542C181 -name IM_MOD_RL_matte_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F159D566-88D6-C347-3E3C-55C2DDFC5FD0 -name IM_MOD_RL_mono_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B873CAA2-011F-94C3-7977-FF344E53C44F -name CORE_RL_jbig_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7004FCB8-C6F4-C7AF-08E4-B6151B2F7050 -name tcl85.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6921F62A-4015-4C9F-98A6-BCBBC43B698E -name msvcm90.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7276E0CA-C205-4B18-19A3-157F1B8523FB -name IM_MOD_RL_xtrn_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7B9624A9-88B4-C61E-6771-9A34FB6CA3B5 -name PyQt4.QtGui.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::80EC4265-6F3B-7F74-F995-4FA85E87A877 -name IM_MOD_RL_vicar_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4CDC2DC5-5A3D-B773-A338-3E12E8C5BB9D -name IM_MOD_RL_psd_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4AF4C7B2-8926-4581-2896-C6436460C81F -name IM_MOD_RL_uyvy_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BF71D048-1137-4453-960C-9267CB790EAD -name IM_MOD_RL_art_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::1BB40D21-9061-9B38-E653-26D808F7DEEA -name html2lrf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D9B6371C-332C-45FB-DAEE-1A247130F704 -name calibre-customize.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::605E727F-DE13-27FE-75C0-34567C468B18 -name calibre-parallel.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::61F250E4-BAA0-5AF3-F597-E53ADEC19877 -name comic2pdf.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::37306B68-BABB-418C-A2CA-BAF4E439D3F1 -name viewer.ico -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6092C5A2-8740-2769-DB77-E0B664CD19EC -name PyQt4.QtWebKit.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::73C82473-A35A-23B8-067C-AA3120D1CEE2 -name win32pipe.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F269DBE1-334B-2108-31C2-E28664703103 -name _imaging.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::1FF92E56-1C02-1600-8FF5-24B25DD40A51 -name IM_MOD_RL_ycbcr_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::B863CAFC-1205-0F3B-949A-68310B7F3EDB -name type-ghostscript.xml -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::931C4A8D-5B70-AC18-605E-DE89810C778E -name IM_MOD_RL_x_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5BF8A346-2EC6-E5E0-063F-6FB02F84FCAC -name IM_MOD_RL_dng_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::19891B45-E756-8755-9156-FA8CB87AD057 -name IM_MOD_RL_html_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::BC81419A-A3B0-45B4-6672-7EB91B249786 -name IM_MOD_RL_tim_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4E37CF0F-60E7-5CA5-D9FB-C9CD211B8A94 -name IM_MOD_RL_tile_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::D6A57A60-DCB9-BA5C-14BD-5C6143CF34C5 -name numpy.lib._compiled_base.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E2573481-3079-D789-64A6-CC6EA121F7B4 -name IM_MOD_RL_xc_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::F7129083-E586-0227-69C1-63D832794884 -name CORE_RL_zlib_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::29022F2B-4E69-8624-2B2C-98A40E12DC0A -name calibre-server.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::7592A236-4835-6A9D-56B1-78985267D9AD -name win32console.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::05F4A6BD-A7C3-5108-4708-1524EB0628D9 -name calibre.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::DEB8E703-365E-A7BB-2315-6306B2E5C978 -name win32evtlog.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6C90CF50-4894-7672-BB1B-F7EE5EB631E3 -name lit2oeb.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::AB44B44C-E258-A5AB-9511-8D0FBB456EE8 -name IM_MOD_RL_gif_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5D5F2AAD-6668-5E14-B84C-5D80C4CCC968 -name calibre-customize.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::5A0DA3BB-2659-F5AA-A305-C4810FA28D89 -name mobi2oeb.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9A0C05D0-26E3-72C5-1F04-76F76ABB180F -name numpy.core.scalarmath.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::42616D6D-2797-559B-7E96-2685599993DF -name any2lit.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::CB47F917-A45E-B7DE-645C-CA33F005D1BD -name any2mobi.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::16AF75CF-CA5E-F533-6C28-7CF167B1215C -name comic2mobi.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::0888A744-B049-01E8-6AC8-ECC3EE2CEFE6 -name IM_MOD_RL_sgi_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::4AEEBBED-E749-15A9-E1F7-9F6BE2A2C3D0 -name _socket.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::35B19292-0C50-4AEE-0A79-B2F3BC34B529 -name w9xpopen.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::E2C2739C-56B3-E2D0-A1DA-0E3FC3087924 -name IM_MOD_RL_ept_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::C6C5BD2D-887F-5D79-F8B5-C6041C0AEF3C -name PyQt4.Qt.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::8CD7A1E2-9139-8FE7-AAF5-E18B2A6825F2 -name bz2.pyd -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::EBF917FB-15F6-9B3E-5C37-AB0B1D50C64D -name IM_MOD_RL_palm_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::A5500C64-121C-4419-9E3D-F698FE462AE8 -name IM_MOD_RL_ps_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::ADBFA663-E232-7AC1-34A1-700D056E143A -name IM_MOD_RL_miff_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::325F545D-30A8-08DA-74F0-AC1244F6C1D9 -name IM_MOD_RL_vid_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::24238371-77D0-0A8F-35D1-498A5FCC1B0D -name IM_MOD_RL_rla_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::6F5D62F3-5E63-0753-364C-01CAAF1002E0 -name IM_MOD_RL_magick_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
File ::9FDAC308-5D4F-A865-A09A-9FBF48162A47 -name IM_MOD_RL_djvu_.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
|
||||
Component ::F6829AB7-9F66-4CEE-CA0E-21F54C6D3609 -setup Install -active Yes -platforms {AIX-ppc FreeBSD-4-x86 FreeBSD-x86 HPUX-hppa Linux-x86 Solaris-sparc Windows} -name Main -parent Components
|
||||
SetupType ::D9ADE41C-B744-690C-2CED-CF826BF03D2E -setup Install -active Yes -platforms {AIX-ppc FreeBSD-4-x86 FreeBSD-x86 HPUX-hppa Linux-x86 Solaris-sparc Windows} -name Typical -parent SetupTypes
|
||||
|
||||
InstallComponent 3EA07B17-04D8-6508-B535-96CC7173B49A -setup Install -type pane -conditions D7F585DB-0DEC-A94E-DAB0-94D558D82764 -title {Welcome Screen} -component Welcome -command reorder -active Yes -parent StandardInstall
|
||||
InstallComponent 3EA07B17-04D8-6508-B535-96CC7173B49A -setup Install -type pane -conditions D7F585DB-0DEC-A94E-DAB0-94D558D82764 -title {Welcome Screen} -component Welcome -command insert -active Yes -parent StandardInstall
|
||||
Condition D7F585DB-0DEC-A94E-DAB0-94D558D82764 -active Yes -parent 3EA07B17-04D8-6508-B535-96CC7173B49A -title {Execute Script Condition} -component ExecuteScriptCondition -TreeObject::id D7F585DB-0DEC-A94E-DAB0-94D558D82764
|
||||
InstallComponent 7CCDA4BB-861C-C21E-3011-E93DB58F07D6 -setup Install -type action -title {Check for Previous Install} -component CheckForPreviousInstall -active Yes -parent 3EA07B17-04D8-6508-B535-96CC7173B49A
|
||||
InstallComponent 7CCDA4BB-861C-C21E-3011-E93DB58F07D6 -setup Install -type action -conditions ADBCD53E-C9A6-A3CA-1AAC-0DB0CE84F71E -title {Check for Previous Install} -component CheckForPreviousInstall -command reorder -active Yes -parent 3EA07B17-04D8-6508-B535-96CC7173B49A
|
||||
Condition ADBCD53E-C9A6-A3CA-1AAC-0DB0CE84F71E -active Yes -parent 7CCDA4BB-861C-C21E-3011-E93DB58F07D6 -title {Execute Script Condition} -component ExecuteScriptCondition -TreeObject::id ADBCD53E-C9A6-A3CA-1AAC-0DB0CE84F71E
|
||||
InstallComponent 580ACF2C-517F-5E48-9DEF-7DAEFBA59FDD -setup Install -type action -conditions 6DE3B369-9D6B-6BC1-4EA0-2C54ECE159EB -title {Set Virtual Text} -component SetVirtualText -command insert -active Yes -parent 3EA07B17-04D8-6508-B535-96CC7173B49A
|
||||
Condition 6DE3B369-9D6B-6BC1-4EA0-2C54ECE159EB -active Yes -parent 580ACF2C-517F-5E48-9DEF-7DAEFBA59FDD -title {String Is Condition} -component StringIsCondition -TreeObject::id 6DE3B369-9D6B-6BC1-4EA0-2C54ECE159EB
|
||||
InstallComponent 9ACCDC35-E034-F142-ED42-AC8EC2B9AE2D -setup Install -type pane -title {License Agreement} -component License -active Yes -parent StandardInstall
|
||||
@ -528,6 +868,9 @@ false
|
||||
3F2A14F0-06AC-C9D3-1F07-311F41E1338E,Alias
|
||||
{Startup Actions}
|
||||
|
||||
3F2A14F0-06AC-C9D3-1F07-311F41E1338E,Conditions
|
||||
{0 conditions}
|
||||
|
||||
442920D9-8A51-9476-14E4-787D5C230E84,Message,subst
|
||||
1
|
||||
|
||||
@ -868,10 +1211,10 @@ LaunchApplication
|
||||
160
|
||||
|
||||
7CCDA4BB-861C-C21E-3011-E93DB58F07D6,Conditions
|
||||
{0 conditions}
|
||||
{1 condition}
|
||||
|
||||
7CCDA4BB-861C-C21E-3011-E93DB58F07D6,ExecuteAction
|
||||
{Before Pane is Displayed}
|
||||
{Before Next Pane is Displayed}
|
||||
|
||||
825CDD1E-9B3D-E64E-5381-5C5557D204A2,CheckCondition
|
||||
{Before Action is Executed}
|
||||
@ -1098,6 +1441,23 @@ AAF04AF0-329D-75A6-BB68-60ECF2EB74F0,String
|
||||
AAFE58A0-2DFB-CA20-1F6E-D3815F885996,Alias
|
||||
{Cancel Actions}
|
||||
|
||||
ADBCD53E-C9A6-A3CA-1AAC-0DB0CE84F71E,CheckCondition
|
||||
{Before Action is Executed}
|
||||
|
||||
ADBCD53E-C9A6-A3CA-1AAC-0DB0CE84F71E,FailureMessage
|
||||
{Cannot proceed because calibre is running. You can quit it by right clicking the calibre system tray icon.}
|
||||
|
||||
ADBCD53E-C9A6-A3CA-1AAC-0DB0CE84F71E,ResultVirtualText
|
||||
CalibreRunning
|
||||
|
||||
ADBCD53E-C9A6-A3CA-1AAC-0DB0CE84F71E,Script
|
||||
{set pid [::InstallAPI::FindProcesses -name calibre.exe]
|
||||
if {$pid eq ""} {
|
||||
## myapp.exe is not running
|
||||
return 1
|
||||
}
|
||||
return 0}
|
||||
|
||||
AIX-ppc,Active
|
||||
No
|
||||
|
||||
|
@ -65,6 +65,23 @@ def sanitize_file_name(name, substitute='_', as_unicode=False):
|
||||
return one
|
||||
|
||||
|
||||
def prints(*args, **kwargs):
|
||||
'''
|
||||
Print unicode arguments safely by encoding them to preferred_encoding
|
||||
Has the same signature as the print function from Python 3.
|
||||
'''
|
||||
file = kwargs.get('file', sys.stdout)
|
||||
sep = kwargs.get('sep', ' ')
|
||||
end = kwargs.get('end', '\n')
|
||||
for i, arg in enumerate(args):
|
||||
if isinstance(arg, unicode):
|
||||
arg = arg.encode(preferred_encoding)
|
||||
file.write(arg)
|
||||
if i != len(args)-1:
|
||||
file.write(sep)
|
||||
file.write(end)
|
||||
file.flush()
|
||||
|
||||
class CommandLineError(Exception):
|
||||
pass
|
||||
|
||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.4.129'
|
||||
__version__ = '0.4.131'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
'''
|
||||
Various run time constants.
|
||||
|
@ -132,7 +132,7 @@ class HTMLMetadataReader(MetadataReaderPlugin):
|
||||
class MOBIMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read MOBI metadata'
|
||||
file_types = set(['mobi'])
|
||||
file_types = set(['mobi', 'prc'])
|
||||
description = _('Read metadata from %s files')%'MOBI'
|
||||
|
||||
def get_metadata(self, stream, ftype):
|
||||
@ -189,11 +189,22 @@ class ZipMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.zip import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
class RARMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read RAR metadata'
|
||||
file_types = set(['rar'])
|
||||
description = _('Read metadata from ebooks in RAR archives')
|
||||
|
||||
def get_metadata(self, stream, ftype):
|
||||
from calibre.ebooks.metadata.rar import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class EPUBMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set EPUB metadata'
|
||||
file_types = set(['epub'])
|
||||
description = _('Set metadata in EPUB files')
|
||||
description = _('Set metadata in %s files')%'EPUB'
|
||||
|
||||
def set_metadata(self, stream, mi, type):
|
||||
from calibre.ebooks.metadata.epub import set_metadata
|
||||
@ -203,7 +214,7 @@ class LRFMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set LRF metadata'
|
||||
file_types = set(['lrf'])
|
||||
description = _('Set metadata in LRF files')
|
||||
description = _('Set metadata in %s files')%'LRF'
|
||||
|
||||
def set_metadata(self, stream, mi, type):
|
||||
from calibre.ebooks.lrf.meta import set_metadata
|
||||
@ -213,12 +224,24 @@ class RTFMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set RTF metadata'
|
||||
file_types = set(['rtf'])
|
||||
description = _('Set metadata in RTF files')
|
||||
description = _('Set metadata in %s files')%'RTF'
|
||||
|
||||
def set_metadata(self, stream, mi, type):
|
||||
from calibre.ebooks.metadata.rtf import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
class MOBIMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set MOBI metadata'
|
||||
file_types = set(['mobi', 'prc'])
|
||||
description = _('Set metadata in %s files')%'MOBI'
|
||||
author = 'Marshall T. Vandegrift'
|
||||
|
||||
def set_metadata(self, stream, mi, type):
|
||||
from calibre.ebooks.metadata.mobi import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
plugins = [HTML2ZIP]
|
||||
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
||||
x.__name__.endswith('MetadataReader')]
|
||||
|
@ -114,7 +114,19 @@ def reread_metadata_plugins():
|
||||
_metadata_writers[ft] = []
|
||||
_metadata_writers[ft].append(plugin)
|
||||
|
||||
|
||||
def metadata_readers():
|
||||
ans = set([])
|
||||
for plugins in _metadata_readers.values():
|
||||
for plugin in plugins:
|
||||
ans.add(plugin)
|
||||
return ans
|
||||
|
||||
def metadata_writers():
|
||||
ans = set([])
|
||||
for plugins in _metadata_writers.values():
|
||||
for plugin in plugins:
|
||||
ans.add(plugin)
|
||||
return ans
|
||||
|
||||
def get_file_type_metadata(stream, ftype):
|
||||
mi = MetaInformation(None, None)
|
||||
|
@ -21,6 +21,9 @@ Run an embedded python interpreter.
|
||||
'Module specifications are of the form full.name.of.module,path_to_module.py', default=None
|
||||
)
|
||||
parser.add_option('-c', '--command', help='Run python code.', default=None)
|
||||
parser.add_option('-e', '--exec-file', default=None, help='Run the python code in file.')
|
||||
parser.add_option('-d', '--debug-device-driver', default=False, action='store_true',
|
||||
help='Debug the specified device driver.')
|
||||
parser.add_option('-g', '--gui', default=False, action='store_true',
|
||||
help='Run the GUI',)
|
||||
parser.add_option('--migrate', action='store_true', default=False,
|
||||
@ -75,6 +78,40 @@ def migrate(old, new):
|
||||
prefs['library_path'] = os.path.abspath(new)
|
||||
print 'Database migrated to', os.path.abspath(new)
|
||||
|
||||
def debug_device_driver():
|
||||
from calibre.devices.scanner import DeviceScanner
|
||||
s = DeviceScanner()
|
||||
s.scan()
|
||||
print 'USB devices on system:', repr(s.devices)
|
||||
if iswindows:
|
||||
wmi = __import__('wmi', globals(), locals(), [], -1)
|
||||
drives = []
|
||||
print 'Drives detected:'
|
||||
print '\t', '(ID, Partitions, Drive letter)'
|
||||
for drive in wmi.WMI().Win32_DiskDrive():
|
||||
if drive.Partitions == 0:
|
||||
continue
|
||||
try:
|
||||
partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
|
||||
logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
|
||||
prefix = logical_disk.DeviceID+os.sep
|
||||
drives.append((str(drive.PNPDeviceID), drive.Index, prefix))
|
||||
except IndexError:
|
||||
drives.append(str(drive.PNPDeviceID))
|
||||
for drive in drives:
|
||||
print '\t', drive
|
||||
from calibre.devices import devices
|
||||
for dev in devices():
|
||||
print 'Looking for', dev.__name__
|
||||
connected = s.is_device_connected(dev)
|
||||
if connected:
|
||||
print 'Device Connected:', dev
|
||||
print 'Trying to open device...'
|
||||
d = dev()
|
||||
d.open()
|
||||
print 'Total space:', d.total_space()
|
||||
break
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
opts, args = option_parser().parse_args(args)
|
||||
@ -87,6 +124,11 @@ def main(args=sys.argv):
|
||||
elif opts.command:
|
||||
sys.argv = args[:1]
|
||||
exec opts.command
|
||||
elif opts.exec_file:
|
||||
sys.argv = args[:1]
|
||||
execfile(opts.exec_file)
|
||||
elif opts.debug_device_driver:
|
||||
debug_device_driver()
|
||||
elif opts.migrate:
|
||||
if len(args) < 3:
|
||||
print 'You must specify the path to library1.db and the path to the new library folder'
|
||||
|
@ -32,6 +32,7 @@ class CYBOOKG3(USBMS):
|
||||
STORAGE_CARD_VOLUME_LABEL = 'Cybook Gen 3 Storage Card'
|
||||
|
||||
EBOOK_DIR_MAIN = "eBooks"
|
||||
EBOOK_DIR_CARD = "eBooks"
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
def upload_books(self, files, names, on_card=False, end_session=True,
|
||||
|
@ -120,7 +120,6 @@ class Device(_Device):
|
||||
|
||||
def total_space(self, end_session=True):
|
||||
msz = csz = 0
|
||||
print self._main_prefix
|
||||
if not iswindows:
|
||||
if self._main_prefix is not None:
|
||||
stats = os.statvfs(self._main_prefix)
|
||||
@ -185,11 +184,11 @@ class Device(_Device):
|
||||
if 'main' and 'card' in drives.keys():
|
||||
break
|
||||
|
||||
if not drives:
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.') % self.__class__.__name__)
|
||||
|
||||
self._main_prefix = drives.get('main', None)
|
||||
self._card_prefix = drives.get('card', None)
|
||||
|
||||
if not self._main_prefix:
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.') % self.__class__.__name__)
|
||||
|
||||
def get_osx_mountpoints(self, raw=None):
|
||||
if raw is None:
|
||||
|
@ -9,6 +9,8 @@ for a particular device.
|
||||
import os, fnmatch, shutil
|
||||
from itertools import cycle
|
||||
|
||||
from calibre.ebooks.metadata.meta import metadata_from_formats, path_to_ext
|
||||
from calibre.ebooks.metadata import authors_to_string
|
||||
from calibre.devices.usbms.device import Device
|
||||
from calibre.devices.usbms.books import BookList, Book
|
||||
from calibre.devices.errors import FreeSpaceError, PathError
|
||||
@ -59,9 +61,7 @@ class USBMS(Device):
|
||||
# types
|
||||
for book_type in self.FORMATS:
|
||||
for filename in fnmatch.filter(files, '*.%s' % (book_type)):
|
||||
title, author, mime = self.__class__.extract_book_metadata_by_filename(filename)
|
||||
|
||||
bl.append(Book(os.path.join(path, filename), title, author, mime))
|
||||
bl.append(self.__class__.book_from_path(os.path.join(path, filename)))
|
||||
return bl
|
||||
|
||||
def upload_books(self, files, names, on_card=False, end_session=True,
|
||||
@ -132,8 +132,7 @@ class USBMS(Device):
|
||||
path = location[0]
|
||||
on_card = 1 if location[1] else 0
|
||||
|
||||
title, author, mime = cls.extract_book_metadata_by_filename(os.path.basename(path))
|
||||
book = Book(path, title, author, mime)
|
||||
book = cls.book_from_path(path)
|
||||
|
||||
if not book in booklists[on_card]:
|
||||
booklists[on_card].append(book)
|
||||
@ -217,28 +216,13 @@ class USBMS(Device):
|
||||
os.utime(path, None)
|
||||
|
||||
@classmethod
|
||||
def extract_book_metadata_by_filename(cls, filename):
|
||||
book_title = ''
|
||||
book_author = ''
|
||||
book_mime = ''
|
||||
# Calibre uses a specific format for file names. They take the form
|
||||
# title_-_author_number.extention We want to see if the file name is
|
||||
# in this format.
|
||||
if fnmatch.fnmatchcase(filename, '*_-_*.*'):
|
||||
# Get the title and author from the file name
|
||||
title, sep, author = filename.rpartition('_-_')
|
||||
author, sep, ext = author.rpartition('_')
|
||||
book_title = title.replace('_', ' ')
|
||||
book_author = author.replace('_', ' ')
|
||||
# if the filename did not match just set the title to
|
||||
# the filename without the extension
|
||||
else:
|
||||
book_title = os.path.splitext(filename)[0].replace('_', ' ')
|
||||
|
||||
fileext = os.path.splitext(filename)[1][1:]
|
||||
|
||||
if fileext in cls.FORMATS:
|
||||
book_mime = MIME_MAP[fileext] if fileext in MIME_MAP.keys() else 'Unknown'
|
||||
|
||||
return book_title, book_author, book_mime
|
||||
def book_from_path(cls, path):
|
||||
fileext = path_to_ext(path)
|
||||
|
||||
mi = metadata_from_formats([path])
|
||||
mime = MIME_MAP[fileext] if fileext in MIME_MAP.keys() else 'Unknown'
|
||||
|
||||
authors = authors_to_string(mi.authors)
|
||||
|
||||
return Book(path, mi.title, authors, mime)
|
||||
|
||||
|
@ -153,11 +153,30 @@ class HTMLProcessor(Processor, Rationalizer):
|
||||
Perform various markup transforms to get the output to render correctly
|
||||
in the quirky ADE.
|
||||
'''
|
||||
# Replace <br> that are children of <body> with <p> </p>
|
||||
# Replace <br> that are children of <body> as ADE doesn't handle them
|
||||
if hasattr(self.body, 'xpath'):
|
||||
for br in self.body.xpath('./br'):
|
||||
if br.getparent() is None:
|
||||
continue
|
||||
try:
|
||||
sibling = br.itersiblings().next()
|
||||
except:
|
||||
sibling = None
|
||||
br.tag = 'p'
|
||||
br.text = u'\u00a0'
|
||||
if (br.tail and br.tail.strip()) or sibling is None or \
|
||||
getattr(sibling, 'tag', '') != 'br':
|
||||
style = br.get('style', '').split(';')
|
||||
style = filter(None, map(lambda x: x.strip(), style))
|
||||
style.append('margin: 0pt; border:0pt; height:0pt')
|
||||
br.set('style', '; '.join(style))
|
||||
else:
|
||||
sibling.getparent().remove(sibling)
|
||||
if sibling.tail:
|
||||
if not br.tail:
|
||||
br.tail = ''
|
||||
br.tail += sibling.tail
|
||||
|
||||
|
||||
if self.opts.profile.remove_object_tags:
|
||||
for tag in self.root.xpath('//embed'):
|
||||
@ -166,6 +185,16 @@ class HTMLProcessor(Processor, Rationalizer):
|
||||
if tag.get('type', '').lower().strip() in ('image/svg+xml',):
|
||||
continue
|
||||
tag.getparent().remove(tag)
|
||||
|
||||
|
||||
for tag in self.root.xpath('//title|//style'):
|
||||
if not tag.text:
|
||||
tag.getparent().remove(tag)
|
||||
for tag in self.root.xpath('//script'):
|
||||
if not tag.text and not tag.get('src', False):
|
||||
tag.getparent().remove(tag)
|
||||
|
||||
|
||||
|
||||
def save(self):
|
||||
for meta in list(self.root.xpath('//meta')):
|
||||
|
@ -50,6 +50,7 @@ class Splitter(LoggingInterface):
|
||||
self.split_size = 0
|
||||
|
||||
# Split on page breaks
|
||||
self.splitting_on_page_breaks = True
|
||||
if not opts.dont_split_on_page_breaks:
|
||||
self.log_info('\tSplitting on page breaks...')
|
||||
if self.path in stylesheet_map:
|
||||
@ -61,6 +62,7 @@ class Splitter(LoggingInterface):
|
||||
trees = list(self.trees)
|
||||
|
||||
# Split any remaining over-sized trees
|
||||
self.splitting_on_page_breaks = False
|
||||
if self.opts.profile.flow_size < sys.maxint:
|
||||
lt_found = False
|
||||
self.log_info('\tLooking for large trees...')
|
||||
@ -203,7 +205,8 @@ class Splitter(LoggingInterface):
|
||||
elem.set('style', 'display:none')
|
||||
|
||||
def fix_split_point(sp):
|
||||
sp.set('style', sp.get('style', '')+'page-break-before:avoid;page-break-after:avoid')
|
||||
if not self.splitting_on_page_breaks:
|
||||
sp.set('style', sp.get('style', '')+'page-break-before:avoid;page-break-after:avoid')
|
||||
|
||||
# Tree 1
|
||||
hit_split_point = False
|
||||
@ -304,7 +307,11 @@ class Splitter(LoggingInterface):
|
||||
Search order is:
|
||||
* Heading tags
|
||||
* <div> tags
|
||||
* <pre> tags
|
||||
* <hr> tags
|
||||
* <p> tags
|
||||
* <br> tags
|
||||
* <li> tags
|
||||
|
||||
We try to split in the "middle" of the file (as defined by tag counts.
|
||||
'''
|
||||
@ -324,6 +331,7 @@ class Splitter(LoggingInterface):
|
||||
'//hr',
|
||||
'//p',
|
||||
'//br',
|
||||
'//li',
|
||||
):
|
||||
elems = root.xpath(path, namespaces={'re':'http://exslt.org/regular-expressions'})
|
||||
elem = pick_elem(elems)
|
||||
|
@ -417,39 +417,44 @@ class Parser(PreProcessor, LoggingInterface):
|
||||
self.level = self.htmlfile.level
|
||||
for f in self.htmlfiles:
|
||||
name = os.path.basename(f.path)
|
||||
name = os.path.splitext(name)[0] + '.xhtml'
|
||||
if name in self.htmlfile_map.values():
|
||||
name = os.path.splitext(name)[0] + '_cr_%d'%save_counter + os.path.splitext(name)[1]
|
||||
save_counter += 1
|
||||
self.htmlfile_map[f.path] = name
|
||||
|
||||
self.parse_html()
|
||||
# Handle <image> tags inside embedded <svg>
|
||||
# At least one source of EPUB files (Penguin) uses xlink:href
|
||||
# without declaring the xlink namespace
|
||||
for image in self.root.xpath('//image'):
|
||||
for attr in image.attrib.keys():
|
||||
if attr.endswith(':href'):
|
||||
nhref = self.rewrite_links(image.get(attr))
|
||||
image.set(attr, nhref)
|
||||
|
||||
self.root.rewrite_links(self.rewrite_links, resolve_base_href=False)
|
||||
for bad in ('xmlns', 'lang', 'xml:lang'): # lxml also adds these attributes for XHTML documents, leading to duplicates
|
||||
if self.root.get(bad, None) is not None:
|
||||
self.root.attrib.pop(bad)
|
||||
|
||||
|
||||
|
||||
def save_path(self):
|
||||
return os.path.join(self.tdir, self.htmlfile_map[self.htmlfile.path])
|
||||
|
||||
def declare_xhtml_namespace(self, match):
|
||||
if not match.group('raw'):
|
||||
return '<html xmlns="http://www.w3.org/1999/xhtml">'
|
||||
raw = match.group('raw')
|
||||
m = re.search(r'(?i)xmlns\s*=\s*[\'"](?P<uri>[^"\']*)[\'"]', raw)
|
||||
if not m:
|
||||
return '<html xmlns="http://www.w3.org/1999/xhtml" %s>'%raw
|
||||
else:
|
||||
return match.group().replace(m.group('uri'), "http://www.w3.org/1999/xhtml")
|
||||
|
||||
def save(self):
|
||||
'''
|
||||
Save processed HTML into the content directory.
|
||||
Should be called after all HTML processing is finished.
|
||||
'''
|
||||
self.root.set('xmlns', 'http://www.w3.org/1999/xhtml')
|
||||
self.root.set('xmlns:xlink', 'http://www.w3.org/1999/xlink')
|
||||
for svg in self.root.xpath('//svg'):
|
||||
svg.set('xmlns', 'http://www.w3.org/2000/svg')
|
||||
|
||||
ans = tostring(self.root, pretty_print=self.opts.pretty_print)
|
||||
ans = re.sub(r'(?i)<\s*html(?P<raw>\s+[^>]*){0,1}>', self.declare_xhtml_namespace, ans[:1000]) + ans[1000:]
|
||||
ans = re.compile(r'<head>', re.IGNORECASE).sub('<head>\n\t<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n', ans[:1000])+ans[1000:]
|
||||
|
||||
with open(self.save_path(), 'wb') as f:
|
||||
f.write(ans)
|
||||
return f.name
|
||||
@ -823,21 +828,28 @@ class Processor(Parser):
|
||||
font.set('class', cn)
|
||||
font.tag = 'span'
|
||||
|
||||
id_css, id_css_counter = {}, 0
|
||||
for elem in self.root.xpath('//*[@style]'):
|
||||
setting = elem.get('style')
|
||||
classname = cache.get(setting, None)
|
||||
if classname is None:
|
||||
classname = 'calibre_class_%d'%class_counter
|
||||
class_counter += 1
|
||||
cache[setting] = classname
|
||||
cn = elem.get('class', '')
|
||||
if cn: cn += ' '
|
||||
cn += classname
|
||||
elem.set('class', cn)
|
||||
if elem.get('id', False) or elem.get('class', False):
|
||||
elem.set('id', elem.get('id', 'calibre_css_id_%d'%id_css_counter))
|
||||
id_css_counter += 1
|
||||
id_css[elem.tag+'#'+elem.get('id')] = setting
|
||||
else:
|
||||
classname = cache.get(setting, None)
|
||||
if classname is None:
|
||||
classname = 'calibre_class_%d'%class_counter
|
||||
class_counter += 1
|
||||
cache[setting] = classname
|
||||
cn = elem.get('class', classname)
|
||||
elem.set('class', cn)
|
||||
elem.attrib.pop('style')
|
||||
|
||||
css = '\n'.join(['.%s {%s;}'%(cn, setting) for \
|
||||
setting, cn in cache.items()])
|
||||
css += '\n\n'
|
||||
css += '\n'.join(['%s {%s;}'%(selector, setting) for \
|
||||
selector, setting in id_css.items()])
|
||||
sheet = self.css_parser.parseString(self.preprocess_css(css.replace(';;}', ';}')))
|
||||
for rule in sheet:
|
||||
self.stylesheet.add(rule)
|
||||
|
@ -11,6 +11,7 @@ import sys, struct, cStringIO, os
|
||||
import functools
|
||||
import re
|
||||
from urlparse import urldefrag
|
||||
from urllib import unquote as urlunquote
|
||||
from lxml import etree
|
||||
from calibre.ebooks.lit import LitError
|
||||
from calibre.ebooks.lit.maps import OPF_MAP, HTML_MAP
|
||||
@ -611,6 +612,8 @@ class LitReader(object):
|
||||
offset, raw = u32(raw), raw[4:]
|
||||
internal, raw = consume_sized_utf8_string(raw)
|
||||
original, raw = consume_sized_utf8_string(raw)
|
||||
# The path should be stored unquoted, but not always
|
||||
original = urlunquote(original)
|
||||
# Is this last one UTF-8 or ASCIIZ?
|
||||
mime_type, raw = consume_sized_utf8_string(raw, zpad=True)
|
||||
self.manifest[internal] = ManifestItem(
|
||||
|
@ -122,6 +122,8 @@ LZXC_CONTROL = \
|
||||
|
||||
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
|
||||
|
||||
PAGE_BREAKS = set(['always', 'left', 'right'])
|
||||
|
||||
def decint(value):
|
||||
bytes = []
|
||||
while True:
|
||||
@ -202,7 +204,7 @@ class ReBinary(object):
|
||||
self.write(FLAG_CUSTOM, len(tag)+1, tag)
|
||||
last_break = self.page_breaks[-1][0] if self.page_breaks else None
|
||||
if style and last_break != tag_offset \
|
||||
and style['page-break-before'] not in ('avoid', 'auto'):
|
||||
and style['page-break-before'] in PAGE_BREAKS:
|
||||
self.page_breaks.append((tag_offset, list(parents)))
|
||||
for attr, value in attrib.items():
|
||||
attr = prefixname(attr, nsrmap)
|
||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
||||
Based on ideas from comiclrf created by FangornUK.
|
||||
'''
|
||||
|
||||
import os, sys, shutil, traceback, textwrap
|
||||
import os, sys, shutil, traceback, textwrap, fnmatch
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
@ -389,10 +389,33 @@ def create_lrf(pages, profile, opts, thumbnail=None):
|
||||
print _('Output written to'), opts.output
|
||||
|
||||
|
||||
def create_pdf(pages, profile, opts, thumbnail=None):
|
||||
def create_pdf(pages, profile, opts, thumbnail=None,toc=None):
|
||||
width, height = PROFILES[profile]
|
||||
|
||||
from reportlab.pdfgen import canvas
|
||||
|
||||
cur_page=0
|
||||
heading = []
|
||||
if toc != None:
|
||||
if len(toc) == 1:
|
||||
toc = None
|
||||
else:
|
||||
toc_index = 0
|
||||
base_cur = 0
|
||||
rem = 0
|
||||
breaker = False
|
||||
while True:
|
||||
letter=toc[0][0][base_cur]
|
||||
for i in range(len(toc)):
|
||||
if letter != toc[i][0][base_cur]:
|
||||
breaker = True
|
||||
if breaker:
|
||||
break
|
||||
if letter == os.sep:
|
||||
rem=base_cur
|
||||
base_cur += 1
|
||||
toc.append(("Not seen",-1))
|
||||
|
||||
|
||||
pdf = canvas.Canvas(filename=opts.output, pagesize=(width,height+15))
|
||||
pdf.setAuthor(opts.author)
|
||||
@ -400,7 +423,52 @@ def create_pdf(pages, profile, opts, thumbnail=None):
|
||||
|
||||
|
||||
for page in pages:
|
||||
pdf.drawImage(page, x=0,y=0,width=width, height=height)
|
||||
if opts.keep_aspect_ratio:
|
||||
img = NewMagickWand()
|
||||
if img < 0:
|
||||
raise RuntimeError('Cannot create wand.')
|
||||
if not MagickReadImage(img, page):
|
||||
raise IOError('Failed to read image from: %'%page)
|
||||
sizex = MagickGetImageWidth(img)
|
||||
sizey = MagickGetImageHeight(img)
|
||||
if opts.keep_aspect_ratio:
|
||||
# Preserve the aspect ratio by adding border
|
||||
aspect = float(sizex) / float(sizey)
|
||||
if aspect <= (float(width) / float(height)):
|
||||
newsizey = height
|
||||
newsizex = int(newsizey * aspect)
|
||||
deltax = (width - newsizex) / 2
|
||||
deltay = 0
|
||||
else:
|
||||
newsizex = width
|
||||
newsizey = int(newsizex / aspect)
|
||||
deltax = 0
|
||||
deltay = (height - newsizey) / 2
|
||||
pdf.drawImage(page, x=deltax,y=deltay,width=newsizex, height=newsizey)
|
||||
else:
|
||||
pdf.drawImage(page, x=0,y=0,width=width, height=height)
|
||||
if toc != None:
|
||||
if toc[toc_index][1] == cur_page:
|
||||
tmp=toc[toc_index][0]
|
||||
toc_current=tmp[rem:len(tmp)-4]
|
||||
index=0
|
||||
while True:
|
||||
key = 'page%d-%d' % (cur_page, index)
|
||||
pdf.bookmarkPage(key)
|
||||
(head,dummy,list)=toc_current.partition(os.sep)
|
||||
try:
|
||||
if heading[index] != head:
|
||||
heading[index] = head
|
||||
pdf.addOutlineEntry(title=head,key=key,level=index)
|
||||
except:
|
||||
heading.append(head)
|
||||
pdf.addOutlineEntry(title=head,key=key,level=index)
|
||||
index += 1
|
||||
toc_current=list
|
||||
if dummy == "":
|
||||
break
|
||||
toc_index += 1
|
||||
cur_page += 1
|
||||
pdf.showPage()
|
||||
# Write the document to disk
|
||||
pdf.save()
|
||||
@ -409,36 +477,64 @@ def create_pdf(pages, profile, opts, thumbnail=None):
|
||||
def do_convert(path_to_file, opts, notification=lambda m, p: p, output_format='lrf'):
|
||||
path_to_file = run_plugins_on_preprocess(path_to_file)
|
||||
source = path_to_file
|
||||
to_delete = []
|
||||
toc = []
|
||||
list = []
|
||||
pages = []
|
||||
|
||||
|
||||
if not opts.title:
|
||||
opts.title = os.path.splitext(os.path.basename(source))[0]
|
||||
if not opts.output:
|
||||
opts.output = os.path.abspath(os.path.splitext(os.path.basename(source))[0]+'.'+output_format)
|
||||
tdir = extract_comic(source)
|
||||
pages = find_pages(tdir, sort_on_mtime=opts.no_sort, verbose=opts.verbose)
|
||||
thumbnail = None
|
||||
if not pages:
|
||||
raise ValueError('Could not find any pages in the comic: %s'%source)
|
||||
if not getattr(opts, 'no_process', False):
|
||||
pages, failures, tdir2 = process_pages(pages, opts, notification)
|
||||
if not pages:
|
||||
raise ValueError('Could not find any valid pages in the comic: %s'%source)
|
||||
if failures:
|
||||
print 'Could not process the following pages (run with --verbose to see why):'
|
||||
for f in failures:
|
||||
print '\t', f
|
||||
thumbnail = os.path.join(tdir2, 'thumbnail.png')
|
||||
if not os.access(thumbnail, os.R_OK):
|
||||
thumbnail = None
|
||||
if os.path.isdir(source):
|
||||
for path in all_files( source , '*.cbr|*.cbz' ):
|
||||
list.append( path )
|
||||
else:
|
||||
list= [ os.path.abspath(source) ]
|
||||
|
||||
for source in list:
|
||||
tdir = extract_comic(source)
|
||||
new_pages = find_pages(tdir, sort_on_mtime=opts.no_sort, verbose=opts.verbose)
|
||||
thumbnail = None
|
||||
if not new_pages:
|
||||
raise ValueError('Could not find any pages in the comic: %s'%source)
|
||||
if not getattr(opts, 'no_process', False):
|
||||
new_pages, failures, tdir2 = process_pages(new_pages, opts, notification)
|
||||
if not new_pages:
|
||||
raise ValueError('Could not find any valid pages in the comic: %s'%source)
|
||||
if failures:
|
||||
print 'Could not process the following pages (run with --verbose to see why):'
|
||||
for f in failures:
|
||||
print '\t', f
|
||||
thumbnail = os.path.join(tdir2, 'thumbnail.png')
|
||||
if not os.access(thumbnail, os.R_OK):
|
||||
thumbnail = None
|
||||
toc.append((source,len(pages)))
|
||||
pages.extend(new_pages)
|
||||
to_delete.append(tdir)
|
||||
|
||||
|
||||
if output_format == 'lrf':
|
||||
create_lrf(pages, opts.profile, opts, thumbnail=thumbnail)
|
||||
if output_format == 'epub':
|
||||
create_epub(pages, opts.profile, opts, thumbnail=thumbnail)
|
||||
if output_format == 'pdf':
|
||||
create_pdf(pages, opts.profile, opts, thumbnail=thumbnail)
|
||||
shutil.rmtree(tdir)
|
||||
if not getattr(opts, 'no_process', False):
|
||||
shutil.rmtree(tdir2)
|
||||
create_pdf(pages, opts.profile, opts, thumbnail=thumbnail,toc=toc)
|
||||
for tdir in to_delete:
|
||||
shutil.rmtree(tdir)
|
||||
|
||||
|
||||
def all_files(root, patterns='*'):
|
||||
# Expand patterns from semicolon-separated string to list
|
||||
patterns = patterns.split('|')
|
||||
for path, subdirs, files in os.walk(root):
|
||||
files.sort( )
|
||||
for name in files:
|
||||
for pattern in patterns:
|
||||
if fnmatch.fnmatch(name, pattern):
|
||||
yield os.path.join(path, name)
|
||||
break
|
||||
|
||||
|
||||
def main(args=sys.argv, notification=None, output_format='lrf'):
|
||||
|
@ -1720,7 +1720,7 @@ class HTMLConverter(object, LoggingInterface):
|
||||
self.previous_text = '\n'
|
||||
elif tagname in ['hr', 'tr']: # tr needed for nested tables
|
||||
self.end_current_block()
|
||||
if tagname == 'hr':
|
||||
if tagname == 'hr' and not tag_css.get('width', '').strip().startswith('0'):
|
||||
self.current_page.RuledLine(linelength=int(self.current_page.pageStyle.attrs['textwidth']))
|
||||
self.previous_text = '\n'
|
||||
self.process_children(tag, tag_css, tag_pseudo_css)
|
||||
|
@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
|
||||
"""
|
||||
Provides abstraction for metadata reading.writing from a variety of ebook formats.
|
||||
"""
|
||||
import os, mimetypes, sys
|
||||
import os, mimetypes, sys, re
|
||||
from urllib import unquote, quote
|
||||
from urlparse import urlparse
|
||||
|
||||
@ -21,7 +21,10 @@ def string_to_authors(raw):
|
||||
return authors
|
||||
|
||||
def authors_to_string(authors):
|
||||
return ' & '.join([a.replace('&', '&&') for a in authors])
|
||||
if authors is not None:
|
||||
return ' & '.join([a.replace('&', '&&') for a in authors if a])
|
||||
else:
|
||||
return ''
|
||||
|
||||
def author_to_author_sort(author):
|
||||
tokens = author.split()
|
||||
@ -33,18 +36,14 @@ def author_to_author_sort(author):
|
||||
def authors_to_sort_string(authors):
|
||||
return ' & '.join(map(author_to_author_sort, authors))
|
||||
|
||||
def get_parser(extension):
|
||||
''' Return an option parser with the basic metadata options already setup'''
|
||||
parser = OptionParser(usage='%prog [options] myfile.'+extension+'\n\nRead and write metadata from an ebook file.')
|
||||
parser.add_option("-t", "--title", action="store", type="string", \
|
||||
dest="title", help=_("Set the book title"), default=None)
|
||||
parser.add_option("-a", "--authors", action="store", type="string", \
|
||||
dest="authors", help=_("Set the authors"), default=None)
|
||||
parser.add_option("-c", "--category", action="store", type="string", \
|
||||
dest="category", help=_("The category this book belongs to. E.g.: History"), default=None)
|
||||
parser.add_option('--comment', dest='comment', default=None, action='store',
|
||||
help=_('Set the comment'))
|
||||
return parser
|
||||
_title_pat = re.compile('^(A|The|An)\s+', re.IGNORECASE)
|
||||
def title_sort(title):
|
||||
match = _title_pat.search(title)
|
||||
if match:
|
||||
prep = match.group(1)
|
||||
title = title.replace(prep, '') + ', ' + prep
|
||||
return title.strip()
|
||||
|
||||
|
||||
class Resource(object):
|
||||
'''
|
||||
@ -186,7 +185,7 @@ class MetaInformation(object):
|
||||
@staticmethod
|
||||
def copy(mi):
|
||||
ans = MetaInformation(mi.title, mi.authors)
|
||||
for attr in ('author_sort', 'title_sort', 'comments', 'category',
|
||||
for attr in ('author_sort', 'title_sort', 'comments',
|
||||
'publisher', 'series', 'series_index', 'rating',
|
||||
'isbn', 'tags', 'cover_data', 'application_id', 'guide',
|
||||
'manifest', 'spine', 'toc', 'cover', 'language', 'book_producer'):
|
||||
@ -195,7 +194,7 @@ class MetaInformation(object):
|
||||
|
||||
def __init__(self, title, authors=[_('Unknown')]):
|
||||
'''
|
||||
@param title: title or "Unknown" or a MetaInformation object
|
||||
@param title: title or ``_('Unknown')`` or a MetaInformation object
|
||||
@param authors: List of strings or []
|
||||
'''
|
||||
mi = None
|
||||
@ -211,7 +210,7 @@ class MetaInformation(object):
|
||||
#: mi.cover_data = (ext, data)
|
||||
self.cover_data = getattr(mi, 'cover_data', (None, None))
|
||||
|
||||
for x in ('author_sort', 'title_sort', 'comments', 'category', 'publisher',
|
||||
for x in ('author_sort', 'title_sort', 'comments', 'publisher',
|
||||
'series', 'series_index', 'rating', 'isbn', 'language',
|
||||
'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
|
||||
'book_producer',
|
||||
@ -229,16 +228,16 @@ class MetaInformation(object):
|
||||
if mi.authors and mi.authors[0] != _('Unknown'):
|
||||
self.authors = mi.authors
|
||||
|
||||
for attr in ('author_sort', 'title_sort', 'comments', 'category',
|
||||
for attr in ('author_sort', 'title_sort', 'comments',
|
||||
'publisher', 'series', 'series_index', 'rating',
|
||||
'isbn', 'application_id', 'manifest', 'spine', 'toc',
|
||||
'cover', 'language', 'guide', 'book_producer'):
|
||||
if hasattr(mi, attr):
|
||||
val = getattr(mi, attr)
|
||||
if val is not None:
|
||||
setattr(self, attr, val)
|
||||
val = getattr(mi, attr, None)
|
||||
if val is not None:
|
||||
setattr(self, attr, val)
|
||||
|
||||
self.tags += mi.tags
|
||||
if mi.tags:
|
||||
self.tags += mi.tags
|
||||
self.tags = list(set(self.tags))
|
||||
|
||||
if getattr(mi, 'cover_data', None) and mi.cover_data[0] is not None:
|
||||
@ -246,35 +245,38 @@ class MetaInformation(object):
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
ans = u''
|
||||
ans += u'Title : ' + unicode(self.title) + u'\n'
|
||||
ans = [ fmt('Title', self.title) ]
|
||||
def fmt(x, y):
|
||||
ans.append(u'%-20s: %s'%(unicode(x), unicode(y)))
|
||||
|
||||
if self.title_sort:
|
||||
fmt('Title sort', self.title_sort)
|
||||
if self.authors:
|
||||
ans += u'Author : ' + (' & '.join(self.authors) if self.authors is not None else _('Unknown'))
|
||||
ans += ((' [' + self.author_sort + ']') if self.author_sort else '') + u'\n'
|
||||
fmt('Author(s)', authors_to_string(self.authors) + \
|
||||
((' [' + self.author_sort + ']') if self.author_sort else ''))
|
||||
if self.publisher:
|
||||
ans += u'Publisher: '+ unicode(self.publisher) + u'\n'
|
||||
fmt('Publisher', self.publisher)
|
||||
if getattr(self, 'book_producer', False):
|
||||
ans += u'Producer : '+ unicode(self.book_producer) + u'\n'
|
||||
if self.category:
|
||||
ans += u'Category : ' + unicode(self.category) + u'\n'
|
||||
fmt('Book Producer', self.book_producer)
|
||||
if self.comments:
|
||||
ans += u'Comments : ' + unicode(self.comments) + u'\n'
|
||||
fmt('Comments', self.comments)
|
||||
if self.isbn:
|
||||
ans += u'ISBN : ' + unicode(self.isbn) + u'\n'
|
||||
fmt('ISBN', self.isbn)
|
||||
if self.tags:
|
||||
ans += u'Tags : ' + u', '.join([unicode(t) for t in self.tags]) + '\n'
|
||||
fmt('Tags', u', '.join([unicode(t) for t in self.tags]))
|
||||
if self.series:
|
||||
ans += u'Series : '+unicode(self.series) + ' #%d\n'%self.series_index
|
||||
fmt('Series', self.series + '#%s'%self.series_index)
|
||||
if self.language:
|
||||
ans += u'Language : ' + unicode(self.language) + u'\n'
|
||||
return ans.strip()
|
||||
fmt('Language', self.language)
|
||||
if self.rating is not None:
|
||||
fmt('Rating', self.rating)
|
||||
return u'\n'.join(ans)
|
||||
|
||||
def to_html(self):
|
||||
ans = [(_('Title'), unicode(self.title))]
|
||||
ans += [(_('Author(s)'), (authors_to_string(self.authors) if self.authors else _('Unknown')))]
|
||||
ans += [(_('Publisher'), unicode(self.publisher))]
|
||||
ans += [(_('Producer'), unicode(self.book_producer))]
|
||||
ans += [(_('Category'), unicode(self.category))]
|
||||
ans += [(_('Comments'), unicode(self.comments))]
|
||||
ans += [('ISBN', unicode(self.isbn))]
|
||||
ans += [(_('Tags'), u', '.join([unicode(t) for t in self.tags]))]
|
||||
@ -290,4 +292,4 @@ class MetaInformation(object):
|
||||
return self.__unicode__().encode('utf-8')
|
||||
|
||||
def __nonzero__(self):
|
||||
return bool(self.title or self.author or self.comments or self.category)
|
||||
return bool(self.title or self.author or self.comments or self.tags)
|
||||
|
@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
'''Read meta information from epub files'''
|
||||
|
||||
import sys, os, time
|
||||
import os, time
|
||||
from cStringIO import StringIO
|
||||
from contextlib import closing
|
||||
|
||||
@ -15,10 +15,10 @@ from PyQt4.QtWebKit import QWebPage
|
||||
|
||||
from calibre.utils.zipfile import ZipFile, BadZipfile, safe_replace
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
||||
from calibre.ebooks.metadata import get_parser, MetaInformation
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.metadata.opf2 import OPF
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre import CurrentDir, fit_image
|
||||
from calibre import CurrentDir
|
||||
|
||||
class EPubException(Exception):
|
||||
pass
|
||||
@ -188,67 +188,10 @@ def get_metadata(stream, extract_cover=True):
|
||||
def set_metadata(stream, mi):
|
||||
stream.seek(0)
|
||||
reader = OCFZipReader(stream, root=os.getcwdu())
|
||||
mi = MetaInformation(mi)
|
||||
for x in ('guide', 'toc', 'manifest', 'spine'):
|
||||
setattr(mi, x, None)
|
||||
reader.opf.smart_update(mi)
|
||||
newopf = StringIO(reader.opf.render())
|
||||
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf)
|
||||
|
||||
def option_parser():
|
||||
parser = get_parser('epub')
|
||||
parser.remove_option('--category')
|
||||
parser.add_option('--tags', default=None,
|
||||
help=_('A comma separated list of tags to set'))
|
||||
parser.add_option('--series', default=None,
|
||||
help=_('The series to which this book belongs'))
|
||||
parser.add_option('--series-index', default=None,
|
||||
help=_('The series index'))
|
||||
parser.add_option('--language', default=None,
|
||||
help=_('The book language'))
|
||||
parser.add_option('--get-cover', default=False, action='store_true',
|
||||
help=_('Extract the cover'))
|
||||
return parser
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
if len(args) != 2:
|
||||
parser.print_help()
|
||||
return 1
|
||||
with open(args[1], 'r+b') as stream:
|
||||
mi = get_metadata(stream, extract_cover=opts.get_cover)
|
||||
changed = False
|
||||
if opts.title:
|
||||
mi.title = opts.title
|
||||
changed = True
|
||||
if opts.authors:
|
||||
mi.authors = opts.authors.split(',')
|
||||
changed = True
|
||||
if opts.tags:
|
||||
mi.tags = opts.tags.split(',')
|
||||
changed = True
|
||||
if opts.comment:
|
||||
mi.comments = opts.comment
|
||||
changed = True
|
||||
if opts.series:
|
||||
mi.series = opts.series
|
||||
changed = True
|
||||
if opts.series_index:
|
||||
mi.series_index = opts.series_index
|
||||
changed = True
|
||||
if opts.language is not None:
|
||||
mi.language = opts.language
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
set_metadata(stream, mi)
|
||||
print unicode(get_metadata(stream, extract_cover=False)).encode('utf-8')
|
||||
|
||||
if mi.cover_data[1] is not None:
|
||||
cpath = os.path.splitext(os.path.basename(args[1]))[0] + '_cover.jpg'
|
||||
with open(cpath, 'wb') as f:
|
||||
f.write(mi.cover_data[1])
|
||||
print 'Cover saved to', f.name
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
@ -48,15 +48,3 @@ def get_metadata(stream):
|
||||
if cdata:
|
||||
mi.cover_data = cdata
|
||||
return mi
|
||||
|
||||
def main(args=sys.argv):
|
||||
if len(args) != 2 or '--help' in args or '-h' in args:
|
||||
print >>sys.stderr, _('Usage:'), args[0], 'mybook.fb2'
|
||||
return 1
|
||||
|
||||
path = os.path.abspath(os.path.expanduser(args[1]))
|
||||
print unicode(get_metadata(open(path, 'rb')))
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
@ -46,17 +46,3 @@ def get_metadata(stream):
|
||||
msg = u'Couldn\'t read metadata from imp: %s with error %s'%(mi.title, unicode(err))
|
||||
print >>sys.stderr, msg.encode('utf8')
|
||||
return mi
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
if len(args) != 2:
|
||||
print >>sys.stderr, _('Usage: imp-meta file.imp')
|
||||
print >>sys.stderr, _('No filename specified.')
|
||||
return 1
|
||||
|
||||
path = os.path.abspath(os.path.expanduser(args[1]))
|
||||
print get_metadata(open(path, 'rb'))
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
@ -30,21 +30,3 @@ def get_metadata(stream):
|
||||
mi.cover_data = ('jpg', covers[-1])
|
||||
return mi
|
||||
|
||||
def main(args=sys.argv):
|
||||
if len(args) != 2:
|
||||
print >>sys.stderr, _('Usage: %s file.lit') % args[0]
|
||||
return 1
|
||||
fname = args[1]
|
||||
mi = get_metadata(open(fname, 'rb'))
|
||||
print unicode(mi)
|
||||
if mi.cover_data[1]:
|
||||
cover = os.path.abspath(
|
||||
'.'.join((os.path.splitext(os.path.basename(fname))[0],
|
||||
mi.cover_data[0])))
|
||||
open(cover, 'wb').write(mi.cover_data[1])
|
||||
print _('Cover saved to'), cover
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
|
@ -80,10 +80,3 @@ def get_metadata(f):
|
||||
else:
|
||||
raise ValueError('Not a LRX file')
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
print get_metadata(open(args[1], 'rb'))
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -72,6 +72,17 @@ def get_metadata(stream, stream_type='lrf', use_libprs_metadata=False):
|
||||
|
||||
name = os.path.basename(getattr(stream, 'name', ''))
|
||||
base = metadata_from_filename(name)
|
||||
if base.title == os.path.splitext(name)[0] and base.authors is None:
|
||||
# Assume that there was no metadata in the file and the user set pattern
|
||||
# to match meta info from the file name did not match.
|
||||
# The regex is meant to match the standard format filenames are written
|
||||
# in: title_-_author_number.extension
|
||||
base.smart_update(metadata_from_filename(name, re.compile(
|
||||
r'^(?P<title>\S+?)_-_(?P<author>\S+?)_+\d+')))
|
||||
if base.title:
|
||||
base.title = base.title.replace('_', ' ')
|
||||
if base.authors:
|
||||
base.authors = [a.replace('_', ' ').strip() for a in base.authors]
|
||||
if not base.authors:
|
||||
base.authors = [_('Unknown')]
|
||||
if not base.title:
|
||||
|
@ -1,29 +1,170 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
Retrieve and modify in-place Mobipocket book metadata.
|
||||
'''
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net and ' \
|
||||
'Marshall T. Vandegrift <llasram@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
'''
|
||||
|
||||
import sys, os
|
||||
|
||||
import sys
|
||||
import os
|
||||
from struct import pack, unpack
|
||||
from cStringIO import StringIO
|
||||
from calibre.ebooks.mobi import MobiError
|
||||
from calibre.ebooks.mobi.reader import get_metadata
|
||||
from calibre.ebooks.mobi.writer import rescale_image, MAX_THUMB_DIMEN
|
||||
from calibre.ebooks.mobi.langcodes import iana2mobi
|
||||
|
||||
def main(args=sys.argv):
|
||||
if len(args) != 2:
|
||||
print >>sys.stderr, 'Usage: %s file.mobi' % args[0]
|
||||
return 1
|
||||
fname = args[1]
|
||||
mi = get_metadata(open(fname, 'rb'))
|
||||
print unicode(mi)
|
||||
if mi.cover_data[1]:
|
||||
cover = os.path.abspath(
|
||||
'.'.join((os.path.splitext(os.path.basename(fname))[0],
|
||||
mi.cover_data[0].lower())))
|
||||
open(cover, 'wb').write(mi.cover_data[1])
|
||||
print _('Cover saved to'), cover
|
||||
return 0
|
||||
class StreamSlicer(object):
|
||||
def __init__(self, stream, start=0, stop=None):
|
||||
self._stream = stream
|
||||
self.start = start
|
||||
if stop is None:
|
||||
stream.seek(0, 2)
|
||||
stop = stream.tell()
|
||||
self.stop = stop
|
||||
self._len = stop - start
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
def __len__(self):
|
||||
return self._len
|
||||
|
||||
def __getitem__(self, key):
|
||||
stream = self._stream
|
||||
base = self.start
|
||||
if isinstance(key, (int, long)):
|
||||
stream.seek(base + key)
|
||||
return stream.read(1)
|
||||
if isinstance(key, slice):
|
||||
start, stop, stride = key.indices(self._len)
|
||||
if stride < 0:
|
||||
start, stop = stop, start
|
||||
size = stop - start
|
||||
if size <= 0:
|
||||
return ""
|
||||
stream.seek(base + start)
|
||||
data = stream.read(size)
|
||||
if stride != 1:
|
||||
data = data[::stride]
|
||||
return data
|
||||
raise TypeError("stream indices must be integers")
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
stream = self._stream
|
||||
base = self.start
|
||||
if isinstance(key, (int, long)):
|
||||
if len(value) != 1:
|
||||
raise ValueError("key and value lengths must match")
|
||||
stream.seek(base + key)
|
||||
return stream.write(value)
|
||||
if isinstance(key, slice):
|
||||
start, stop, stride = key.indices(self._len)
|
||||
if stride < 0:
|
||||
start, stop = stop, start
|
||||
size = stop - start
|
||||
if stride != 1:
|
||||
value = value[::stride]
|
||||
if len(value) != size:
|
||||
raise ValueError("key and value lengths must match")
|
||||
stream.seek(base + start)
|
||||
return stream.write(value)
|
||||
raise TypeError("stream indices must be integers")
|
||||
|
||||
|
||||
class MetadataUpdater(object):
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
data = self.data = StreamSlicer(stream)
|
||||
type = self.type = data[60:68]
|
||||
self.nrecs, = unpack('>H', data[76:78])
|
||||
record0 = self.record0 = self.record(0)
|
||||
codepage, = unpack('>I', record0[28:32])
|
||||
self.codec = 'utf-8' if codepage == 65001 else 'cp1252'
|
||||
image_base, = unpack('>I', record0[108:112])
|
||||
flags, = unpack('>I', record0[128:132])
|
||||
have_exth = self.have_exth = (flags & 0x40) != 0
|
||||
if not have_exth:
|
||||
return
|
||||
self.cover_record = self.thumbnail_record = None
|
||||
exth_off = unpack('>I', record0[20:24])[0] + 16 + record0.start
|
||||
exth = self.exth = StreamSlicer(stream, exth_off, record0.stop)
|
||||
nitems, = unpack('>I', exth[8:12])
|
||||
pos = 12
|
||||
for i in xrange(nitems):
|
||||
id, size = unpack('>II', exth[pos:pos + 8])
|
||||
content = exth[pos + 8: pos + size]
|
||||
pos += size
|
||||
if id == 201:
|
||||
rindex, = self.cover_rindex, = unpack('>I', content)
|
||||
self.cover_record = self.record(rindex + image_base)
|
||||
elif id == 202:
|
||||
rindex, = self.thumbnail_rindex, = unpack('>I', content)
|
||||
self.thumbnail_record = self.record(rindex + image_base)
|
||||
|
||||
def record(self, n):
|
||||
if n >= self.nrecs:
|
||||
raise ValueError('non-existent record %r' % n)
|
||||
offoff = 78 + (8 * n)
|
||||
start, = unpack('>I', self.data[offoff + 0:offoff + 4])
|
||||
stop = None
|
||||
if n < (self.nrecs - 1):
|
||||
stop, = unpack('>I', self.data[offoff + 8:offoff + 12])
|
||||
return StreamSlicer(self.stream, start, stop)
|
||||
|
||||
def update(self, mi):
|
||||
recs = []
|
||||
if mi.authors:
|
||||
authors = '; '.join(mi.authors)
|
||||
recs.append((100, authors.encode(self.codec, 'replace')))
|
||||
if mi.publisher:
|
||||
recs.append((101, mi.publisher.encode(self.codec, 'replace')))
|
||||
if mi.comments:
|
||||
recs.append((103, mi.comments.encode(self.codec, 'replace')))
|
||||
if mi.isbn:
|
||||
recs.append((104, mi.isbn.encode(self.codec, 'replace')))
|
||||
if mi.tags:
|
||||
subjects = '; '.join(mi.tags)
|
||||
recs.append((105, subjects.encode(self.codec, 'replace')))
|
||||
if self.cover_record is not None:
|
||||
recs.append((201, pack('>I', self.cover_rindex)))
|
||||
recs.append((203, pack('>I', 0)))
|
||||
if self.thumbnail_record is not None:
|
||||
recs.append((202, pack('>I', self.thumbnail_rindex)))
|
||||
exth = StringIO()
|
||||
for code, data in recs:
|
||||
exth.write(pack('>II', code, len(data) + 8))
|
||||
exth.write(data)
|
||||
exth = exth.getvalue()
|
||||
trail = len(exth) % 4
|
||||
pad = '\0' * (4 - trail) # Always pad w/ at least 1 byte
|
||||
exth = ['EXTH', pack('>II', len(exth) + 12, len(recs)), exth, pad]
|
||||
exth = ''.join(exth)
|
||||
title = (mi.title or _('Unknown')).encode(self.codec, 'replace')
|
||||
title_off = (self.exth.start - self.record0.start) + len(exth)
|
||||
title_len = len(title)
|
||||
trail = len(self.exth) - len(exth) - len(title)
|
||||
if trail < 0:
|
||||
raise MobiError("Insufficient space to update metadata")
|
||||
self.exth[:] = ''.join([exth, title, '\0' * trail])
|
||||
self.record0[84:92] = pack('>II', title_off, title_len)
|
||||
self.record0[92:96] = iana2mobi(mi.language)
|
||||
if mi.cover_data[1]:
|
||||
data = mi.cover_data[1]
|
||||
if self.cover_record is not None:
|
||||
size = len(self.cover_record)
|
||||
cover = rescale_image(data, size)
|
||||
cover += '\0' * (size - len(cover))
|
||||
self.cover_record[:] = cover
|
||||
if self.thumbnail_record is not None:
|
||||
size = len(self.thumbnail_record)
|
||||
thumbnail = rescale_image(data, size, dimen=MAX_THUMB_DIMEN)
|
||||
thumbnail += '\0' * (size - len(thumbnail))
|
||||
self.thumbnail_record[:] = thumbnail
|
||||
return
|
||||
|
||||
def set_metadata(stream, mi):
|
||||
mu = MetadataUpdater(stream)
|
||||
mu.update(mi)
|
||||
return
|
||||
|
@ -164,103 +164,3 @@ def get_metadata(stream):
|
||||
|
||||
return mi
|
||||
|
||||
def main(args=sys.argv):
|
||||
if len(args) != 2:
|
||||
print 'Usage: %s file.odt'%args[0]
|
||||
return 1
|
||||
mi = get_metadata(open(args[1], 'rb'))
|
||||
print mi
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
#now = time.localtime()[:6]
|
||||
#outputfile = "-"
|
||||
#writemeta = False # Do we change any meta data?
|
||||
#usenormalize = False
|
||||
#
|
||||
#try:
|
||||
# opts, args = getopt.getopt(sys.argv[1:], "cdlI:A:a:o:x:X:")
|
||||
#except getopt.GetoptError:
|
||||
# exitwithusage()
|
||||
#
|
||||
#if len(opts) == 0:
|
||||
# opts = [ ('-l','') ]
|
||||
#
|
||||
#for o, a in opts:
|
||||
# if o in ('-a','-A','-I'):
|
||||
# writemeta = True
|
||||
# if a.find(":") >= 0:
|
||||
# k,v = a.split(":",1)
|
||||
# else:
|
||||
# k,v = (a, "")
|
||||
# if len(k) == 0:
|
||||
# exitwithusage()
|
||||
# k = fields.get(k,k)
|
||||
# addfields[k] = unicode(v,'utf-8')
|
||||
# if o == '-a':
|
||||
# yieldfields[k] = True
|
||||
# if o == '-I':
|
||||
# deletefields[k] = True
|
||||
# if o == '-d':
|
||||
# writemeta = True
|
||||
# addfields[(DCNS,u'date')] = "%04d-%02d-%02dT%02d:%02d:%02d" % now
|
||||
# deletefields[(DCNS,u'date')] = True
|
||||
# if o == '-c':
|
||||
# usenormalize = True
|
||||
# if o == '-l':
|
||||
# Xfields = fields.values()
|
||||
# if o == "-x":
|
||||
# xfields.append(fields.get(a,a))
|
||||
# if o == "-X":
|
||||
# Xfields.append(fields.get(a,a))
|
||||
# if o == "-o":
|
||||
# outputfile = a
|
||||
#
|
||||
## The specification says we should change the element to our own,
|
||||
## and must not export the original identifier.
|
||||
#if writemeta:
|
||||
# addfields[(METANS,u'generator')] = TOOLSVERSION
|
||||
# deletefields[(METANS,u'generator')] = True
|
||||
#
|
||||
#odfs = odfmetaparser()
|
||||
#parser = xml.sax.make_parser()
|
||||
#parser.setFeature(xml.sax.handler.feature_namespaces, 1)
|
||||
#parser.setContentHandler(odfs)
|
||||
#
|
||||
#if len(args) == 0:
|
||||
# zin = zipfile.ZipFile(sys.stdin,'r')
|
||||
#else:
|
||||
# if not zipfile.is_zipfile(args[0]):
|
||||
# exitwithusage()
|
||||
# zin = zipfile.ZipFile(args[0], 'r')
|
||||
#
|
||||
#content = zin.read('meta.xml')
|
||||
#parser.parse(StringIO(content))
|
||||
#
|
||||
#if writemeta:
|
||||
# if outputfile == '-':
|
||||
# if sys.stdout.isatty():
|
||||
# sys.stderr.write("Won't write ODF file to terminal\n")
|
||||
# sys.exit(1)
|
||||
# zout = zipfile.ZipFile(sys.stdout,"w")
|
||||
# else:
|
||||
# zout = zipfile.ZipFile(outputfile,"w")
|
||||
#
|
||||
#
|
||||
#
|
||||
# # Loop through the input zipfile and copy the content to the output until we
|
||||
# # get to the meta.xml. Then substitute.
|
||||
# for zinfo in zin.infolist():
|
||||
# if zinfo.filename == "meta.xml":
|
||||
# # Write meta
|
||||
# zi = zipfile.ZipInfo("meta.xml", now)
|
||||
# zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
# zout.writestr(zi,odfs.meta() )
|
||||
# else:
|
||||
# payload = zin.read(zinfo.filename)
|
||||
# zout.writestr(zinfo, payload)
|
||||
#
|
||||
# zout.close()
|
||||
#zin.close()
|
||||
|
@ -11,7 +11,7 @@ from calibre.constants import __appname__, __version__
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, BeautifulSoup
|
||||
from calibre.ebooks.lrf import entity_to_unicode
|
||||
from calibre.ebooks.metadata import get_parser, Resource, ResourceCollection
|
||||
from calibre.ebooks.metadata import Resource, ResourceCollection
|
||||
from calibre.ebooks.metadata.toc import TOC
|
||||
|
||||
class OPFSoup(BeautifulStoneSoup):
|
||||
@ -541,45 +541,3 @@ class OPFCreator(MetaInformation):
|
||||
toc.render(ncx_stream, self.application_id)
|
||||
ncx_stream.flush()
|
||||
|
||||
def option_parser():
|
||||
return get_parser('opf')
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
if len(args) != 2:
|
||||
parser.print_help()
|
||||
return 1
|
||||
mi = MetaInformation(OPFReader(open(args[1], 'rb'), os.path.abspath(os.path.dirname(args[1]))))
|
||||
write = False
|
||||
if opts.title is not None:
|
||||
mi.title = opts.title.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||
write = True
|
||||
if opts.authors is not None:
|
||||
aus = [i.strip().replace('&', '&').replace('<', '<').replace('>', '>') for i in opts.authors.split(',')]
|
||||
mi.authors = aus
|
||||
write = True
|
||||
if opts.category is not None:
|
||||
mi.category = opts.category.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||
write = True
|
||||
if opts.comment is not None:
|
||||
mi.comments = opts.comment.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||
write = True
|
||||
if write:
|
||||
mo = OPFCreator(os.path.dirname(args[1]), mi)
|
||||
ncx = cStringIO.StringIO()
|
||||
mo.render(open(args[1], 'wb'), ncx)
|
||||
ncx = ncx.getvalue()
|
||||
if ncx:
|
||||
f = glob.glob(os.path.join(os.path.dirname(args[1]), '*.ncx'))
|
||||
if f:
|
||||
f = open(f[0], 'wb')
|
||||
else:
|
||||
f = open(os.path.splitext(args[1])[0]+'.ncx', 'wb')
|
||||
f.write(ncx)
|
||||
f.close()
|
||||
print MetaInformation(OPFReader(open(args[1], 'rb'), os.path.abspath(os.path.dirname(args[1]))))
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
@ -17,7 +17,7 @@ from calibre.ebooks.chardet import xml_to_unicode
|
||||
from calibre import relpath
|
||||
from calibre.constants import __appname__, __version__
|
||||
from calibre.ebooks.metadata.toc import TOC
|
||||
from calibre.ebooks.metadata import MetaInformation, get_parser
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
|
||||
|
||||
class Resource(object):
|
||||
@ -960,54 +960,3 @@ def suite():
|
||||
def test():
|
||||
unittest.TextTestRunner(verbosity=2).run(suite())
|
||||
|
||||
def option_parser():
|
||||
parser = get_parser('opf')
|
||||
parser.add_option('--language', default=None, help=_('Set the dc:language field'))
|
||||
return parser
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
if len(args) != 2:
|
||||
parser.print_help()
|
||||
return 1
|
||||
opfpath = os.path.abspath(args[1])
|
||||
basedir = os.path.dirname(opfpath)
|
||||
mi = MetaInformation(OPF(open(opfpath, 'rb'), basedir))
|
||||
write = False
|
||||
if opts.title is not None:
|
||||
mi.title = opts.title
|
||||
write = True
|
||||
if opts.authors is not None:
|
||||
aus = [i.strip() for i in opts.authors.split(',')]
|
||||
mi.authors = aus
|
||||
write = True
|
||||
if opts.category is not None:
|
||||
mi.category = opts.category
|
||||
write = True
|
||||
if opts.comment is not None:
|
||||
mi.comments = opts.comment
|
||||
write = True
|
||||
if opts.language is not None:
|
||||
mi.language = opts.language
|
||||
write = True
|
||||
if write:
|
||||
mo = OPFCreator(basedir, mi)
|
||||
ncx = cStringIO.StringIO()
|
||||
mo.render(open(args[1], 'wb'), ncx)
|
||||
ncx = ncx.getvalue()
|
||||
if ncx:
|
||||
f = glob.glob(os.path.join(os.path.dirname(args[1]), '*.ncx'))
|
||||
if f:
|
||||
f = open(f[0], 'wb')
|
||||
else:
|
||||
f = open(os.path.splitext(args[1])[0]+'.ncx', 'wb')
|
||||
f.write(ncx)
|
||||
f.close()
|
||||
print MetaInformation(OPF(open(opfpath, 'rb'), basedir))
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
@ -2,9 +2,9 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
'''Read meta information from PDF files'''
|
||||
|
||||
import sys, os, re
|
||||
import sys, re
|
||||
|
||||
from calibre.ebooks.metadata import MetaInformation, authors_to_string, get_parser
|
||||
from calibre.ebooks.metadata import MetaInformation, authors_to_string
|
||||
from pyPdf import PdfFileReader
|
||||
|
||||
def get_metadata(stream):
|
||||
@ -45,27 +45,3 @@ def set_metadata(stream, mi):
|
||||
stream.write(raw)
|
||||
stream.seek(0)
|
||||
|
||||
def option_parser():
|
||||
p = get_parser('pdf')
|
||||
p.remove_option('--category')
|
||||
p.remove_option('--comment')
|
||||
return p
|
||||
|
||||
def main(args=sys.argv):
|
||||
#p = option_parser()
|
||||
#opts, args = p.parse_args(args)
|
||||
if len(args) != 2:
|
||||
print >>sys.stderr, _('Usage: pdf-meta file.pdf')
|
||||
print >>sys.stderr, _('No filename specified.')
|
||||
return 1
|
||||
|
||||
stream = open(os.path.abspath(os.path.expanduser(args[1])), 'r+b')
|
||||
#mi = MetaInformation(opts.title, opts.authors)
|
||||
#if mi.title or mi.authors:
|
||||
# set_metadata(stream, mi)
|
||||
print unicode(get_metadata(stream)).encode('utf-8')
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
36
src/calibre/ebooks/metadata/rar.py
Normal file
36
src/calibre/ebooks/metadata/rar.py
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Read metadata from RAR archives
|
||||
'''
|
||||
|
||||
import os
|
||||
from cStringIO import StringIO
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.libunrar import extract_member, names
|
||||
|
||||
def get_metadata(stream):
|
||||
path = getattr(stream, 'name', False)
|
||||
if not path:
|
||||
pt = PersistentTemporaryFile('_rar-meta.rar')
|
||||
pt.write(stream.read())
|
||||
pt.close()
|
||||
path = pt.name
|
||||
path = os.path.abspath(path)
|
||||
file_names = list(names(path))
|
||||
for f in file_names:
|
||||
stream_type = os.path.splitext(f)[1].lower()
|
||||
if stream_type:
|
||||
stream_type = stream_type[1:]
|
||||
if stream_type in ('lit', 'opf', 'prc', 'mobi', 'fb2', 'epub',
|
||||
'rb', 'imp', 'pdf', 'lrf'):
|
||||
data = extract_member(path, match=None, name=f)[1]
|
||||
stream = StringIO(data)
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
return get_metadata(stream, stream_type)
|
||||
raise ValueError('No ebook found in RAR archive')
|
||||
|
||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Ashish Kulkarni <kulkarni.ashish@gmail.com>'
|
||||
'''Read meta information from RB files'''
|
||||
|
||||
import sys, os, struct
|
||||
import sys, struct
|
||||
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
|
||||
@ -53,16 +53,4 @@ def get_metadata(stream):
|
||||
raise
|
||||
return mi
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
if len(args) != 2:
|
||||
print >>sys.stderr, _('Usage: rb-meta file.rb')
|
||||
print >>sys.stderr, _('No filename specified.')
|
||||
return 1
|
||||
|
||||
path = os.path.abspath(os.path.expanduser(args[1]))
|
||||
print get_metadata(open(path, 'rb'))
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
|
@ -5,7 +5,7 @@ Edit metadata in RTF files.
|
||||
"""
|
||||
import re, cStringIO, sys
|
||||
|
||||
from calibre.ebooks.metadata import MetaInformation, get_parser
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
|
||||
title_pat = re.compile(r'\{\\info.*?\{\\title(.*?)(?<!\\)\}', re.DOTALL)
|
||||
author_pat = re.compile(r'\{\\info.*?\{\\author(.*?)(?<!\\)\}', re.DOTALL)
|
||||
@ -166,22 +166,3 @@ def set_metadata(stream, options):
|
||||
stream.write(src)
|
||||
stream.write(after)
|
||||
|
||||
def option_parser():
|
||||
return get_parser('rtf')
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
options, args = parser.parse_args(args)
|
||||
if len(args) != 2:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
stream = open(args[1], 'r+b')
|
||||
if options.authors:
|
||||
options.authors = options.authors.split(',')
|
||||
options.comments = options.comment
|
||||
set_metadata(stream, options)
|
||||
mi = get_metadata(stream)
|
||||
return mi
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
44
src/calibre/ebooks/mobi/from_comic.py
Normal file
44
src/calibre/ebooks/mobi/from_comic.py
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
'''
|
||||
import sys, os
|
||||
from calibre.ebooks.lrf.comic.convert_from import do_convert, option_parser, \
|
||||
ProgressBar, terminal_controller
|
||||
from calibre.ebooks.mobi.from_any import config, any2mobi
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
|
||||
|
||||
def convert(path_to_file, opts, notification=lambda m, p: p):
|
||||
pt = PersistentTemporaryFile('_comic2mobi.epub')
|
||||
pt.close()
|
||||
orig_output = opts.output
|
||||
opts.output = pt.name
|
||||
do_convert(path_to_file, opts, notification=notification, output_format='epub')
|
||||
opts = config('').parse()
|
||||
if orig_output is None:
|
||||
orig_output = os.path.splitext(path_to_file)[0]+'.mobi'
|
||||
opts.output = orig_output
|
||||
any2mobi(opts, pt.name)
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
if len(args) < 2:
|
||||
parser.print_help()
|
||||
print '\nYou must specify a file to convert'
|
||||
return 1
|
||||
|
||||
pb = ProgressBar(terminal_controller, _('Rendering comic pages...'),
|
||||
no_progress_bar=opts.no_progress_bar or getattr(opts, 'no_process', False))
|
||||
notification = pb.update
|
||||
|
||||
source = os.path.abspath(args[1])
|
||||
convert(source, opts, notification=notification)
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -27,7 +27,7 @@ TABLE_TAGS = set(['table', 'tr', 'td', 'th'])
|
||||
SPECIAL_TAGS = set(['hr', 'br'])
|
||||
CONTENT_TAGS = set(['img', 'hr', 'br'])
|
||||
|
||||
PAGE_BREAKS = set(['always', 'odd', 'even'])
|
||||
PAGE_BREAKS = set(['always', 'left', 'right'])
|
||||
|
||||
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
|
||||
|
||||
@ -79,6 +79,9 @@ class FormatState(object):
|
||||
|
||||
|
||||
class MobiMLizer(object):
|
||||
def __init__(self, ignore_tables=False):
|
||||
self.ignore_tables = ignore_tables
|
||||
|
||||
def transform(self, oeb, context):
|
||||
oeb.logger.info('Converting XHTML to Mobipocket markup...')
|
||||
self.oeb = oeb
|
||||
@ -341,6 +344,8 @@ class MobiMLizer(object):
|
||||
tag = 'tr'
|
||||
elif display == 'table-cell':
|
||||
tag = 'td'
|
||||
if tag in TABLE_TAGS and self.ignore_tables:
|
||||
tag = 'span' if tag == 'td' else 'div'
|
||||
if tag in TABLE_TAGS:
|
||||
for attr in ('rowspan', 'colspan'):
|
||||
if attr in elem.attrib:
|
||||
|
@ -87,6 +87,49 @@ def decint(value, direction):
|
||||
bytes[-1] |= 0x80
|
||||
return ''.join(chr(b) for b in reversed(bytes))
|
||||
|
||||
def rescale_image(data, maxsizeb, dimen=None):
|
||||
image = Image.open(StringIO(data))
|
||||
format = image.format
|
||||
changed = False
|
||||
if image.format not in ('JPEG', 'GIF'):
|
||||
width, height = image.size
|
||||
area = width * height
|
||||
if area <= 40000:
|
||||
format = 'GIF'
|
||||
else:
|
||||
image = image.convert('RGBA')
|
||||
format = 'JPEG'
|
||||
changed = True
|
||||
if dimen is not None:
|
||||
image.thumbnail(dimen, Image.ANTIALIAS)
|
||||
changed = True
|
||||
if changed:
|
||||
data = StringIO()
|
||||
image.save(data, format)
|
||||
data = data.getvalue()
|
||||
if len(data) <= maxsizeb:
|
||||
return data
|
||||
image = image.convert('RGBA')
|
||||
for quality in xrange(95, -1, -1):
|
||||
data = StringIO()
|
||||
image.save(data, 'JPEG', quality=quality)
|
||||
data = data.getvalue()
|
||||
if len(data) <= maxsizeb:
|
||||
return data
|
||||
width, height = image.size
|
||||
for scale in xrange(99, 0, -1):
|
||||
scale = scale / 100.
|
||||
data = StringIO()
|
||||
scaled = image.copy()
|
||||
size = (int(width * scale), (height * scale))
|
||||
scaled.thumbnail(size, Image.ANTIALIAS)
|
||||
scaled.save(data, 'JPEG', quality=0)
|
||||
data = data.getvalue()
|
||||
if len(data) <= maxsizeb:
|
||||
return data
|
||||
# Well, we tried?
|
||||
return data
|
||||
|
||||
|
||||
class Serializer(object):
|
||||
NSRMAP = {'': None, XML_NS: 'xml', XHTML_NS: '', MBP_NS: 'mbp'}
|
||||
@ -355,50 +398,7 @@ class MobiWriter(object):
|
||||
offset += RECORD_SIZE
|
||||
data, overlap = self._read_text_record(text)
|
||||
self._text_nrecords = nrecords
|
||||
|
||||
def _rescale_image(self, data, maxsizeb, dimen=None):
|
||||
image = Image.open(StringIO(data))
|
||||
format = image.format
|
||||
changed = False
|
||||
if image.format not in ('JPEG', 'GIF'):
|
||||
width, height = image.size
|
||||
area = width * height
|
||||
if area <= 40000:
|
||||
format = 'GIF'
|
||||
else:
|
||||
image = image.convert('RGBA')
|
||||
format = 'JPEG'
|
||||
changed = True
|
||||
if dimen is not None:
|
||||
image.thumbnail(dimen, Image.ANTIALIAS)
|
||||
changed = True
|
||||
if changed:
|
||||
data = StringIO()
|
||||
image.save(data, format)
|
||||
data = data.getvalue()
|
||||
if len(data) <= maxsizeb:
|
||||
return data
|
||||
image = image.convert('RGBA')
|
||||
for quality in xrange(95, -1, -1):
|
||||
data = StringIO()
|
||||
image.save(data, 'JPEG', quality=quality)
|
||||
data = data.getvalue()
|
||||
if len(data) <= maxsizeb:
|
||||
return data
|
||||
width, height = image.size
|
||||
for scale in xrange(99, 0, -1):
|
||||
scale = scale / 100.
|
||||
data = StringIO()
|
||||
scaled = image.copy()
|
||||
size = (int(width * scale), (height * scale))
|
||||
scaled.thumbnail(size, Image.ANTIALIAS)
|
||||
scaled.save(data, 'JPEG', quality=0)
|
||||
data = data.getvalue()
|
||||
if len(data) <= maxsizeb:
|
||||
return data
|
||||
# Well, we tried?
|
||||
return data
|
||||
|
||||
|
||||
def _generate_images(self):
|
||||
self._oeb.logger.info('Serializing images...')
|
||||
images = [(index, href) for href, index in self._images.items()]
|
||||
@ -407,7 +407,7 @@ class MobiWriter(object):
|
||||
coverid = metadata.cover[0] if metadata.cover else None
|
||||
for _, href in images:
|
||||
item = self._oeb.manifest.hrefs[href]
|
||||
data = self._rescale_image(item.data, self._imagemax)
|
||||
data = rescale_image(item.data, self._imagemax)
|
||||
self._records.append(data)
|
||||
|
||||
def _generate_record0(self):
|
||||
@ -452,6 +452,13 @@ class MobiWriter(object):
|
||||
code = EXTH_CODES[term]
|
||||
for item in oeb.metadata[term]:
|
||||
data = self.COLLAPSE_RE.sub(' ', unicode(item))
|
||||
if term == 'identifier':
|
||||
if data.lower().startswith('urn:isbn:'):
|
||||
data = data[9:]
|
||||
elif item.get('scheme', '').lower() == 'isbn':
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
data = data.encode('utf-8')
|
||||
exth.write(pack('>II', code, len(data) + 8))
|
||||
exth.write(data)
|
||||
@ -468,12 +475,12 @@ class MobiWriter(object):
|
||||
nrecs += 3
|
||||
exth = exth.getvalue()
|
||||
trail = len(exth) % 4
|
||||
pad = '' if not trail else '\0' * (4 - trail)
|
||||
pad = '\0' * (4 - trail) # Always pad w/ at least 1 byte
|
||||
exth = ['EXTH', pack('>II', len(exth) + 12, nrecs), exth, pad]
|
||||
return ''.join(exth)
|
||||
|
||||
def _add_thumbnail(self, item):
|
||||
data = self._rescale_image(item.data, MAX_THUMB_SIZE, MAX_THUMB_DIMEN)
|
||||
data = rescale_image(item.data, MAX_THUMB_SIZE, MAX_THUMB_DIMEN)
|
||||
manifest = self._oeb.manifest
|
||||
id, href = manifest.generate('thumbnail', 'thumbnail.jpeg')
|
||||
manifest.add(id, href, 'image/jpeg', data=data)
|
||||
@ -517,6 +524,10 @@ def config(defaults=None):
|
||||
help=_('Modify images to meet Palm device size limitations.'))
|
||||
mobi('toc_title', ['--toc-title'], default=None,
|
||||
help=_('Title for any generated in-line table of contents.'))
|
||||
mobi('ignore_tables', ['--ignore-tables'], default=False,
|
||||
help=_('Render HTML tables as blocks of text instead of actual '
|
||||
'tables. This is neccessary if the HTML contains very large '
|
||||
'or complex tables.'))
|
||||
profiles = c.add_group('profiles', _('Device renderer profiles. '
|
||||
'Affects conversion of font sizes, image rescaling and rasterization '
|
||||
'of tables. Valid profiles are: %s.') % ', '.join(_profiles))
|
||||
@ -574,7 +585,7 @@ def oeb2mobi(opts, inpath):
|
||||
rasterizer.transform(oeb, context)
|
||||
trimmer = ManifestTrimmer()
|
||||
trimmer.transform(oeb, context)
|
||||
mobimlizer = MobiMLizer()
|
||||
mobimlizer = MobiMLizer(ignore_tables=opts.ignore_tables)
|
||||
mobimlizer.transform(oeb, context)
|
||||
writer = MobiWriter(compression=compression, imagemax=imagemax)
|
||||
writer.dump(oeb, outpath)
|
||||
|
@ -10,7 +10,7 @@ import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from types import StringTypes
|
||||
from itertools import izip, count
|
||||
from itertools import izip, count, chain
|
||||
from urlparse import urldefrag, urlparse, urlunparse
|
||||
from urllib import unquote as urlunquote
|
||||
import logging
|
||||
@ -21,15 +21,18 @@ from lxml import etree
|
||||
from lxml import html
|
||||
from calibre import LoggingInterface
|
||||
from calibre.translations.dynamic import translate
|
||||
from calibre.startup import get_lang
|
||||
from calibre.ebooks.oeb.entitydefs import ENTITYDEFS
|
||||
|
||||
XML_PARSER = etree.XMLParser(recover=True)
|
||||
XML_NS = 'http://www.w3.org/XML/1998/namespace'
|
||||
XHTML_NS = 'http://www.w3.org/1999/xhtml'
|
||||
OEB_DOC_NS = 'http://openebook.org/namespaces/oeb-document/1.0/'
|
||||
OPF1_NS = 'http://openebook.org/namespaces/oeb-package/1.0/'
|
||||
OPF2_NS = 'http://www.idpf.org/2007/opf'
|
||||
DC09_NS = 'http://purl.org/metadata/dublin_core'
|
||||
DC10_NS = 'http://purl.org/dc/elements/1.0/'
|
||||
DC11_NS = 'http://purl.org/dc/elements/1.1/'
|
||||
DC_NSES = set([DC09_NS, DC10_NS, DC11_NS])
|
||||
XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance'
|
||||
DCTERMS_NS = 'http://purl.org/dc/terms/'
|
||||
NCX_NS = 'http://www.daisy.org/z3986/2005/ncx/'
|
||||
@ -39,6 +42,7 @@ XPNSMAP = {'h': XHTML_NS, 'o1': OPF1_NS, 'o2': OPF2_NS,
|
||||
'd09': DC09_NS, 'd10': DC10_NS, 'd11': DC11_NS,
|
||||
'xsi': XSI_NS, 'dt': DCTERMS_NS, 'ncx': NCX_NS,
|
||||
'svg': SVG_NS, 'xl': XLINK_NS}
|
||||
DC_PREFIXES = ('d11', 'd10', 'd09')
|
||||
|
||||
def XML(name): return '{%s}%s' % (XML_NS, name)
|
||||
def XHTML(name): return '{%s}%s' % (XHTML_NS, name)
|
||||
@ -60,6 +64,7 @@ GIF_MIME = 'image/gif'
|
||||
JPEG_MIME = 'image/jpeg'
|
||||
PNG_MIME = 'image/png'
|
||||
SVG_MIME = 'image/svg+xml'
|
||||
BINARY_MIME = 'application/octet-stream'
|
||||
|
||||
OEB_STYLES = set([CSS_MIME, OEB_CSS_MIME, 'text/x-oeb-css'])
|
||||
OEB_DOCS = set([XHTML_MIME, 'text/html', OEB_DOC_MIME, 'text/x-oeb-document'])
|
||||
@ -68,6 +73,8 @@ OEB_IMAGES = set([GIF_MIME, JPEG_MIME, PNG_MIME, SVG_MIME])
|
||||
|
||||
MS_COVER_TYPE = 'other.ms-coverimage-standard'
|
||||
|
||||
ENTITY_RE = re.compile(r'&([a-zA-Z_:][a-zA-Z0-9.-_:]+);')
|
||||
COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+')
|
||||
|
||||
def element(parent, *args, **kwargs):
|
||||
if parent is not None:
|
||||
@ -138,8 +145,7 @@ class Logger(LoggingInterface, object):
|
||||
class AbstractContainer(object):
|
||||
def read_xml(self, path):
|
||||
return etree.fromstring(
|
||||
self.read(path), parser=XML_PARSER,
|
||||
base_url=os.path.dirname(path))
|
||||
self.read(path), base_url=os.path.dirname(path))
|
||||
|
||||
class DirContainer(AbstractContainer):
|
||||
def __init__(self, rootdir):
|
||||
@ -191,18 +197,19 @@ class Metadata(object):
|
||||
def __init__(self, term, value, fq_attrib={}, **kwargs):
|
||||
self.fq_attrib = fq_attrib = dict(fq_attrib)
|
||||
fq_attrib.update(kwargs)
|
||||
if term == OPF('meta') and not value:
|
||||
term = self.fq_attrib.pop('name')
|
||||
value = self.fq_attrib.pop('content')
|
||||
elif term in Metadata.TERMS and not namespace(term):
|
||||
term = DC(term)
|
||||
if barename(term).lower() in Metadata.TERMS and \
|
||||
(not namespace(term) or namespace(term) in DC_NSES):
|
||||
# Anything looking like Dublin Core is coerced
|
||||
term = DC(barename(term).lower())
|
||||
elif namespace(term) == OPF2_NS:
|
||||
term = barename(term)
|
||||
self.term = term
|
||||
self.value = value
|
||||
self.attrib = attrib = {}
|
||||
for fq_attr in fq_attrib:
|
||||
if fq_attr in Metadata.ATTRS:
|
||||
attr = fq_attr
|
||||
fq_attr = OPF2(fq_attr)
|
||||
fq_attr = OPF(fq_attr)
|
||||
fq_attrib[fq_attr] = fq_attrib.pop(attr)
|
||||
else:
|
||||
attr = barename(fq_attr)
|
||||
@ -216,7 +223,16 @@ class Metadata(object):
|
||||
raise AttributeError(
|
||||
'%r object has no attribute %r' \
|
||||
% (self.__class__.__name__, name))
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.attrib[key]
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.attrib
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self.attrib.get(key, default)
|
||||
|
||||
def __repr__(self):
|
||||
return 'Item(term=%r, value=%r, attrib=%r)' \
|
||||
% (barename(self.term), self.value, self.attrib)
|
||||
@ -316,20 +332,74 @@ class Manifest(object):
|
||||
% (self.id, self.href, self.media_type)
|
||||
|
||||
def _force_xhtml(self, data):
|
||||
# Possibly decode in user-specified encoding
|
||||
if self.oeb.encoding is not None:
|
||||
data = data.decode(self.oeb.encoding, 'replace')
|
||||
# Handle broken XHTML w/ SVG (ugh)
|
||||
if 'svg:' in data and SVG_NS not in data:
|
||||
data = data.replace(
|
||||
'<html', '<html xmlns:svg="%s"' % SVG_NS, 1)
|
||||
if 'xlink:' in data and XLINK_NS not in data:
|
||||
data = data.replace(
|
||||
'<html', '<html xmlns:xlink="%s"' % XLINK_NS, 1)
|
||||
# Try with more & more drastic measures to parse
|
||||
try:
|
||||
data = etree.fromstring(data, parser=XML_PARSER)
|
||||
data = etree.fromstring(data)
|
||||
except etree.XMLSyntaxError:
|
||||
data = html.fromstring(data)
|
||||
data = etree.tostring(data, encoding=unicode)
|
||||
data = etree.fromstring(data, parser=XML_PARSER)
|
||||
if namespace(data.tag) != XHTML_NS:
|
||||
repl = lambda m: ENTITYDEFS.get(m.group(1), m.group(0))
|
||||
data = ENTITY_RE.sub(repl, data)
|
||||
try:
|
||||
data = etree.fromstring(data)
|
||||
except etree.XMLSyntaxError:
|
||||
self.oeb.logger.warn('Parsing file %r as HTML' % self.href)
|
||||
data = html.fromstring(data)
|
||||
data.attrib.pop('xmlns', None)
|
||||
data = etree.tostring(data, encoding=unicode)
|
||||
data = etree.fromstring(data)
|
||||
# Force into the XHTML namespace
|
||||
if barename(data.tag) != 'html':
|
||||
raise OEBError(
|
||||
'File %r does not appear to be (X)HTML' % self.href)
|
||||
elif not namespace(data.tag):
|
||||
data.attrib['xmlns'] = XHTML_NS
|
||||
data = etree.tostring(data, encoding=unicode)
|
||||
data = etree.fromstring(data, parser=XML_PARSER)
|
||||
data = etree.fromstring(data)
|
||||
elif namespace(data.tag) != XHTML_NS:
|
||||
# OEB_DOC_NS, but possibly others
|
||||
ns = namespace(data.tag)
|
||||
attrib = dict(data.attrib)
|
||||
nroot = etree.Element(XHTML('html'),
|
||||
nsmap={None: XHTML_NS}, attrib=attrib)
|
||||
for elem in data.iterdescendants():
|
||||
if isinstance(elem.tag, basestring) and \
|
||||
namespace(elem.tag) == ns:
|
||||
elem.tag = XHTML(barename(elem.tag))
|
||||
for elem in data:
|
||||
nroot.append(elem)
|
||||
data = nroot
|
||||
# Remove any encoding-specifying <meta/> elements
|
||||
for meta in self.META_XP(data):
|
||||
meta.getparent().remove(meta)
|
||||
# Ensure has a <head/>
|
||||
head = xpath(data, '/h:html/h:head')
|
||||
head = head[0] if head else None
|
||||
if head is None:
|
||||
self.oeb.logger.warn(
|
||||
'File %r missing <head/> element' % self.href)
|
||||
head = etree.Element(XHTML('head'))
|
||||
data.insert(0, head)
|
||||
title = etree.SubElement(head, XHTML('title'))
|
||||
title.text = self.oeb.translate(__('Unknown'))
|
||||
elif not xpath(data, '/h:html/h:head/h:title'):
|
||||
self.oeb.logger.warn(
|
||||
'File %r missing <title/> element' % self.href)
|
||||
title = etree.SubElement(head, XHTML('title'))
|
||||
title.text = self.oeb.translate(__('Unknown'))
|
||||
# Ensure has a <body/>
|
||||
if not xpath(data, '/h:html/h:body'):
|
||||
self.oeb.logger.warn(
|
||||
'File %r missing <body/> element' % self.href)
|
||||
etree.SubElement(data, XHTML('body'))
|
||||
return data
|
||||
|
||||
def data():
|
||||
@ -340,7 +410,7 @@ class Manifest(object):
|
||||
if self.media_type in OEB_DOCS:
|
||||
data = self._force_xhtml(data)
|
||||
elif self.media_type[-4:] in ('+xml', '/xml'):
|
||||
data = etree.fromstring(data, parser=XML_PARSER)
|
||||
data = etree.fromstring(data)
|
||||
self._data = data
|
||||
return data
|
||||
def fset(self, value):
|
||||
@ -456,9 +526,9 @@ class Manifest(object):
|
||||
elem = element(parent, 'manifest')
|
||||
for item in self.ids.values():
|
||||
media_type = item.media_type
|
||||
if media_type == XHTML_MIME:
|
||||
if media_type in OEB_DOCS:
|
||||
media_type = OEB_DOC_MIME
|
||||
elif media_type == CSS_MIME:
|
||||
elif media_type in OEB_STYLES:
|
||||
media_type = OEB_CSS_MIME
|
||||
attrib = {'id': item.id, 'href': item.href,
|
||||
'media-type': media_type}
|
||||
@ -470,6 +540,11 @@ class Manifest(object):
|
||||
def to_opf2(self, parent=None):
|
||||
elem = element(parent, OPF('manifest'))
|
||||
for item in self.ids.values():
|
||||
media_type = item.media_type
|
||||
if media_type in OEB_DOCS:
|
||||
media_type = XHTML_MIME
|
||||
elif media_type in OEB_STYLES:
|
||||
media_type = CSS_MIME
|
||||
attrib = {'id': item.id, 'href': item.href,
|
||||
'media-type': item.media_type}
|
||||
if item.fallback:
|
||||
@ -733,25 +808,19 @@ class OEBBook(object):
|
||||
opf = self._read_opf(opfpath)
|
||||
self._all_from_opf(opf)
|
||||
|
||||
def _convert_opf1(self, opf):
|
||||
# Seriously, seriously wrong
|
||||
if namespace(opf.tag) == OPF1_NS:
|
||||
opf.tag = barename(opf.tag)
|
||||
for elem in opf.iterdescendants():
|
||||
if isinstance(elem.tag, basestring) \
|
||||
and namespace(elem.tag) == OPF1_NS:
|
||||
elem.tag = barename(elem.tag)
|
||||
def _clean_opf(self, opf):
|
||||
for elem in opf.iter():
|
||||
if isinstance(elem.tag, basestring) \
|
||||
and namespace(elem.tag) in ('', OPF1_NS):
|
||||
elem.tag = OPF(barename(elem.tag))
|
||||
attrib = dict(opf.attrib)
|
||||
attrib['version'] = '2.0'
|
||||
nroot = etree.Element(OPF('package'),
|
||||
nsmap={None: OPF2_NS}, attrib=attrib)
|
||||
metadata = etree.SubElement(nroot, OPF('metadata'),
|
||||
nsmap={'opf': OPF2_NS, 'dc': DC11_NS,
|
||||
'xsi': XSI_NS, 'dcterms': DCTERMS_NS})
|
||||
for prefix in ('d11', 'd10', 'd09'):
|
||||
elements = xpath(opf, 'metadata//%s:*' % prefix)
|
||||
if elements: break
|
||||
for element in elements:
|
||||
dc = lambda prefix: xpath(opf, 'o2:metadata//%s:*' % prefix)
|
||||
for element in chain(*(dc(prefix) for prefix in DC_PREFIXES)):
|
||||
if not element.text: continue
|
||||
tag = barename(element.tag).lower()
|
||||
element.tag = '{%s}%s' % (DC11_NS, tag)
|
||||
@ -761,28 +830,26 @@ class OEBBook(object):
|
||||
element.attrib[nsname] = element.attrib[name]
|
||||
del element.attrib[name]
|
||||
metadata.append(element)
|
||||
for element in opf.xpath('metadata//meta'):
|
||||
for element in xpath(opf, 'o2:metadata//o2:meta'):
|
||||
metadata.append(element)
|
||||
for item in opf.xpath('manifest/item'):
|
||||
media_type = item.attrib['media-type'].lower()
|
||||
if media_type in OEB_DOCS:
|
||||
media_type = XHTML_MIME
|
||||
elif media_type in OEB_STYLES:
|
||||
media_type = CSS_MIME
|
||||
item.attrib['media-type'] = media_type
|
||||
for tag in ('manifest', 'spine', 'tours', 'guide'):
|
||||
for element in opf.xpath(tag):
|
||||
for tag in ('o2:manifest', 'o2:spine', 'o2:tours', 'o2:guide'):
|
||||
for element in xpath(opf, tag):
|
||||
nroot.append(element)
|
||||
return etree.fromstring(etree.tostring(nroot), parser=XML_PARSER)
|
||||
return nroot
|
||||
|
||||
def _read_opf(self, opfpath):
|
||||
opf = self.container.read_xml(opfpath)
|
||||
version = float(opf.get('version', 1.0))
|
||||
opf = self.container.read(opfpath)
|
||||
try:
|
||||
opf = etree.fromstring(opf)
|
||||
except etree.XMLSyntaxError:
|
||||
repl = lambda m: ENTITYDEFS.get(m.group(1), m.group(0))
|
||||
opf = ENTITY_RE.sub(repl, opf)
|
||||
opf = etree.fromstring(opf)
|
||||
self.logger.warn('OPF contains invalid HTML named entities')
|
||||
ns = namespace(opf.tag)
|
||||
if ns not in ('', OPF1_NS, OPF2_NS):
|
||||
raise OEBError('Invalid namespace %r for OPF document' % ns)
|
||||
if ns != OPF2_NS or version < 2.0:
|
||||
opf = self._convert_opf1(opf)
|
||||
opf = self._clean_opf(opf)
|
||||
return opf
|
||||
|
||||
def _metadata_from_opf(self, opf):
|
||||
@ -791,8 +858,16 @@ class OEBBook(object):
|
||||
self.metadata = metadata = Metadata(self)
|
||||
ignored = (OPF('dc-metadata'), OPF('x-metadata'))
|
||||
for elem in xpath(opf, '/o2:package/o2:metadata//*'):
|
||||
if elem.tag not in ignored and (elem.text or elem.attrib):
|
||||
metadata.add(elem.tag, elem.text, elem.attrib)
|
||||
if elem.tag in ignored: continue
|
||||
term = elem.tag
|
||||
value = elem.text
|
||||
if term == OPF('meta'):
|
||||
term = elem.attrib.pop('name', None)
|
||||
value = elem.attrib.pop('content', None)
|
||||
if value:
|
||||
value = COLLAPSE_RE.sub(' ', value.strip())
|
||||
if term and (value or elem.attrib):
|
||||
metadata.add(term, value, elem.attrib)
|
||||
haveuuid = haveid = False
|
||||
for ident in metadata.identifier:
|
||||
if unicode(ident).startswith('urn:uuid:'):
|
||||
@ -807,36 +882,38 @@ class OEBBook(object):
|
||||
self.uid = item
|
||||
break
|
||||
else:
|
||||
self.logger.warn(u'Unique-identifier %r not found.' % uid)
|
||||
self.logger.warn(u'Unique-identifier %r not found' % uid)
|
||||
for ident in metadata.identifier:
|
||||
if 'id' in ident.attrib:
|
||||
self.uid = metadata.identifier[0]
|
||||
break
|
||||
if not metadata.language:
|
||||
self.logger.warn(u'Language not specified.')
|
||||
metadata.add('language', 'en')
|
||||
self.logger.warn(u'Language not specified')
|
||||
metadata.add('language', get_lang())
|
||||
if not metadata.creator:
|
||||
self.logger.warn(u'Creator not specified.')
|
||||
metadata.add('creator', 'Unknown')
|
||||
self.logger.warn('Creator not specified')
|
||||
metadata.add('creator', self.translate(__('Unknown')))
|
||||
if not metadata.title:
|
||||
self.logger.warn(u'Title not specified.')
|
||||
metadata.add('title', 'Unknown')
|
||||
self.logger.warn('Title not specified')
|
||||
metadata.add('title', self.translate(__('Unknown')))
|
||||
|
||||
def _manifest_from_opf(self, opf):
|
||||
self.manifest = manifest = Manifest(self)
|
||||
for elem in xpath(opf, '/o2:package/o2:manifest/o2:item'):
|
||||
id = elem.get('id')
|
||||
href = elem.get('href')
|
||||
media_type = elem.get('media-type')
|
||||
media_type = elem.get('media-type', None)
|
||||
if media_type is None:
|
||||
media_type = elem.get('mediatype', BINARY_MIME)
|
||||
fallback = elem.get('fallback')
|
||||
if href in manifest.hrefs:
|
||||
self.logger.warn(u'Duplicate manifest entry for %r.' % href)
|
||||
self.logger.warn(u'Duplicate manifest entry for %r' % href)
|
||||
continue
|
||||
if not self.container.exists(href):
|
||||
self.logger.warn(u'Manifest item %r not found.' % href)
|
||||
self.logger.warn(u'Manifest item %r not found' % href)
|
||||
continue
|
||||
if id in manifest.ids:
|
||||
self.logger.warn(u'Duplicate manifest id %r.' % id)
|
||||
self.logger.warn(u'Duplicate manifest id %r' % id)
|
||||
id, href = manifest.generate(id, href)
|
||||
manifest.add(id, href, media_type, fallback)
|
||||
|
||||
@ -845,7 +922,7 @@ class OEBBook(object):
|
||||
for elem in xpath(opf, '/o2:package/o2:spine/o2:itemref'):
|
||||
idref = elem.get('idref')
|
||||
if idref not in self.manifest:
|
||||
self.logger.warn(u'Spine item %r not found.' % idref)
|
||||
self.logger.warn(u'Spine item %r not found' % idref)
|
||||
continue
|
||||
item = self.manifest[idref]
|
||||
spine.add(item, elem.get('linear'))
|
||||
@ -857,6 +934,8 @@ class OEBBook(object):
|
||||
extras.sort()
|
||||
for item in extras:
|
||||
spine.add(item, False)
|
||||
if len(spine) == 0:
|
||||
raise OEBError("Spine is empty")
|
||||
|
||||
def _guide_from_opf(self, opf):
|
||||
self.guide = guide = Guide(self)
|
||||
@ -886,9 +965,13 @@ class OEBBook(object):
|
||||
if len(result) != 1:
|
||||
return False
|
||||
id = result[0]
|
||||
ncx = self.manifest[id].data
|
||||
self.manifest.remove(id)
|
||||
title = xpath(ncx, 'ncx:docTitle/ncx:text/text()')[0]
|
||||
if id not in self.manifest.ids:
|
||||
return False
|
||||
item = self.manifest.ids[id]
|
||||
ncx = item.data
|
||||
self.manifest.remove(item)
|
||||
title = xpath(ncx, 'ncx:docTitle/ncx:text/text()')
|
||||
title = title[0].strip() if title else unicode(self.metadata.title)
|
||||
self.toc = toc = TOC(title)
|
||||
navmaps = xpath(ncx, 'ncx:navMap')
|
||||
for navmap in navmaps:
|
||||
@ -945,7 +1028,8 @@ class OEBBook(object):
|
||||
if not item.linear: continue
|
||||
html = item.data
|
||||
title = xpath(html, '/h:html/h:head/h:title/text()')
|
||||
if title: titles.append(title[0])
|
||||
title = title[0].strip() if title else None
|
||||
if title: titles.append(title)
|
||||
headers.append('(unlabled)')
|
||||
for tag in ('h1', 'h2', 'h3', 'h4', 'h5', 'strong'):
|
||||
expr = '/h:html/h:body//h:%s[position()=1]/text()' % (tag,)
|
||||
@ -969,9 +1053,19 @@ class OEBBook(object):
|
||||
|
||||
def _ensure_cover_image(self):
|
||||
cover = None
|
||||
spine0 = self.spine[0]
|
||||
html = spine0.data
|
||||
if self.metadata.cover:
|
||||
hcover = self.spine[0]
|
||||
if 'cover' in self.guide:
|
||||
href = self.guide['cover'].href
|
||||
item = self.manifest.hrefs[href]
|
||||
media_type = item.media_type
|
||||
if media_type in OEB_RASTER_IMAGES:
|
||||
cover = item
|
||||
elif media_type in OEB_DOCS:
|
||||
hcover = item
|
||||
html = hcover.data
|
||||
if cover is not None:
|
||||
pass
|
||||
elif self.metadata.cover:
|
||||
id = str(self.metadata.cover[0])
|
||||
cover = self.manifest.ids[id]
|
||||
elif MS_COVER_TYPE in self.guide:
|
||||
@ -979,16 +1073,16 @@ class OEBBook(object):
|
||||
cover = self.manifest.hrefs[href]
|
||||
elif xpath(html, '//h:img[position()=1]'):
|
||||
img = xpath(html, '//h:img[position()=1]')[0]
|
||||
href = spine0.abshref(img.get('src'))
|
||||
href = hcover.abshref(img.get('src'))
|
||||
cover = self.manifest.hrefs[href]
|
||||
elif xpath(html, '//h:object[position()=1]'):
|
||||
object = xpath(html, '//h:object[position()=1]')[0]
|
||||
href = spine0.abshref(object.get('data'))
|
||||
href = hcover.abshref(object.get('data'))
|
||||
cover = self.manifest.hrefs[href]
|
||||
elif xpath(html, '//svg:svg[position()=1]'):
|
||||
svg = copy.deepcopy(xpath(html, '//svg:svg[position()=1]')[0])
|
||||
href = os.path.splitext(spine0.href)[0] + '.svg'
|
||||
id, href = self.manifest.generate(spine0.id, href)
|
||||
href = os.path.splitext(hcover.href)[0] + '.svg'
|
||||
id, href = self.manifest.generate(hcover.id, href)
|
||||
cover = self.manifest.add(id, href, SVG_MIME, data=svg)
|
||||
if cover and not self.metadata.cover:
|
||||
self.metadata.add('cover', cover.id)
|
||||
|
256
src/calibre/ebooks/oeb/entitydefs.py
Normal file
256
src/calibre/ebooks/oeb/entitydefs.py
Normal file
@ -0,0 +1,256 @@
|
||||
"""
|
||||
Replacement for htmlentitydefs which uses purely numeric entities.
|
||||
"""
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
||||
|
||||
ENTITYDEFS = \
|
||||
{'AElig': 'Æ',
|
||||
'Aacute': 'Á',
|
||||
'Acirc': 'Â',
|
||||
'Agrave': 'À',
|
||||
'Alpha': 'Α',
|
||||
'Aring': 'Å',
|
||||
'Atilde': 'Ã',
|
||||
'Auml': 'Ä',
|
||||
'Beta': 'Β',
|
||||
'Ccedil': 'Ç',
|
||||
'Chi': 'Χ',
|
||||
'Dagger': '‡',
|
||||
'Delta': 'Δ',
|
||||
'ETH': 'Ð',
|
||||
'Eacute': 'É',
|
||||
'Ecirc': 'Ê',
|
||||
'Egrave': 'È',
|
||||
'Epsilon': 'Ε',
|
||||
'Eta': 'Η',
|
||||
'Euml': 'Ë',
|
||||
'Gamma': 'Γ',
|
||||
'Iacute': 'Í',
|
||||
'Icirc': 'Î',
|
||||
'Igrave': 'Ì',
|
||||
'Iota': 'Ι',
|
||||
'Iuml': 'Ï',
|
||||
'Kappa': 'Κ',
|
||||
'Lambda': 'Λ',
|
||||
'Mu': 'Μ',
|
||||
'Ntilde': 'Ñ',
|
||||
'Nu': 'Ν',
|
||||
'OElig': 'Œ',
|
||||
'Oacute': 'Ó',
|
||||
'Ocirc': 'Ô',
|
||||
'Ograve': 'Ò',
|
||||
'Omega': 'Ω',
|
||||
'Omicron': 'Ο',
|
||||
'Oslash': 'Ø',
|
||||
'Otilde': 'Õ',
|
||||
'Ouml': 'Ö',
|
||||
'Phi': 'Φ',
|
||||
'Pi': 'Π',
|
||||
'Prime': '″',
|
||||
'Psi': 'Ψ',
|
||||
'Rho': 'Ρ',
|
||||
'Scaron': 'Š',
|
||||
'Sigma': 'Σ',
|
||||
'THORN': 'Þ',
|
||||
'Tau': 'Τ',
|
||||
'Theta': 'Θ',
|
||||
'Uacute': 'Ú',
|
||||
'Ucirc': 'Û',
|
||||
'Ugrave': 'Ù',
|
||||
'Upsilon': 'Υ',
|
||||
'Uuml': 'Ü',
|
||||
'Xi': 'Ξ',
|
||||
'Yacute': 'Ý',
|
||||
'Yuml': 'Ÿ',
|
||||
'Zeta': 'Ζ',
|
||||
'aacute': 'á',
|
||||
'acirc': 'â',
|
||||
'acute': '´',
|
||||
'aelig': 'æ',
|
||||
'agrave': 'à',
|
||||
'alefsym': 'ℵ',
|
||||
'alpha': 'α',
|
||||
'and': '∧',
|
||||
'ang': '∠',
|
||||
'aring': 'å',
|
||||
'asymp': '≈',
|
||||
'atilde': 'ã',
|
||||
'auml': 'ä',
|
||||
'bdquo': '„',
|
||||
'beta': 'β',
|
||||
'brvbar': '¦',
|
||||
'bull': '•',
|
||||
'cap': '∩',
|
||||
'ccedil': 'ç',
|
||||
'cedil': '¸',
|
||||
'cent': '¢',
|
||||
'chi': 'χ',
|
||||
'circ': 'ˆ',
|
||||
'clubs': '♣',
|
||||
'cong': '≅',
|
||||
'copy': '©',
|
||||
'crarr': '↵',
|
||||
'cup': '∪',
|
||||
'curren': '¤',
|
||||
'dArr': '⇓',
|
||||
'dagger': '†',
|
||||
'darr': '↓',
|
||||
'deg': '°',
|
||||
'delta': 'δ',
|
||||
'diams': '♦',
|
||||
'divide': '÷',
|
||||
'eacute': 'é',
|
||||
'ecirc': 'ê',
|
||||
'egrave': 'è',
|
||||
'empty': '∅',
|
||||
'emsp': ' ',
|
||||
'ensp': ' ',
|
||||
'epsilon': 'ε',
|
||||
'equiv': '≡',
|
||||
'eta': 'η',
|
||||
'eth': 'ð',
|
||||
'euml': 'ë',
|
||||
'euro': '€',
|
||||
'exist': '∃',
|
||||
'fnof': 'ƒ',
|
||||
'forall': '∀',
|
||||
'frac12': '½',
|
||||
'frac14': '¼',
|
||||
'frac34': '¾',
|
||||
'frasl': '⁄',
|
||||
'gamma': 'γ',
|
||||
'ge': '≥',
|
||||
'hArr': '⇔',
|
||||
'harr': '↔',
|
||||
'hearts': '♥',
|
||||
'hellip': '…',
|
||||
'iacute': 'í',
|
||||
'icirc': 'î',
|
||||
'iexcl': '¡',
|
||||
'igrave': 'ì',
|
||||
'image': 'ℑ',
|
||||
'infin': '∞',
|
||||
'int': '∫',
|
||||
'iota': 'ι',
|
||||
'iquest': '¿',
|
||||
'isin': '∈',
|
||||
'iuml': 'ï',
|
||||
'kappa': 'κ',
|
||||
'lArr': '⇐',
|
||||
'lambda': 'λ',
|
||||
'lang': '〈',
|
||||
'laquo': '«',
|
||||
'larr': '←',
|
||||
'lceil': '⌈',
|
||||
'ldquo': '“',
|
||||
'le': '≤',
|
||||
'lfloor': '⌊',
|
||||
'lowast': '∗',
|
||||
'loz': '◊',
|
||||
'lrm': '‎',
|
||||
'lsaquo': '‹',
|
||||
'lsquo': '‘',
|
||||
'macr': '¯',
|
||||
'mdash': '—',
|
||||
'micro': 'µ',
|
||||
'middot': '·',
|
||||
'minus': '−',
|
||||
'mu': 'μ',
|
||||
'nabla': '∇',
|
||||
'nbsp': ' ',
|
||||
'ndash': '–',
|
||||
'ne': '≠',
|
||||
'ni': '∋',
|
||||
'not': '¬',
|
||||
'notin': '∉',
|
||||
'nsub': '⊄',
|
||||
'ntilde': 'ñ',
|
||||
'nu': 'ν',
|
||||
'oacute': 'ó',
|
||||
'ocirc': 'ô',
|
||||
'oelig': 'œ',
|
||||
'ograve': 'ò',
|
||||
'oline': '‾',
|
||||
'omega': 'ω',
|
||||
'omicron': 'ο',
|
||||
'oplus': '⊕',
|
||||
'or': '∨',
|
||||
'ordf': 'ª',
|
||||
'ordm': 'º',
|
||||
'oslash': 'ø',
|
||||
'otilde': 'õ',
|
||||
'otimes': '⊗',
|
||||
'ouml': 'ö',
|
||||
'para': '¶',
|
||||
'part': '∂',
|
||||
'permil': '‰',
|
||||
'perp': '⊥',
|
||||
'phi': 'φ',
|
||||
'pi': 'π',
|
||||
'piv': 'ϖ',
|
||||
'plusmn': '±',
|
||||
'pound': '£',
|
||||
'prime': '′',
|
||||
'prod': '∏',
|
||||
'prop': '∝',
|
||||
'psi': 'ψ',
|
||||
'rArr': '⇒',
|
||||
'radic': '√',
|
||||
'rang': '〉',
|
||||
'raquo': '»',
|
||||
'rarr': '→',
|
||||
'rceil': '⌉',
|
||||
'rdquo': '”',
|
||||
'real': 'ℜ',
|
||||
'reg': '®',
|
||||
'rfloor': '⌋',
|
||||
'rho': 'ρ',
|
||||
'rlm': '‏',
|
||||
'rsaquo': '›',
|
||||
'rsquo': '’',
|
||||
'sbquo': '‚',
|
||||
'scaron': 'š',
|
||||
'sdot': '⋅',
|
||||
'sect': '§',
|
||||
'shy': '­',
|
||||
'sigma': 'σ',
|
||||
'sigmaf': 'ς',
|
||||
'sim': '∼',
|
||||
'spades': '♠',
|
||||
'sub': '⊂',
|
||||
'sube': '⊆',
|
||||
'sum': '∑',
|
||||
'sup': '⊃',
|
||||
'sup1': '¹',
|
||||
'sup2': '²',
|
||||
'sup3': '³',
|
||||
'supe': '⊇',
|
||||
'szlig': 'ß',
|
||||
'tau': 'τ',
|
||||
'there4': '∴',
|
||||
'theta': 'θ',
|
||||
'thetasym': 'ϑ',
|
||||
'thinsp': ' ',
|
||||
'thorn': 'þ',
|
||||
'tilde': '˜',
|
||||
'times': '×',
|
||||
'trade': '™',
|
||||
'uArr': '⇑',
|
||||
'uacute': 'ú',
|
||||
'uarr': '↑',
|
||||
'ucirc': 'û',
|
||||
'ugrave': 'ù',
|
||||
'uml': '¨',
|
||||
'upsih': 'ϒ',
|
||||
'upsilon': 'υ',
|
||||
'uuml': 'ü',
|
||||
'weierp': '℘',
|
||||
'xi': 'ξ',
|
||||
'yacute': 'ý',
|
||||
'yen': '¥',
|
||||
'yuml': 'ÿ',
|
||||
'zeta': 'ζ',
|
||||
'zwj': '‍',
|
||||
'zwnj': '‌'}
|
@ -110,7 +110,8 @@ class Stylizer(object):
|
||||
|
||||
def __init__(self, tree, path, oeb, profile=PROFILES['PRS505']):
|
||||
self.profile = profile
|
||||
base = os.path.dirname(path)
|
||||
self.logger = oeb.logger
|
||||
item = oeb.manifest.hrefs[path]
|
||||
basename = os.path.basename(path)
|
||||
cssname = os.path.splitext(basename)[0] + '.css'
|
||||
stylesheets = [HTML_CSS_STYLESHEET]
|
||||
@ -128,8 +129,12 @@ class Stylizer(object):
|
||||
and elem.get('rel', 'stylesheet') == 'stylesheet' \
|
||||
and elem.get('type', CSS_MIME) in OEB_STYLES:
|
||||
href = urlnormalize(elem.attrib['href'])
|
||||
path = os.path.join(base, href)
|
||||
path = os.path.normpath(path).replace('\\', '/')
|
||||
path = item.abshref(href)
|
||||
if path not in oeb.manifest.hrefs:
|
||||
self.logger.warn(
|
||||
'Stylesheet %r referenced by file %r not in manifest' %
|
||||
(path, item.href))
|
||||
continue
|
||||
if path in self.STYLESHEETS:
|
||||
stylesheet = self.STYLESHEETS[path]
|
||||
else:
|
||||
@ -277,7 +282,9 @@ class Style(object):
|
||||
def _apply_style_attr(self):
|
||||
attrib = self._element.attrib
|
||||
if 'style' in attrib:
|
||||
style = CSSStyleDeclaration(attrib['style'])
|
||||
css = attrib['style'].split(';')
|
||||
css = filter(None, map(lambda x: x.strip(), css))
|
||||
style = CSSStyleDeclaration('; '.join(css))
|
||||
self._style.update(self._stylizer.flatten_style(style))
|
||||
|
||||
def _has_parent(self):
|
||||
|
@ -207,7 +207,8 @@ class CSSFlattener(object):
|
||||
items = cssdict.items()
|
||||
items.sort()
|
||||
css = u';\n'.join(u'%s: %s' % (key, val) for key, val in items)
|
||||
klass = STRIPNUM.sub('', node.get('class', 'calibre').split()[0])
|
||||
classes = node.get('class', None) or 'calibre'
|
||||
klass = STRIPNUM.sub('', classes.split()[0])
|
||||
if css in styles:
|
||||
match = styles[css]
|
||||
else:
|
||||
|
@ -46,9 +46,10 @@ class SVGRasterizer(object):
|
||||
data = QByteArray(xml2str(elem))
|
||||
svg = QSvgRenderer(data)
|
||||
size = svg.defaultSize()
|
||||
view_box = elem.get('viewBox', elem.get('viewbox', None))
|
||||
if size.width() == 100 and size.height() == 100 \
|
||||
and 'viewBox' in elem.attrib:
|
||||
box = [float(x) for x in elem.attrib['viewBox'].split()]
|
||||
and view_box is not None:
|
||||
box = [float(x) for x in view_box.split()]
|
||||
size.setWidth(box[2] - box[0])
|
||||
size.setHeight(box[3] - box[1])
|
||||
if width or height:
|
||||
|
@ -13,6 +13,7 @@ from urlparse import urldefrag
|
||||
from lxml import etree
|
||||
import cssutils
|
||||
from calibre.ebooks.oeb.base import XPNSMAP, CSS_MIME, OEB_DOCS
|
||||
from calibre.ebooks.oeb.base import urlnormalize
|
||||
|
||||
LINK_SELECTORS = []
|
||||
for expr in ('//h:link/@href', '//h:img/@src', '//h:object/@data',
|
||||
@ -46,7 +47,7 @@ class ManifestTrimmer(object):
|
||||
item.data is not None:
|
||||
hrefs = [sel(item.data) for sel in LINK_SELECTORS]
|
||||
for href in chain(*hrefs):
|
||||
href = item.abshref(href)
|
||||
href = item.abshref(urlnormalize(href))
|
||||
if href in oeb.manifest.hrefs:
|
||||
found = oeb.manifest.hrefs[href]
|
||||
if found not in used:
|
||||
|
@ -6,7 +6,7 @@ from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSiz
|
||||
QByteArray, QLocale, QUrl, QTranslator, QCoreApplication, \
|
||||
QModelIndex
|
||||
from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
|
||||
QIcon, QTableView, QDialogButtonBox, QApplication
|
||||
QIcon, QTableView, QDialogButtonBox, QApplication, QDialog
|
||||
|
||||
ORG_NAME = 'KovidsBrain'
|
||||
APP_UID = 'libprs500'
|
||||
@ -60,6 +60,8 @@ def _config():
|
||||
help=_('Delete books from library after uploading to device'))
|
||||
c.add_opt('separate_cover_flow', default=False,
|
||||
help=_('Show the cover flow in a separate window instead of in the main calibre window'))
|
||||
c.add_opt('disable_tray_notification', default=False,
|
||||
help=_('Disable notifications from the system tray icon'))
|
||||
return ConfigProxy(c)
|
||||
|
||||
config = _config()
|
||||
@ -392,7 +394,20 @@ def pixmap_to_data(pixmap, format='JPEG'):
|
||||
pixmap.save(buf, format)
|
||||
return str(ba.data())
|
||||
|
||||
|
||||
class ResizableDialog(QDialog):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
QDialog.__init__(self, *args)
|
||||
self.setupUi(self)
|
||||
nh, nw = min_available_height()-25, available_width()-10
|
||||
if nh < 0:
|
||||
nh = 800
|
||||
if nw < 0:
|
||||
nw = 600
|
||||
nh = min(self.height(), nh)
|
||||
nw = min(self.width(), nw)
|
||||
self.resize(nw, nh)
|
||||
|
||||
try:
|
||||
from calibre.utils.single_qt_application import SingleApplication
|
||||
except:
|
||||
|
@ -186,6 +186,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
single_format = config['save_to_disk_single_format']
|
||||
self.single_format.setCurrentIndex(BOOK_EXTENSIONS.index(single_format))
|
||||
self.cover_browse.setValue(config['cover_flow_queue_length'])
|
||||
self.systray_notifications.setChecked(not config['disable_tray_notification'])
|
||||
from calibre.translations.compiled import translations
|
||||
from calibre.translations import language_codes
|
||||
from calibre.startup import get_lang
|
||||
@ -394,6 +395,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
config['toolbar_icon_size'] = self.ICON_SIZES[self.toolbar_button_size.currentIndex()]
|
||||
config['show_text_in_toolbar'] = bool(self.show_toolbar_text.isChecked())
|
||||
config['separate_cover_flow'] = bool(self.separate_cover_flow.isChecked())
|
||||
config['disable_tray_notification'] = not self.systray_notifications.isChecked()
|
||||
pattern = self.filename_pattern.commit()
|
||||
prefs['filename_pattern'] = pattern
|
||||
p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()]
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>570</height>
|
||||
<width>755</width>
|
||||
<height>557</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
@ -328,8 +328,8 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page" >
|
||||
<layout class="QGridLayout" name="gridLayout_3" >
|
||||
<item row="0" column="0" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" >
|
||||
<item>
|
||||
<widget class="QCheckBox" name="roman_numerals" >
|
||||
<property name="text" >
|
||||
<string>Use &Roman numerals for series number</string>
|
||||
@ -339,12 +339,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<item>
|
||||
<widget class="QCheckBox" name="systray_icon" >
|
||||
<property name="text" >
|
||||
<string>Enable system &tray icon (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="systray_notifications" >
|
||||
<property name="text" >
|
||||
<string>Show &notifications in system tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="separate_cover_flow" >
|
||||
<property name="text" >
|
||||
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="sync_news" >
|
||||
<property name="text" >
|
||||
<string>Automatically send downloaded &news to ebook reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="delete_news" >
|
||||
<property name="text" >
|
||||
<string>&Delete news from library when it is sent to reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6" >
|
||||
<property name="text" >
|
||||
<string>&Number of covers to show in browse mode (after restart):</string>
|
||||
<string>&Number of covers to show in browse mode (needs restart):</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>cover_browse</cstring>
|
||||
@ -356,7 +391,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="0" >
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2" >
|
||||
<property name="title" >
|
||||
<string>Toolbar</string>
|
||||
@ -402,118 +437,112 @@
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>toolbar_button_size</zorder>
|
||||
<zorder>label_4</zorder>
|
||||
<zorder>show_toolbar_text</zorder>
|
||||
<zorder>columns</zorder>
|
||||
<zorder></zorder>
|
||||
<zorder>groupBox_3</zorder>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" >
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Select visible &columns in library view</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7" >
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Select visible &columns in library view</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7" >
|
||||
<item>
|
||||
<widget class="QListWidget" name="columns" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionBehavior" >
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" >
|
||||
<item>
|
||||
<widget class="QToolButton" name="column_up" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
<widget class="QListWidget" name="columns" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||
<property name="selectionBehavior" >
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="column_down" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<item>
|
||||
<widget class="QToolButton" name="column_up" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="column_down" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3" >
|
||||
<property name="title" >
|
||||
<string>Use internal &viewer for the following formats:</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QListWidget" name="viewer" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QCheckBox" name="systray_icon" >
|
||||
<property name="text" >
|
||||
<string>Enable system &tray icon (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" >
|
||||
<widget class="QCheckBox" name="sync_news" >
|
||||
<property name="text" >
|
||||
<string>Automatically send downloaded &news to ebook reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" >
|
||||
<widget class="QCheckBox" name="delete_news" >
|
||||
<property name="text" >
|
||||
<string>&Delete news from library when it is sent to reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<widget class="QCheckBox" name="separate_cover_flow" >
|
||||
<property name="text" >
|
||||
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
<zorder>columns</zorder>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3" >
|
||||
<property name="title" >
|
||||
<string>Use internal &viewer for:</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QListWidget" name="viewer" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>roman_numerals</zorder>
|
||||
<zorder>groupBox_2</zorder>
|
||||
<zorder>groupBox</zorder>
|
||||
<zorder>systray_icon</zorder>
|
||||
<zorder>sync_news</zorder>
|
||||
<zorder>delete_news</zorder>
|
||||
<zorder>separate_cover_flow</zorder>
|
||||
<zorder>systray_notifications</zorder>
|
||||
<zorder>groupBox_3</zorder>
|
||||
<zorder></zorder>
|
||||
<zorder></zorder>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2" >
|
||||
<layout class="QVBoxLayout" >
|
||||
|
@ -14,7 +14,7 @@ from lxml.etree import XPath
|
||||
|
||||
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||
from calibre.gui2.dialogs.epub_ui import Ui_Dialog
|
||||
from calibre.gui2 import error_dialog, choose_images, pixmap_to_data
|
||||
from calibre.gui2 import error_dialog, choose_images, pixmap_to_data, ResizableDialog
|
||||
from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
@ -22,13 +22,12 @@ from calibre.ebooks.metadata.opf import OPFCreator
|
||||
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
||||
|
||||
|
||||
class Config(QDialog, Ui_Dialog):
|
||||
class Config(ResizableDialog, Ui_Dialog):
|
||||
|
||||
OUTPUT = 'EPUB'
|
||||
|
||||
def __init__(self, parent, db, row=None, config=epubconfig):
|
||||
QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
ResizableDialog.__init__(self, parent)
|
||||
self.hide_controls()
|
||||
self.connect(self.category_list, SIGNAL('itemEntered(QListWidgetItem *)'),
|
||||
self.show_category_help)
|
||||
@ -62,17 +61,18 @@ class Config(QDialog, Ui_Dialog):
|
||||
self.opt_toc_title.setVisible(False)
|
||||
self.toc_title_label.setVisible(False)
|
||||
self.opt_rescale_images.setVisible(False)
|
||||
self.opt_ignore_tables.setVisible(False)
|
||||
|
||||
def initialize(self):
|
||||
self.__w = []
|
||||
self.__w.append(QIcon(':/images/dialog_information.svg'))
|
||||
self.item1 = QListWidgetItem(self.__w[-1], _('Metadata'), self.category_list)
|
||||
self.__w.append(QIcon(':/images/lookfeel.svg'))
|
||||
self.item2 = QListWidgetItem(self.__w[-1], _('Look & Feel'), self.category_list)
|
||||
self.item2 = QListWidgetItem(self.__w[-1], _('Look & Feel').replace(' ','\n'), self.category_list)
|
||||
self.__w.append(QIcon(':/images/page.svg'))
|
||||
self.item3 = QListWidgetItem(self.__w[-1], _('Page Setup'), self.category_list)
|
||||
self.item3 = QListWidgetItem(self.__w[-1], _('Page Setup').replace(' ','\n'), self.category_list)
|
||||
self.__w.append(QIcon(':/images/chapters.svg'))
|
||||
self.item4 = QListWidgetItem(self.__w[-1], _('Chapter Detection'), self.category_list)
|
||||
self.item4 = QListWidgetItem(self.__w[-1], _('Chapter Detection').replace(' ','\n'), self.category_list)
|
||||
self.setup_tooltips()
|
||||
self.initialize_options()
|
||||
|
||||
@ -98,7 +98,7 @@ class Config(QDialog, Ui_Dialog):
|
||||
_('Page Setup') : _('Specify the page layout settings like margins.'),
|
||||
_('Chapter Detection') : _('Fine tune the detection of chapter and section headings.'),
|
||||
}
|
||||
self.set_help(help[text])
|
||||
self.set_help(help[text.replace('\n', ' ')])
|
||||
|
||||
def select_cover(self):
|
||||
files = choose_images(self, 'change cover dialog',
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog, QCompl
|
||||
|
||||
|
||||
from calibre.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \
|
||||
choose_files, pixmap_to_data, choose_images
|
||||
choose_files, pixmap_to_data, choose_images, ResizableDialog
|
||||
from calibre.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog
|
||||
from calibre.gui2.dialogs.fetch_metadata import FetchMetadata
|
||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||
@ -40,7 +40,7 @@ class AuthorCompleter(QCompleter):
|
||||
all_authors.sort(cmp=lambda x, y : cmp(x[1], y[1]))
|
||||
QCompleter.__init__(self, [x[1] for x in all_authors])
|
||||
|
||||
class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
|
||||
class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
|
||||
def do_reset_cover(self, *args):
|
||||
pix = QPixmap(':/images/book.svg')
|
||||
@ -164,9 +164,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
|
||||
self.db.remove_format(self.row, ext, notify=False)
|
||||
|
||||
def __init__(self, window, row, db, accepted_callback=None):
|
||||
QDialog.__init__(self, window)
|
||||
Ui_MetadataSingleDialog.__init__(self)
|
||||
self.setupUi(self)
|
||||
ResizableDialog.__init__(self, window)
|
||||
self.bc_box.layout().setAlignment(self.cover, Qt.AlignCenter|Qt.AlignHCenter)
|
||||
self.splitter.setStretchFactor(100, 1)
|
||||
self.db = db
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,11 +7,11 @@ __docformat__ = 'restructuredtext en'
|
||||
Scheduler for automated recipe downloads
|
||||
'''
|
||||
|
||||
import sys, copy
|
||||
import sys, copy, time
|
||||
from datetime import datetime, timedelta
|
||||
from PyQt4.Qt import QDialog, QApplication, QLineEdit, QPalette, SIGNAL, QBrush, \
|
||||
QColor, QAbstractListModel, Qt, QVariant, QFont, QIcon, \
|
||||
QFile, QObject, QTimer, QMutex, QMenu, QAction
|
||||
QFile, QObject, QTimer, QMutex, QMenu, QAction, QTime
|
||||
|
||||
from calibre import english_sort
|
||||
from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog
|
||||
@ -66,7 +66,10 @@ class Recipe(object):
|
||||
return self.id == getattr(other, 'id', None)
|
||||
|
||||
def __repr__(self):
|
||||
return u'%s|%s|%s|%s'%(self.id, self.title, self.last_downloaded.ctime(), self.schedule)
|
||||
schedule = self.schedule
|
||||
if schedule and schedule > 1e5:
|
||||
schedule = decode_schedule(schedule)
|
||||
return u'%s|%s|%s|%s'%(self.id, self.title, self.last_downloaded.ctime(), schedule)
|
||||
|
||||
builtin_recipes = [Recipe(m, r, True) for r, m in zip(recipes, recipe_modules)]
|
||||
|
||||
@ -169,6 +172,11 @@ class RecipeModel(QAbstractListModel, SearchQueryParser):
|
||||
return QVariant(icon)
|
||||
|
||||
return NONE
|
||||
|
||||
def update_recipe_schedule(self, recipe):
|
||||
for srecipe in self.recipes:
|
||||
if srecipe == recipe:
|
||||
srecipe.schedule = recipe.schedule
|
||||
|
||||
|
||||
class Search(QLineEdit):
|
||||
@ -210,7 +218,17 @@ class Search(QLineEdit):
|
||||
text = unicode(self.text())
|
||||
self.emit(SIGNAL('search(PyQt_PyObject)'), text)
|
||||
|
||||
|
||||
def encode_schedule(day, hour, minute):
|
||||
day = 1e7 * (day+1)
|
||||
hour = 1e4 * (hour+1)
|
||||
return day + hour + minute + 1
|
||||
|
||||
def decode_schedule(num):
|
||||
raw = '%d'%int(num)
|
||||
day = int(raw[0])
|
||||
hour = int(raw[2:4])
|
||||
minute = int(raw[-2:])
|
||||
return day-1, hour-1, minute-1
|
||||
|
||||
class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
|
||||
@ -228,17 +246,22 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
self.connect(self.username, SIGNAL('textEdited(QString)'), self.set_account_info)
|
||||
self.connect(self.password, SIGNAL('textEdited(QString)'), self.set_account_info)
|
||||
self.connect(self.schedule, SIGNAL('stateChanged(int)'), self.do_schedule)
|
||||
self.connect(self.schedule, SIGNAL('stateChanged(int)'),
|
||||
lambda state: self.interval.setEnabled(state == Qt.Checked))
|
||||
self.connect(self.show_password, SIGNAL('stateChanged(int)'),
|
||||
lambda state: self.password.setEchoMode(self.password.Normal if state == Qt.Checked else self.password.Password))
|
||||
self.connect(self.interval, SIGNAL('valueChanged(double)'), self.do_schedule)
|
||||
self.connect(self.day, SIGNAL('currentIndexChanged(int)'), self.do_schedule)
|
||||
self.connect(self.time, SIGNAL('timeChanged(QTime)'), self.do_schedule)
|
||||
for button in (self.daily_button, self.interval_button):
|
||||
self.connect(button, SIGNAL('toggled(bool)'), self.do_schedule)
|
||||
self.connect(self.search, SIGNAL('search(PyQt_PyObject)'), self._model.search)
|
||||
self.connect(self._model, SIGNAL('modelReset()'), lambda : self.detail_box.setVisible(False))
|
||||
self.connect(self.download, SIGNAL('clicked()'), self.download_now)
|
||||
self.search.setFocus(Qt.OtherFocusReason)
|
||||
self.old_news.setValue(gconf['oldest_news'])
|
||||
self.rnumber.setText(_('%d recipes')%self._model.rowCount(None))
|
||||
for day in (_('day'), _('Monday'), _('Tuesday'), _('Wednesday'),
|
||||
_('Thursday'), _('Friday'), _('Saturday'), _('Sunday')):
|
||||
self.day.addItem(day)
|
||||
|
||||
def download_now(self):
|
||||
recipe = self._model.data(self.recipes.currentIndex(), Qt.UserRole)
|
||||
@ -252,6 +275,8 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
config[key] = (username, password) if username and password else None
|
||||
|
||||
def do_schedule(self, *args):
|
||||
if not getattr(self, 'allow_scheduling', False):
|
||||
return
|
||||
recipe = self.recipes.currentIndex()
|
||||
if not recipe.isValid():
|
||||
return
|
||||
@ -263,17 +288,26 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
else:
|
||||
recipe.last_downloaded = datetime.fromordinal(1)
|
||||
recipes.append(recipe)
|
||||
recipe.schedule = self.interval.value()
|
||||
if recipe.schedule < 0.1:
|
||||
recipe.schedule = 1/24.
|
||||
if recipe.needs_subscription and not config['recipe_account_info_%s'%recipe.id]:
|
||||
error_dialog(self, _('Must set account information'), _('This recipe requires a username and password')).exec_()
|
||||
self.schedule.setCheckState(Qt.Unchecked)
|
||||
return
|
||||
if self.interval_button.isChecked():
|
||||
recipe.schedule = self.interval.value()
|
||||
if recipe.schedule < 0.1:
|
||||
recipe.schedule = 1/24.
|
||||
else:
|
||||
day_of_week = self.day.currentIndex() - 1
|
||||
if day_of_week < 0:
|
||||
day_of_week = 7
|
||||
t = self.time.time()
|
||||
hour, minute = t.hour(), t.minute()
|
||||
recipe.schedule = encode_schedule(day_of_week, hour, minute)
|
||||
else:
|
||||
if recipe in recipes:
|
||||
recipes.remove(recipe)
|
||||
save_recipes(recipes)
|
||||
self._model.update_recipe_schedule(recipe)
|
||||
self.emit(SIGNAL('new_schedule(PyQt_PyObject)'), recipes)
|
||||
|
||||
def show_recipe(self, index):
|
||||
@ -282,8 +316,26 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
self.title.setText(recipe.title)
|
||||
self.author.setText(_('Created by: ') + recipe.author)
|
||||
self.description.setText(recipe.description if recipe.description else '')
|
||||
self.allow_scheduling = False
|
||||
schedule = -1 if recipe.schedule is None else recipe.schedule
|
||||
if schedule < 1e5 and schedule >= 0:
|
||||
self.interval.setValue(schedule)
|
||||
self.interval_button.setChecked(True)
|
||||
self.day.setEnabled(False), self.time.setEnabled(False)
|
||||
else:
|
||||
if schedule > 0:
|
||||
day, hour, minute = decode_schedule(schedule)
|
||||
else:
|
||||
day, hour, minute = 7, 12, 0
|
||||
if day == 7:
|
||||
day = -1
|
||||
self.day.setCurrentIndex(day+1)
|
||||
self.time.setTime(QTime(hour, minute))
|
||||
self.daily_button.setChecked(True)
|
||||
self.interval_button.setChecked(False)
|
||||
self.interval.setEnabled(False)
|
||||
self.schedule.setChecked(recipe.schedule is not None)
|
||||
self.interval.setValue(recipe.schedule if recipe.schedule is not None else 1)
|
||||
self.allow_scheduling = True
|
||||
self.detail_box.setVisible(True)
|
||||
self.account.setVisible(recipe.needs_subscription)
|
||||
self.interval.setEnabled(self.schedule.checkState() == Qt.Checked)
|
||||
@ -365,13 +417,22 @@ class Scheduler(QObject):
|
||||
self.dirtied = False
|
||||
needs_downloading = set([])
|
||||
self.debug('Checking...')
|
||||
now = datetime.utcnow()
|
||||
nowt = datetime.utcnow()
|
||||
for recipe in self.recipes:
|
||||
if recipe.schedule is None:
|
||||
continue
|
||||
delta = now - recipe.last_downloaded
|
||||
if delta > timedelta(days=recipe.schedule):
|
||||
needs_downloading.add(recipe)
|
||||
delta = nowt - recipe.last_downloaded
|
||||
if recipe.schedule < 1e5:
|
||||
if delta > timedelta(days=recipe.schedule):
|
||||
needs_downloading.add(recipe)
|
||||
else:
|
||||
day, hour, minute = decode_schedule(recipe.schedule)
|
||||
now = time.localtime()
|
||||
day_matches = day > 6 or day == now.tm_wday
|
||||
tnow = now.tm_hour*60 + now.tm_min
|
||||
matches = day_matches and (hour*60+minute) < tnow
|
||||
if matches and delta >= timedelta(days=1):
|
||||
needs_downloading.add(recipe)
|
||||
|
||||
self.debug('Needs downloading:', needs_downloading)
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>726</width>
|
||||
<height>551</height>
|
||||
<height>575</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
@ -42,25 +42,12 @@
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="detail_box" >
|
||||
<property name="title" >
|
||||
<string>Schedule for download</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" >
|
||||
<item>
|
||||
<widget class="QLabel" name="title" >
|
||||
<property name="font" >
|
||||
@ -110,70 +97,97 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="schedule" >
|
||||
<property name="text" >
|
||||
<string>&Schedule for download every:</string>
|
||||
<string>&Schedule for download:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<spacer name="horizontalSpacer" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="interval" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>Interval at which to download this recipe. A value of zero means that the recipe will be downloaded every hour.</string>
|
||||
</property>
|
||||
<property name="suffix" >
|
||||
<string> days</string>
|
||||
</property>
|
||||
<property name="decimals" >
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<double>365.100000000000023</double>
|
||||
</property>
|
||||
<property name="singleStep" >
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QWidget" native="1" name="widget" >
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" >
|
||||
<item>
|
||||
<widget class="QRadioButton" name="daily_button" >
|
||||
<property name="text" >
|
||||
<string>Every </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="day" />
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<string>at</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTimeEdit" name="time" />
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<widget class="QRadioButton" name="interval_button" >
|
||||
<property name="text" >
|
||||
<string>Every </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="interval" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>Interval at which to download this recipe. A value of zero means that the recipe will be downloaded every hour.</string>
|
||||
</property>
|
||||
<property name="suffix" >
|
||||
<string> days</string>
|
||||
</property>
|
||||
<property name="decimals" >
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<double>365.100000000000023</double>
|
||||
</property>
|
||||
<property name="singleStep" >
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="last_downloaded" >
|
||||
@ -315,8 +329,8 @@
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
<x>613</x>
|
||||
<y>824</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>157</x>
|
||||
@ -331,8 +345,8 @@
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
<x>681</x>
|
||||
<y>824</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>286</x>
|
||||
@ -340,5 +354,85 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>schedule</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>widget</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>454</x>
|
||||
<y>147</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>461</x>
|
||||
<y>168</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>schedule</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>widget</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>458</x>
|
||||
<y>137</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>461</x>
|
||||
<y>169</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>daily_button</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>day</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>421</x>
|
||||
<y>186</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>500</x>
|
||||
<y>184</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>daily_button</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>time</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>442</x>
|
||||
<y>193</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>603</x>
|
||||
<y>183</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>interval_button</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>interval</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>428</x>
|
||||
<y>213</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>495</x>
|
||||
<y>218</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
@ -3,21 +3,20 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import time, os
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, QUrl
|
||||
from PyQt4.QtGui import QDialog, QMessageBox, QDesktopServices
|
||||
from PyQt4.QtGui import QMessageBox, QDesktopServices
|
||||
|
||||
from calibre.web.feeds.recipes import compile_recipe
|
||||
from calibre.web.feeds.news import AutomaticNewsRecipe
|
||||
from calibre.gui2.dialogs.user_profiles_ui import Ui_Dialog
|
||||
from calibre.gui2 import qstring_to_unicode, error_dialog, question_dialog, choose_files
|
||||
from calibre.gui2 import qstring_to_unicode, error_dialog, question_dialog, \
|
||||
choose_files, ResizableDialog
|
||||
from calibre.gui2.widgets import PythonHighlighter
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
|
||||
class UserProfiles(QDialog, Ui_Dialog):
|
||||
class UserProfiles(ResizableDialog, Ui_Dialog):
|
||||
|
||||
def __init__(self, parent, feeds):
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_Dialog.__init__(self)
|
||||
self.setupUi(self)
|
||||
ResizableDialog.__init__(self, parent)
|
||||
|
||||
self.connect(self.remove_feed_button, SIGNAL('clicked(bool)'),
|
||||
self.added_feeds.remove_selected_items)
|
||||
|
@ -5,8 +5,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>744</width>
|
||||
<height>633</height>
|
||||
<width>738</width>
|
||||
<height>640</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
@ -16,8 +16,420 @@
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/user_profile.svg</normaloff>:/images/user_profile.svg</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="1" column="0" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" >
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="lineWidth" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="widgetResizable" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>730</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" native="1" name="central_widget" >
|
||||
<property name="minimumSize" >
|
||||
<size>
|
||||
<width>680</width>
|
||||
<height>550</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Preferred" hsizetype="Minimum" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>Available user recipes</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
||||
<item>
|
||||
<widget class="BasicList" name="available_profiles" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Minimum" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_profile_button" >
|
||||
<property name="text" >
|
||||
<string>Add/Update &recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/plus.svg</normaloff>:/images/plus.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="remove_profile_button" >
|
||||
<property name="text" >
|
||||
<string>&Remove recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/list_remove.svg</normaloff>:/images/list_remove.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="share_button" >
|
||||
<property name="text" >
|
||||
<string>&Share recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/forward.svg</normaloff>:/images/forward.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="builtin_recipe_button" >
|
||||
<property name="text" >
|
||||
<string>Customize &builtin recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/news.svg</normaloff>:/images/news.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="load_button" >
|
||||
<property name="text" >
|
||||
<string>&Load recipe from file</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/chapters.svg</normaloff>:/images/chapters.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" >
|
||||
<item>
|
||||
<widget class="QPushButton" name="toggle_mode_button" >
|
||||
<property name="text" >
|
||||
<string>Switch to Advanced mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stacks" >
|
||||
<property name="currentIndex" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string><html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Create a basic news recipe, by adding RSS feeds to it. <br />For most feeds, you will have to use the "Advanced mode" to further customize the fetch process.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>Recipe &title:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>profile_title</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2" >
|
||||
<widget class="QLineEdit" name="profile_title" >
|
||||
<property name="font" >
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_6" >
|
||||
<property name="text" >
|
||||
<string>&Oldest article:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>oldest_article</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2" >
|
||||
<widget class="QSpinBox" name="oldest_article" >
|
||||
<property name="toolTip" >
|
||||
<string>The oldest article to download</string>
|
||||
</property>
|
||||
<property name="suffix" >
|
||||
<string> days</string>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<number>365</number>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<number>7</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<widget class="QLabel" name="label_7" >
|
||||
<property name="text" >
|
||||
<string>&Max. number of articles per feed:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>max_articles</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2" >
|
||||
<widget class="QSpinBox" name="max_articles" >
|
||||
<property name="toolTip" >
|
||||
<string>Maximum number of articles to download per feed.</string>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
||||
<horstretch>100</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>Feeds in recipe</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="BasicList" name="added_feeds" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||
<horstretch>100</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QToolButton" name="up_button" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="remove_feed_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Remove feed from recipe</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/list_remove.svg</normaloff>:/images/list_remove.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="down_button" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3" >
|
||||
<property name="title" >
|
||||
<string>Add feed to recipe</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<string>&Feed title:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>feed_title</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QLineEdit" name="feed_title" />
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label_5" >
|
||||
<property name="text" >
|
||||
<string>Feed &URL:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>feed_url</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="feed_url" />
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2" >
|
||||
<widget class="QPushButton" name="add_feed_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Add feed to recipe</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>&Add feed</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/plus.svg</normaloff>:/images/plus.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2" >
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8" >
|
||||
<property name="text" >
|
||||
<string>For help with writing advanced news recipes, please visit <a href="http://__appname__.kovidgoyal.net/user_manual/news.html">User Recipes</a></string>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4" >
|
||||
<property name="title" >
|
||||
<string>Recipe source code (python)</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QTextEdit" name="source_code" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||
<horstretch>100</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font" >
|
||||
<font>
|
||||
<family>DejaVu Sans Mono</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="lineWrapMode" >
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="acceptRichText" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@ -27,360 +439,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" >
|
||||
<widget class="QSplitter" name="splitter" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Preferred" hsizetype="Minimum" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize" >
|
||||
<size>
|
||||
<width>215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>Available user recipes</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="BasicList" name="available_profiles" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Minimum" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_profile_button" >
|
||||
<property name="text" >
|
||||
<string>Add/Update &recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/plus.svg</normaloff>:/images/plus.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="remove_profile_button" >
|
||||
<property name="text" >
|
||||
<string>&Remove recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/list_remove.svg</normaloff>:/images/list_remove.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="share_button" >
|
||||
<property name="text" >
|
||||
<string>&Share recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/forward.svg</normaloff>:/images/forward.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="builtin_recipe_button" >
|
||||
<property name="text" >
|
||||
<string>Customize &builtin recipe</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/news.svg</normaloff>:/images/news.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="load_button" >
|
||||
<property name="text" >
|
||||
<string>&Load recipe from file</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/chapters.svg</normaloff>:/images/chapters.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget" >
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QPushButton" name="toggle_mode_button" >
|
||||
<property name="text" >
|
||||
<string>Switch to Advanced mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stacks" >
|
||||
<property name="currentIndex" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page" >
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string><html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Create a basic news recipe, by adding RSS feeds to it. <br />For most feeds, you will have to use the "Advanced mode" to further customize the fetch process.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>Recipe &title:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>profile_title</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2" >
|
||||
<widget class="QLineEdit" name="profile_title" >
|
||||
<property name="font" >
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_6" >
|
||||
<property name="text" >
|
||||
<string>&Oldest article:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>oldest_article</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2" >
|
||||
<widget class="QSpinBox" name="oldest_article" >
|
||||
<property name="toolTip" >
|
||||
<string>The oldest article to download</string>
|
||||
</property>
|
||||
<property name="suffix" >
|
||||
<string> days</string>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<number>365</number>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<number>7</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<widget class="QLabel" name="label_7" >
|
||||
<property name="text" >
|
||||
<string>&Max. number of articles per feed:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>max_articles</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2" >
|
||||
<widget class="QSpinBox" name="max_articles" >
|
||||
<property name="toolTip" >
|
||||
<string>Maximum number of articles to download per feed.</string>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2" >
|
||||
<property name="title" >
|
||||
<string>Feeds in recipe</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="BasicList" name="added_feeds" >
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QToolButton" name="up_button" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="remove_feed_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Remove feed from recipe</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/list_remove.svg</normaloff>:/images/list_remove.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="down_button" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3" >
|
||||
<property name="title" >
|
||||
<string>Add feed to recipe</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<string>&Feed title:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>feed_title</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QLineEdit" name="feed_title" />
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label_5" >
|
||||
<property name="text" >
|
||||
<string>Feed &URL:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>feed_url</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="feed_url" />
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2" >
|
||||
<widget class="QPushButton" name="add_feed_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Add feed to recipe</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>&Add feed</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/plus.svg</normaloff>:/images/plus.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2" >
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8" >
|
||||
<property name="text" >
|
||||
<string>For help with writing advanced news recipes, please visit <a href="http://__appname__.kovidgoyal.net/user_manual/news.html">User Recipes</a></string>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4" >
|
||||
<property name="title" >
|
||||
<string>Recipe source code (python)</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QTextEdit" name="source_code" >
|
||||
<property name="font" >
|
||||
<font>
|
||||
<family>DejaVu Sans Mono</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="lineWrapMode" >
|
||||
<enum>QTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="acceptRichText" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
BIN
src/calibre/gui2/images/news/amspec.png
Normal file
BIN
src/calibre/gui2/images/news/amspec.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 834 B |
BIN
src/calibre/gui2/images/news/criticadigital.png
Normal file
BIN
src/calibre/gui2/images/news/criticadigital.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 710 B |
BIN
src/calibre/gui2/images/news/elcronista.png
Normal file
BIN
src/calibre/gui2/images/news/elcronista.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 770 B |
BIN
src/calibre/gui2/images/news/freakonomics.png
Normal file
BIN
src/calibre/gui2/images/news/freakonomics.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
BIN
src/calibre/gui2/images/news/teleread.png
Normal file
BIN
src/calibre/gui2/images/news/teleread.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 287 B |
@ -63,7 +63,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
p.end()
|
||||
self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap))
|
||||
|
||||
def __init__(self, single_instance, opts, parent=None):
|
||||
def __init__(self, single_instance, opts, actions, parent=None):
|
||||
self.preferences_action, self.quit_action = actions
|
||||
MainWindow.__init__(self, opts, parent)
|
||||
# Initialize fontconfig in a separate thread as this can be a lengthy
|
||||
# process if run for the first time on this machine
|
||||
@ -99,8 +100,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.system_tray_icon.show()
|
||||
self.system_tray_menu = QMenu()
|
||||
self.restore_action = self.system_tray_menu.addAction(QIcon(':/images/page.svg'), _('&Restore'))
|
||||
self.donate_action = self.system_tray_menu.addAction(QIcon(':/images/donate.svg'), _('&Donate'))
|
||||
self.quit_action = QAction(QIcon(':/images/window-close.svg'), _('&Quit'), self)
|
||||
self.donate_action = self.system_tray_menu.addAction(QIcon(':/images/donate.svg'), _('&Donate to support calibre'))
|
||||
self.donate_button.setDefaultAction(self.donate_action)
|
||||
self.addAction(self.quit_action)
|
||||
self.action_restart = QAction(_('&Restart'), self)
|
||||
self.addAction(self.action_restart)
|
||||
@ -134,16 +135,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
for f in self.output_formats:
|
||||
self.output_format.addItem(f)
|
||||
self.output_format.setCurrentIndex(self.output_formats.index(prefs['output_format']))
|
||||
def change_output_format(x):
|
||||
of = unicode(x).strip()
|
||||
if of != prefs['output_format']:
|
||||
if of not in ('LRF',):
|
||||
warning_dialog(self, 'Warning',
|
||||
'<p>%s support is still in beta. If you find bugs, please report them by opening a <a href="http://calibre.kovidgoyal.net">ticket</a>.'%of).exec_()
|
||||
prefs.set('output_format', of)
|
||||
|
||||
self.connect(self.output_format, SIGNAL('currentIndexChanged(QString)'),
|
||||
change_output_format)
|
||||
self.change_output_format, Qt.QueuedConnection)
|
||||
|
||||
####################### Vanity ########################
|
||||
self.vanity_template = _('<p>For help visit <a href="http://%s.kovidgoyal.net/user_manual">%s.kovidgoyal.net</a><br>')%(__appname__, __appname__)
|
||||
@ -250,6 +243,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
|
||||
|
||||
QObject.connect(self.config_button, SIGNAL('clicked(bool)'), self.do_config)
|
||||
self.connect(self.preferences_action, SIGNAL('triggered(bool)'), self.do_config)
|
||||
QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search)
|
||||
|
||||
####################### Library view ########################
|
||||
@ -376,6 +370,15 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.action_news.setMenu(self.scheduler.news_menu)
|
||||
self.connect(self.action_news, SIGNAL('triggered(bool)'), self.scheduler.show_dialog)
|
||||
self.location_view.setCurrentIndex(self.location_view.model().index(0))
|
||||
|
||||
def change_output_format(self, x):
|
||||
of = unicode(x).strip()
|
||||
if of != prefs['output_format']:
|
||||
if of not in ('LRF',):
|
||||
warning_dialog(self, 'Warning',
|
||||
'<p>%s support is still in beta. If you find bugs, please report them by opening a <a href="http://calibre.kovidgoyal.net">ticket</a>.'%of).exec_()
|
||||
prefs.set('output_format', of)
|
||||
|
||||
|
||||
def test_server(self, *args):
|
||||
if self.content_server.exception is not None:
|
||||
@ -911,13 +914,14 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
_files = self.library_view.model().get_preferred_formats(rows,
|
||||
self.device_manager.device_class.FORMATS, paths=True)
|
||||
files = [getattr(f, 'name', None) for f in _files]
|
||||
bad, good, gf, names = [], [], [], []
|
||||
bad, good, gf, names, remove_ids = [], [], [], [], []
|
||||
for f in files:
|
||||
mi = metadata.next()
|
||||
id = ids.next()
|
||||
if f is None:
|
||||
bad.append(mi['title'])
|
||||
else:
|
||||
remove_ids.append(id)
|
||||
aus = mi['authors'].split(',')
|
||||
aus2 = []
|
||||
for a in aus:
|
||||
@ -944,7 +948,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
prefix = prefix.decode(preferred_encoding, 'replace')
|
||||
prefix = ascii_filename(prefix)
|
||||
names.append('%s_%d%s'%(prefix, id, os.path.splitext(f)[1]))
|
||||
remove = [self.library_view.model().id(r) for r in rows] if delete_from_library else []
|
||||
remove = remove_ids if delete_from_library else []
|
||||
self.upload_books(gf, names, good, on_card, memory=(_files, remove))
|
||||
self.status_bar.showMessage(_('Sending books to device.'), 5000)
|
||||
if bad:
|
||||
@ -1419,7 +1423,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.restart_after_quit = restart
|
||||
QApplication.instance().quit()
|
||||
|
||||
def donate(self):
|
||||
def donate(self, *args):
|
||||
BUTTON = '''
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
@ -1545,6 +1549,7 @@ def main(args=sys.argv):
|
||||
prefs.set('library_path', opts.with_library)
|
||||
print 'Using library at', prefs['library_path']
|
||||
app = Application(args)
|
||||
actions = tuple(Main.create_application_menubar())
|
||||
app.setWindowIcon(QIcon(':/library'))
|
||||
QCoreApplication.setOrganizationName(ORG_NAME)
|
||||
QCoreApplication.setApplicationName(APP_UID)
|
||||
@ -1559,7 +1564,7 @@ def main(args=sys.argv):
|
||||
'<p>%s is already running. %s</p>'%(__appname__, extra))
|
||||
return 1
|
||||
initialize_file_icon_provider()
|
||||
main = Main(single_instance, opts)
|
||||
main = Main(single_instance, opts, actions)
|
||||
sys.excepthook = main.unhandled_exception
|
||||
if len(args) > 1:
|
||||
main.add_filesystem_book(args[1])
|
||||
|
@ -82,6 +82,29 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="donate_button" >
|
||||
<property name="cursor" >
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="images.qrc" >
|
||||
<normaloff>:/images/donate.svg</normaloff>:/images/donate.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize" >
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="autoRaise" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<item>
|
||||
@ -119,7 +142,11 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="output_format" />
|
||||
<widget class="QComboBox" name="output_format" >
|
||||
<property name="toolTip" >
|
||||
<string>Set the output format that is used when converting ebooks and downloading news</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
@ -3,7 +3,8 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import StringIO, traceback, sys
|
||||
|
||||
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL
|
||||
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL,\
|
||||
QAction, QMenu, QMenuBar, QIcon
|
||||
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
|
||||
from calibre.utils.config import OptionParser
|
||||
|
||||
@ -33,6 +34,27 @@ class DebugWindow(ConversionErrorDialog):
|
||||
pass
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
|
||||
_menu_bar = None
|
||||
|
||||
@classmethod
|
||||
def create_application_menubar(cls):
|
||||
mb = QMenuBar(None)
|
||||
menu = QMenu()
|
||||
for action in cls.get_menubar_actions():
|
||||
menu.addAction(action)
|
||||
yield action
|
||||
mb.addMenu(menu)
|
||||
cls._menu_bar = mb
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_menubar_actions(cls):
|
||||
preferences_action = QAction(QIcon(':/images/config.svg'), _('&Preferences'), None)
|
||||
quit_action = QAction(QIcon(':/images/window-close.svg'), _('&Quit'), None)
|
||||
preferences_action.setMenuRole(QAction.PreferencesRole)
|
||||
quit_action.setMenuRole(QAction.QuitRole)
|
||||
return preferences_action, quit_action
|
||||
|
||||
def __init__(self, opts, parent=None):
|
||||
QMainWindow.__init__(self, parent)
|
||||
@ -58,4 +80,4 @@ class MainWindow(QMainWindow):
|
||||
d = ConversionErrorDialog(self, _('ERROR: Unhandled exception'), msg)
|
||||
d.exec_()
|
||||
except:
|
||||
pass
|
||||
pass
|
||||
|
@ -6,7 +6,7 @@ from PyQt4.QtGui import QStatusBar, QMovie, QLabel, QWidget, QHBoxLayout, QPixma
|
||||
QVBoxLayout, QSizePolicy, QToolButton, QIcon, QScrollArea, QFrame
|
||||
from PyQt4.QtCore import Qt, QSize, SIGNAL, QCoreApplication
|
||||
from calibre import fit_image, preferred_encoding, isosx
|
||||
from calibre.gui2 import qstring_to_unicode
|
||||
from calibre.gui2 import qstring_to_unicode, config
|
||||
|
||||
class BookInfoDisplay(QWidget):
|
||||
class BookCoverDisplay(QLabel):
|
||||
@ -148,6 +148,7 @@ class CoverFlowButton(QToolButton):
|
||||
self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding))
|
||||
self.connect(self, SIGNAL('toggled(bool)'), self.adjust_tooltip)
|
||||
self.adjust_tooltip(False)
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
def adjust_tooltip(self, on):
|
||||
tt = _('Click to turn off Cover Browsing') if on else _('Click to browse books by their covers')
|
||||
@ -165,6 +166,7 @@ class TagViewButton(QToolButton):
|
||||
self.setIcon(QIcon(':/images/tags.svg'))
|
||||
self.setToolTip(_('Click to browse books by tags'))
|
||||
self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding))
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
self.setCheckable(True)
|
||||
self.setChecked(False)
|
||||
self.setAutoRaise(True)
|
||||
@ -197,7 +199,7 @@ class StatusBar(QStatusBar):
|
||||
|
||||
def showMessage(self, msg, timeout=0):
|
||||
ret = QStatusBar.showMessage(self, msg, timeout)
|
||||
if self.systray is not None:
|
||||
if self.systray is not None and not config['disable_tray_notification']:
|
||||
if isosx and isinstance(msg, unicode):
|
||||
try:
|
||||
msg = msg.encode(preferred_encoding)
|
||||
|
@ -1,16 +1,8 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
''' Code to manage ebook library'''
|
||||
import re
|
||||
from calibre.utils.config import Config, StringConfig
|
||||
|
||||
title_pat = re.compile('^(A|The|An)\s+', re.IGNORECASE)
|
||||
def title_sort(title):
|
||||
match = title_pat.search(title)
|
||||
if match:
|
||||
prep = match.group(1)
|
||||
title = title.replace(prep, '') + ', ' + prep
|
||||
return title.strip()
|
||||
|
||||
def server_config(defaults=None):
|
||||
desc=_('Settings to control the calibre content server')
|
||||
|
@ -15,7 +15,7 @@ from PyQt4.QtCore import QCoreApplication, QThread, QReadWriteLock
|
||||
from PyQt4.QtGui import QApplication, QPixmap, QImage
|
||||
__app = None
|
||||
|
||||
from calibre.library import title_sort
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
from calibre.library.database import LibraryDatabase
|
||||
from calibre.library.sqlite import connect, IntegrityError
|
||||
from calibre.utils.search_query_parser import SearchQueryParser
|
||||
|
@ -312,7 +312,8 @@ class LibraryServer(object):
|
||||
|
||||
book, books = MarkupTemplate(self.BOOK), []
|
||||
for record in items[start:start+num]:
|
||||
authors = '|'.join([i.replace('|', ',') for i in record[2].split(',')])
|
||||
aus = record[2] if record[2] else _('Unknown')
|
||||
authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
|
||||
books.append(book.generate(r=record, authors=authors).render('xml').decode('utf-8'))
|
||||
updated = self.db.last_modified()
|
||||
|
||||
|
@ -13,7 +13,7 @@ from threading import Thread
|
||||
from Queue import Queue
|
||||
from threading import RLock
|
||||
|
||||
from calibre.library import title_sort
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
|
||||
global_lock = RLock()
|
||||
|
||||
|
@ -188,8 +188,31 @@ def extract(path, dir):
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
_libunrar.RARCloseArchive(arc_data)
|
||||
|
||||
def extract_member(path, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$', re.I)):
|
||||
|
||||
def names(path):
|
||||
if hasattr(path, 'read'):
|
||||
data = path.read()
|
||||
f = NamedTemporaryFile(suffix='.rar')
|
||||
f.write(data)
|
||||
f.flush()
|
||||
path = f.name
|
||||
open_archive_data = RAROpenArchiveDataEx(ArcName=path, OpenMode=RAR_OM_LIST, CmtBuf=None)
|
||||
arc_data = _libunrar.RAROpenArchiveEx(byref(open_archive_data))
|
||||
try:
|
||||
if open_archive_data.OpenResult != 0:
|
||||
raise UnRARException(_interpret_open_error(open_archive_data.OpenResult, path))
|
||||
header_data = RARHeaderDataEx(CmtBuf=None)
|
||||
while True:
|
||||
if _libunrar.RARReadHeaderEx(arc_data, byref(header_data)) != 0:
|
||||
break
|
||||
PFCode = _libunrar.RARProcessFileW(arc_data, RAR_SKIP, None, None)
|
||||
if PFCode != 0:
|
||||
raise UnRARException(_interpret_process_file_error(PFCode))
|
||||
yield header_data.FileNameW
|
||||
finally:
|
||||
_libunrar.RARCloseArchive(arc_data)
|
||||
|
||||
def extract_member(path, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$', re.I), name=None):
|
||||
if hasattr(path, 'read'):
|
||||
data = path.read()
|
||||
f = NamedTemporaryFile(suffix='.rar')
|
||||
@ -210,7 +233,9 @@ def extract_member(path, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$', re.I)):
|
||||
PFCode = _libunrar.RARProcessFileW(arc_data, RAR_EXTRACT, None, None)
|
||||
if PFCode != 0:
|
||||
raise UnRARException(_interpret_process_file_error(PFCode))
|
||||
if match.search(header_data.FileNameW):
|
||||
file_name = header_data.FileNameW
|
||||
if (name is not None and file_name == name) or \
|
||||
(match is not None and match.search(file_name)):
|
||||
return header_data.FileNameW.replace('/', os.sep), \
|
||||
open(os.path.join(dir, *header_data.FileNameW.split('/')), 'rb').read()
|
||||
finally:
|
||||
|
@ -16,65 +16,55 @@ if os.environ.has_key('DESTDIR'):
|
||||
|
||||
entry_points = {
|
||||
'console_scripts': [ \
|
||||
'prs500 = calibre.devices.prs500.cli.main:main',
|
||||
'lrf-meta = calibre.ebooks.lrf.meta:main',
|
||||
'rtf-meta = calibre.ebooks.metadata.rtf:main',
|
||||
'pdf-meta = calibre.ebooks.metadata.pdf:main',
|
||||
'lit-meta = calibre.ebooks.metadata.lit:main',
|
||||
'imp-meta = calibre.ebooks.metadata.imp:main',
|
||||
'rb-meta = calibre.ebooks.metadata.rb:main',
|
||||
'opf-meta = calibre.ebooks.metadata.opf2:main',
|
||||
'odt-meta = calibre.ebooks.metadata.odt:main',
|
||||
'epub-meta = calibre.ebooks.metadata.epub:main',
|
||||
'mobi-meta = calibre.ebooks.metadata.mobi:main',
|
||||
'txt2lrf = calibre.ebooks.lrf.txt.convert_from:main',
|
||||
'html2lrf = calibre.ebooks.lrf.html.convert_from:main',
|
||||
'html2oeb = calibre.ebooks.html:main',
|
||||
'html2epub = calibre.ebooks.epub.from_html:main',
|
||||
'odt2oeb = calibre.ebooks.odt.to_oeb:main',
|
||||
'markdown-calibre = calibre.ebooks.markdown.markdown:main',
|
||||
'lit2lrf = calibre.ebooks.lrf.lit.convert_from:main',
|
||||
'epub2lrf = calibre.ebooks.lrf.epub.convert_from:main',
|
||||
'rtf2lrf = calibre.ebooks.lrf.rtf.convert_from:main',
|
||||
'web2disk = calibre.web.fetch.simple:main',
|
||||
'feeds2disk = calibre.web.feeds.main:main',
|
||||
'calibre-server = calibre.library.server:main',
|
||||
'feeds2lrf = calibre.ebooks.lrf.feeds.convert_from:main',
|
||||
'feeds2epub = calibre.ebooks.epub.from_feeds:main',
|
||||
'feeds2mobi = calibre.ebooks.mobi.from_feeds:main',
|
||||
'web2lrf = calibre.ebooks.lrf.web.convert_from:main',
|
||||
'pdf2lrf = calibre.ebooks.lrf.pdf.convert_from:main',
|
||||
'mobi2lrf = calibre.ebooks.lrf.mobi.convert_from:main',
|
||||
'fb22lrf = calibre.ebooks.lrf.fb2.convert_from:main',
|
||||
'fb2-meta = calibre.ebooks.metadata.fb2:main',
|
||||
'any2lrf = calibre.ebooks.lrf.any.convert_from:main',
|
||||
'any2epub = calibre.ebooks.epub.from_any:main',
|
||||
'any2lit = calibre.ebooks.lit.from_any:main',
|
||||
'any2mobi = calibre.ebooks.mobi.from_any:main',
|
||||
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
|
||||
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main',
|
||||
'pdfreflow = calibre.ebooks.lrf.pdf.reflow:main',
|
||||
'isbndb = calibre.ebooks.metadata.isbndb:main',
|
||||
'librarything = calibre.ebooks.metadata.library_thing:main',
|
||||
'mobi2oeb = calibre.ebooks.mobi.reader:main',
|
||||
'oeb2mobi = calibre.ebooks.mobi.writer:main',
|
||||
'lrf2html = calibre.ebooks.lrf.html.convert_to:main',
|
||||
'lit2oeb = calibre.ebooks.lit.reader:main',
|
||||
'oeb2lit = calibre.ebooks.lit.writer:main',
|
||||
'comic2lrf = calibre.ebooks.lrf.comic.convert_from:main',
|
||||
'comic2epub = calibre.ebooks.epub.from_comic:main',
|
||||
'comic2pdf = calibre.ebooks.pdf.from_comic:main',
|
||||
'calibre-debug = calibre.debug:main',
|
||||
'calibredb = calibre.library.cli:main',
|
||||
'calibre-fontconfig = calibre.utils.fontconfig:main',
|
||||
'calibre-parallel = calibre.parallel:main',
|
||||
'calibre-customize = calibre.customize.ui:main',
|
||||
'pdftrim = calibre.ebooks.pdf.pdftrim:main' ,
|
||||
'ebook-device = calibre.devices.prs500.cli.main:main',
|
||||
'ebook-meta = calibre.ebooks.metadata.cli:main',
|
||||
'txt2lrf = calibre.ebooks.lrf.txt.convert_from:main',
|
||||
'html2lrf = calibre.ebooks.lrf.html.convert_from:main',
|
||||
'html2oeb = calibre.ebooks.html:main',
|
||||
'html2epub = calibre.ebooks.epub.from_html:main',
|
||||
'odt2oeb = calibre.ebooks.odt.to_oeb:main',
|
||||
'markdown-calibre = calibre.ebooks.markdown.markdown:main',
|
||||
'lit2lrf = calibre.ebooks.lrf.lit.convert_from:main',
|
||||
'epub2lrf = calibre.ebooks.lrf.epub.convert_from:main',
|
||||
'rtf2lrf = calibre.ebooks.lrf.rtf.convert_from:main',
|
||||
'web2disk = calibre.web.fetch.simple:main',
|
||||
'feeds2disk = calibre.web.feeds.main:main',
|
||||
'calibre-server = calibre.library.server:main',
|
||||
'feeds2lrf = calibre.ebooks.lrf.feeds.convert_from:main',
|
||||
'feeds2epub = calibre.ebooks.epub.from_feeds:main',
|
||||
'feeds2mobi = calibre.ebooks.mobi.from_feeds:main',
|
||||
'web2lrf = calibre.ebooks.lrf.web.convert_from:main',
|
||||
'pdf2lrf = calibre.ebooks.lrf.pdf.convert_from:main',
|
||||
'mobi2lrf = calibre.ebooks.lrf.mobi.convert_from:main',
|
||||
'fb22lrf = calibre.ebooks.lrf.fb2.convert_from:main',
|
||||
'any2lrf = calibre.ebooks.lrf.any.convert_from:main',
|
||||
'any2epub = calibre.ebooks.epub.from_any:main',
|
||||
'any2lit = calibre.ebooks.lit.from_any:main',
|
||||
'any2mobi = calibre.ebooks.mobi.from_any:main',
|
||||
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
|
||||
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main',
|
||||
'pdfreflow = calibre.ebooks.lrf.pdf.reflow:main',
|
||||
'isbndb = calibre.ebooks.metadata.isbndb:main',
|
||||
'librarything = calibre.ebooks.metadata.library_thing:main',
|
||||
'mobi2oeb = calibre.ebooks.mobi.reader:main',
|
||||
'oeb2mobi = calibre.ebooks.mobi.writer:main',
|
||||
'lit2oeb = calibre.ebooks.lit.reader:main',
|
||||
'oeb2lit = calibre.ebooks.lit.writer:main',
|
||||
'comic2lrf = calibre.ebooks.lrf.comic.convert_from:main',
|
||||
'comic2epub = calibre.ebooks.epub.from_comic:main',
|
||||
'comic2mobi = calibre.ebooks.mobi.from_comic:main',
|
||||
'comic2pdf = calibre.ebooks.pdf.from_comic:main',
|
||||
'calibre-debug = calibre.debug:main',
|
||||
'calibredb = calibre.library.cli:main',
|
||||
'calibre-fontconfig = calibre.utils.fontconfig:main',
|
||||
'calibre-parallel = calibre.parallel:main',
|
||||
'calibre-customize = calibre.customize.ui:main',
|
||||
'pdftrim = calibre.ebooks.pdf.pdftrim:main' ,
|
||||
],
|
||||
'gui_scripts' : [
|
||||
__appname__+' = calibre.gui2.main:main',
|
||||
'lrfviewer = calibre.gui2.lrf_renderer.main:main',
|
||||
'ebook-viewer = calibre.gui2.viewer.main:main',
|
||||
__appname__+' = calibre.gui2.main:main',
|
||||
'lrfviewer = calibre.gui2.lrf_renderer.main:main',
|
||||
'ebook-viewer = calibre.gui2.viewer.main:main',
|
||||
],
|
||||
}
|
||||
|
||||
@ -176,7 +166,7 @@ def setup_completion(fatal_errors):
|
||||
sys.stdout.flush()
|
||||
from calibre.ebooks.lrf.html.convert_from import option_parser as htmlop
|
||||
from calibre.ebooks.lrf.txt.convert_from import option_parser as txtop
|
||||
from calibre.ebooks.lrf.meta import option_parser as metaop
|
||||
from calibre.ebooks.metadata.cli import option_parser as metaop, filetypes as meta_filetypes
|
||||
from calibre.ebooks.lrf.lrfparser import option_parser as lrf2lrsop
|
||||
from calibre.gui2.lrf_renderer.main import option_parser as lrfviewerop
|
||||
from calibre.ebooks.lrf.pdf.reflow import option_parser as pdfhtmlop
|
||||
@ -185,7 +175,6 @@ def setup_completion(fatal_errors):
|
||||
from calibre.web.feeds.main import option_parser as feeds2disk
|
||||
from calibre.web.feeds.recipes import titles as feed_titles
|
||||
from calibre.ebooks.lrf.feeds.convert_from import option_parser as feeds2lrf
|
||||
from calibre.ebooks.metadata.epub import option_parser as epub_meta
|
||||
from calibre.ebooks.lrf.comic.convert_from import option_parser as comicop
|
||||
from calibre.ebooks.epub.from_html import option_parser as html2epub
|
||||
from calibre.ebooks.html import option_parser as html2oeb
|
||||
@ -224,21 +213,14 @@ def setup_completion(fatal_errors):
|
||||
f.write(opts_and_exts('any2mobi', any2mobi, any_formats))
|
||||
f.write(opts_and_exts('oeb2mobi', oeb2mobi, ['opf']))
|
||||
f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf']))
|
||||
f.write(opts_and_exts('lrf-meta', metaop, ['lrf']))
|
||||
f.write(opts_and_exts('rtf-meta', metaop, ['rtf']))
|
||||
f.write(opts_and_exts('pdf-meta', metaop, ['pdf']))
|
||||
f.write(opts_and_exts('lit-meta', metaop, ['lit']))
|
||||
f.write(opts_and_exts('imp-meta', metaop, ['imp']))
|
||||
f.write(opts_and_exts('rb-meta', metaop, ['rb']))
|
||||
f.write(opts_and_exts('opf-meta', metaop, ['opf']))
|
||||
f.write(opts_and_exts('odt-meta', metaop, ['odt', 'ods', 'odf', 'odg', 'odp']))
|
||||
f.write(opts_and_exts('epub-meta', epub_meta, ['epub']))
|
||||
f.write(opts_and_exts('ebook-meta', metaop, list(meta_filetypes())))
|
||||
f.write(opts_and_exts('lrfviewer', lrfviewerop, ['lrf']))
|
||||
f.write(opts_and_exts('pdfrelow', pdfhtmlop, ['pdf']))
|
||||
f.write(opts_and_exts('mobi2oeb', mobioeb, ['mobi', 'prc']))
|
||||
f.write(opts_and_exts('lit2oeb', lit2oeb, ['lit']))
|
||||
f.write(opts_and_exts('comic2lrf', comicop, ['cbz', 'cbr']))
|
||||
f.write(opts_and_exts('comic2epub', comic2epub, ['cbz', 'cbr']))
|
||||
f.write(opts_and_exts('comic2mobi', comic2epub, ['cbz', 'cbr']))
|
||||
f.write(opts_and_exts('comic2pdf', comic2epub, ['cbz', 'cbr']))
|
||||
f.write(opts_and_words('feeds2disk', feeds2disk, feed_titles))
|
||||
f.write(opts_and_words('feeds2lrf', feeds2lrf, feed_titles))
|
||||
@ -421,10 +403,8 @@ def install_man_pages(fatal_errors):
|
||||
os.environ['PATH'] += ':'+os.path.expanduser('~/bin')
|
||||
for src in entry_points['console_scripts']:
|
||||
prog = src[:src.index('=')].strip()
|
||||
if prog in ('prs500', 'pdf-meta', 'epub-meta', 'lit-meta',
|
||||
'markdown-calibre', 'calibre-debug', 'fb2-meta',
|
||||
'calibre-fontconfig', 'calibre-parallel', 'odt-meta',
|
||||
'rb-meta', 'imp-meta', 'mobi-meta'):
|
||||
if prog in ('ebook-device', 'markdown-calibre',
|
||||
'calibre-fontconfig', 'calibre-parallel'):
|
||||
continue
|
||||
|
||||
help2man = ('help2man', prog, '--name', 'part of %s'%__appname__,
|
||||
@ -484,14 +464,14 @@ def post_install():
|
||||
os.unlink(f)
|
||||
|
||||
def binary_install():
|
||||
manifest = os.path.join(sys.frozen_path, 'manifest')
|
||||
manifest = os.path.join(getattr(sys, 'frozen_path'), 'manifest')
|
||||
exes = [x.strip() for x in open(manifest).readlines()]
|
||||
print 'Creating symlinks...'
|
||||
for exe in exes:
|
||||
dest = os.path.join('/usr', 'bin', exe)
|
||||
if os.path.exists(dest):
|
||||
os.remove(dest)
|
||||
os.symlink(os.path.join(sys.frozen_path, exe), dest)
|
||||
os.symlink(os.path.join(getattr(sys, 'frozen_path'), exe), dest)
|
||||
post_install()
|
||||
return 0
|
||||
|
||||
|
@ -19,35 +19,35 @@ What formats does |app| support conversion to/from?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|app| supports the conversion of the following formats:
|
||||
|
||||
+----------------------------+------------------------------------------+
|
||||
| | **Output formats** |
|
||||
| +------------------+-----------------------+
|
||||
| | EPUB | LRF |
|
||||
+===================+========+==================+=======================+
|
||||
| | MOBI | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | LIT | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | PRC | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | EPUB | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | ODT | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | HTML | ✔ | ✔ |
|
||||
| | | | |
|
||||
| **Input formats** | CBR | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | CBZ | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | RTF | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | TXT | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | PDF | ✔ | ✔ |
|
||||
| | | | |
|
||||
| | LRS | | ✔ |
|
||||
+-------------------+--------+------------------+-----------------------+
|
||||
+----------------------------+------------------------------------------------------------------+
|
||||
| | **Output formats** |
|
||||
| +------------------+-----------------------+-----------------------+
|
||||
| | EPUB | LRF | MOBI |
|
||||
+===================+========+==================+=======================+=======================+
|
||||
| | MOBI | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | LIT | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | PRC | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | EPUB | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | ODT | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | HTML | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| **Input formats** | CBR | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | CBZ | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | RTF | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | TXT | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | PDF | ✔ | ✔ | ✔ |
|
||||
| | | | | |
|
||||
| | LRS | | ✔ | |
|
||||
+-------------------+--------+------------------+-----------------------+-----------------------+
|
||||
|
||||
|
||||
|
||||
|
@ -158,21 +158,27 @@ class WorkerMother(object):
|
||||
self.executable = os.path.join(os.path.dirname(sys.executable),
|
||||
'calibre-parallel.exe' if isfrozen else 'Scripts\\calibre-parallel.exe')
|
||||
elif isosx:
|
||||
self.executable = sys.executable
|
||||
self.executable = self.gui_executable = sys.executable
|
||||
self.prefix = ''
|
||||
if isfrozen:
|
||||
fd = getattr(sys, 'frameworks_dir')
|
||||
contents = os.path.dirname(fd)
|
||||
self.gui_executable = os.path.join(contents, 'MacOS',
|
||||
os.path.basename(sys.executable))
|
||||
contents = os.path.join(contents, 'console.app', 'Contents')
|
||||
self.executable = os.path.join(contents, 'MacOS',
|
||||
os.path.basename(sys.executable))
|
||||
|
||||
resources = os.path.join(contents, 'Resources')
|
||||
fd = os.path.join(contents, 'Frameworks')
|
||||
sp = os.path.join(resources, 'lib', 'python'+sys.version[:3], 'site-packages.zip')
|
||||
|
||||
self.prefix += 'import sys; sys.frameworks_dir = "%s"; sys.frozen = "macosx_app"; '%fd
|
||||
self.prefix += 'sys.path.insert(0, %s); '%repr(sp)
|
||||
if fd not in os.environ['PATH']:
|
||||
self.env['PATH'] = os.environ['PATH']+':'+fd
|
||||
self.env['PYTHONHOME'] = resources
|
||||
self.env['MAGICK_HOME'] = os.path.join(getattr(sys, 'frameworks_dir'), 'ImageMagick')
|
||||
self.env['DYLD_LIBRARY_PATH'] = os.path.join(getattr(sys, 'frameworks_dir'), 'ImageMagick', 'lib')
|
||||
self.env['MAGICK_HOME'] = os.path.join(fd, 'ImageMagick')
|
||||
self.env['DYLD_LIBRARY_PATH'] = os.path.join(fd, 'ImageMagick', 'lib')
|
||||
else:
|
||||
self.executable = os.path.join(getattr(sys, 'frozen_path'), 'calibre-parallel') \
|
||||
if isfrozen else 'calibre-parallel'
|
||||
@ -186,7 +192,7 @@ class WorkerMother(object):
|
||||
for func in ('spawn_free_spirit', 'spawn_worker'):
|
||||
setattr(self, func, getattr(self, func+'_'+ext))
|
||||
|
||||
|
||||
|
||||
def cleanup_child_windows(self, child, name=None, fd=None):
|
||||
try:
|
||||
child.kill()
|
||||
@ -219,7 +225,8 @@ class WorkerMother(object):
|
||||
|
||||
def spawn_free_spirit_osx(self, arg, type='free_spirit'):
|
||||
script = 'from calibre.parallel import main; main(args=["calibre-parallel", %s]);'%repr(arg)
|
||||
cmdline = [self.executable, '-c', self.prefix+script]
|
||||
exe = self.gui_executable if type == 'free_spirit' else self.executable
|
||||
cmdline = [exe, '-c', self.prefix+script]
|
||||
child = WorkerStatus(subprocess.Popen(cmdline, env=self.get_env()))
|
||||
atexit.register(self.cleanup_child_linux, child)
|
||||
return child
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -97,7 +97,7 @@ class OptionParser(_OptionParser):
|
||||
|
||||
def merge(self, parser):
|
||||
'''
|
||||
Add options from parser to self. In case of conflicts, confilicting options from
|
||||
Add options from parser to self. In case of conflicts, conflicting options from
|
||||
parser are skipped.
|
||||
'''
|
||||
opts = list(parser.option_list)
|
||||
@ -224,6 +224,8 @@ class OptionSet(object):
|
||||
def update(self, other):
|
||||
for name in other.groups.keys():
|
||||
self.groups[name] = other.groups[name]
|
||||
if name not in self.group_list:
|
||||
self.group_list.append(name)
|
||||
for pref in other.preferences:
|
||||
if pref in self.preferences:
|
||||
self.preferences.remove(pref)
|
||||
|
@ -23,7 +23,8 @@ recipe_modules = ['recipe_' + r for r in (
|
||||
'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet',
|
||||
'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de',
|
||||
'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche', 'the_age',
|
||||
'laprensa',
|
||||
'laprensa', 'amspec', 'freakonomics', 'criticadigital', 'elcronista',
|
||||
'shacknews', 'teleread',
|
||||
)]
|
||||
|
||||
import re, imp, inspect, time, os
|
||||
|
53
src/calibre/web/feeds/recipes/recipe_amspec.py
Normal file
53
src/calibre/web/feeds/recipes/recipe_amspec.py
Normal file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
spectator.org
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class TheAmericanSpectator(BasicNewsRecipe):
|
||||
title = 'The American Spectator'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'news from USA'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
INDEX = 'http://spectator.org'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , 'news, politics, USA'
|
||||
, '--publisher' , title
|
||||
]
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':'post inner'})
|
||||
,dict(name='div', attrs={'class':'author-bio'})
|
||||
]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='object')
|
||||
,dict(name='div', attrs={'class':'col3' })
|
||||
,dict(name='div', attrs={'class':'post-options' })
|
||||
,dict(name='p' , attrs={'class':'letter-editor'})
|
||||
,dict(name='div', attrs={'class':'social' })
|
||||
]
|
||||
|
||||
feeds = [ (u'Articles', u'http://feedproxy.google.com/amspecarticles')]
|
||||
|
||||
def get_cover_url(self):
|
||||
cover_url = None
|
||||
soup = self.index_to_soup(self.INDEX)
|
||||
link_item = soup.find('a',attrs={'class':'cover'})
|
||||
if link_item:
|
||||
soup2 = self.index_to_soup(link_item['href'])
|
||||
link_item2 = soup2.find('div',attrs={'class':'post inner issues'})
|
||||
cover_url = self.INDEX + link_item2.img['src']
|
||||
return cover_url
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '/print'
|
60
src/calibre/web/feeds/recipes/recipe_criticadigital.py
Normal file
60
src/calibre/web/feeds/recipes/recipe_criticadigital.py
Normal file
@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
criticadigital.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class CriticaDigital(BasicNewsRecipe):
|
||||
title = 'Critica de la Argentina'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Noticias de Argentina'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = 'cp1252'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , 'news, Argentina'
|
||||
, '--publisher' , title
|
||||
]
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':'bloqueTitulosNoticia'})
|
||||
,dict(name='div', attrs={'id':'c453-1' })
|
||||
]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':'box300' })
|
||||
,dict(name='div', style=True )
|
||||
,dict(name='div', attrs={'class':'titcomentario'})
|
||||
,dict(name='div', attrs={'class':'comentario' })
|
||||
,dict(name='div', attrs={'class':'paginador' })
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Politica', u'http://www.criticadigital.com/herramientas/rss.php?ch=politica' )
|
||||
,(u'Economia', u'http://www.criticadigital.com/herramientas/rss.php?ch=economia' )
|
||||
,(u'Deportes', u'http://www.criticadigital.com/herramientas/rss.php?ch=deportes' )
|
||||
,(u'Espectaculos', u'http://www.criticadigital.com/herramientas/rss.php?ch=espectaculos')
|
||||
,(u'Mundo', u'http://www.criticadigital.com/herramientas/rss.php?ch=mundo' )
|
||||
,(u'Policiales', u'http://www.criticadigital.com/herramientas/rss.php?ch=policiales' )
|
||||
,(u'Sociedad', u'http://www.criticadigital.com/herramientas/rss.php?ch=sociedad' )
|
||||
,(u'Salud', u'http://www.criticadigital.com/herramientas/rss.php?ch=salud' )
|
||||
,(u'Tecnologia', u'http://www.criticadigital.com/herramientas/rss.php?ch=tecnologia' )
|
||||
,(u'Santa Fe', u'http://www.criticadigital.com/herramientas/rss.php?ch=santa_fe' )
|
||||
]
|
||||
|
||||
def get_cover_url(self):
|
||||
cover_url = None
|
||||
index = 'http://www.criticadigital.com/impresa/'
|
||||
soup = self.index_to_soup(index)
|
||||
link_item = soup.find('div',attrs={'class':'tapa'})
|
||||
if link_item:
|
||||
cover_url = index + link_item.img['src']
|
||||
return cover_url
|
70
src/calibre/web/feeds/recipes/recipe_elcronista.py
Normal file
70
src/calibre/web/feeds/recipes/recipe_elcronista.py
Normal file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
cronista.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ElCronista(BasicNewsRecipe):
|
||||
title = 'El Cronista'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Noticias de Argentina'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = 'cp1252'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , 'news, Argentina'
|
||||
, '--publisher' , title
|
||||
]
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='table', attrs={'width':'100%' })
|
||||
,dict(name='h1' , attrs={'class':'Arialgris16normal'})
|
||||
]
|
||||
|
||||
remove_tags = [dict(name='a', attrs={'class':'Arialazul12'})]
|
||||
|
||||
feeds = [
|
||||
(u'Economia' , u'http://www.cronista.com/adjuntos/8/rss/Economia_EI.xml' )
|
||||
,(u'Negocios' , u'http://www.cronista.com/adjuntos/8/rss/negocios_EI.xml' )
|
||||
,(u'Ultimo momento' , u'http://www.cronista.com/adjuntos/8/rss/ultimo_momento.xml' )
|
||||
,(u'Finanzas y Mercados' , u'http://www.cronista.com/adjuntos/8/rss/Finanzas_Mercados_EI.xml' )
|
||||
,(u'Financial Times' , u'http://www.cronista.com/adjuntos/8/rss/FT_EI.xml' )
|
||||
,(u'Opinion edicion impresa' , u'http://www.cronista.com/adjuntos/8/rss/opinion_edicion_impresa.xml' )
|
||||
,(u'Socialmente Responsables', u'http://www.cronista.com/adjuntos/8/rss/Socialmente_Responsables.xml')
|
||||
,(u'Asuntos Legales' , u'http://www.cronista.com/adjuntos/8/rss/asuntoslegales.xml' )
|
||||
,(u'IT Business' , u'http://www.cronista.com/adjuntos/8/rss/itbusiness.xml' )
|
||||
,(u'Management y RR.HH.' , u'http://www.cronista.com/adjuntos/8/rss/management.xml' )
|
||||
,(u'Inversiones Personales' , u'http://www.cronista.com/adjuntos/8/rss/inversionespersonales.xml' )
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
main, sep, rest = url.partition('.com/notas/')
|
||||
article_id, lsep, rrest = rest.partition('-')
|
||||
return 'http://www.cronista.com/interior/index.php?p=imprimir_nota&idNota=' + article_id
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
|
||||
soup.head.insert(0,mtag)
|
||||
soup.head.base.extract()
|
||||
htext = soup.find('h1',attrs={'class':'Arialgris16normal'})
|
||||
htext.name = 'p'
|
||||
soup.prettify()
|
||||
return soup
|
||||
|
||||
def get_cover_url(self):
|
||||
cover_url = None
|
||||
index = 'http://www.cronista.com/contenidos/'
|
||||
soup = self.index_to_soup(index + 'ee.html')
|
||||
link_item = soup.find('a',attrs={'href':"javascript:Close()"})
|
||||
if link_item:
|
||||
cover_url = index + link_item.img['src']
|
||||
return cover_url
|
||||
|
@ -11,7 +11,7 @@ class FazNet(BasicNewsRecipe):
|
||||
|
||||
title = 'FAZ NET'
|
||||
__author__ = 'Kovid Goyal'
|
||||
description = 'News from Germany'
|
||||
description = '"Frankfurter Allgemeine Zeitung'
|
||||
use_embedded_content = False
|
||||
max_articles_per_feed = 30
|
||||
|
||||
|
20
src/calibre/web/feeds/recipes/recipe_freakonomics.py
Normal file
20
src/calibre/web/feeds/recipes/recipe_freakonomics.py
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Freakonomics(BasicNewsRecipe):
|
||||
|
||||
title = 'Freakonomics Blog'
|
||||
description = 'The Hidden side of everything'
|
||||
__author__ = 'Kovid Goyal'
|
||||
|
||||
feeds = [('Blog', 'http://freakonomics.blogs.nytimes.com/feed/atom/')]
|
||||
|
||||
def get_article_url(self, article):
|
||||
return article.get('feedburner_origlink', None)
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?pagemode=print'
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user