sync with Kovid's branch

This commit is contained in:
Tomasz Długosz 2013-03-21 00:12:52 +01:00
commit c7635f262c
26 changed files with 1248 additions and 225 deletions

View File

@ -11,12 +11,10 @@ class AdvancedUserRecipe1295262156(BasicNewsRecipe):
auto_cleanup = True
encoding='iso-8859-1'
feeds = [(u'kath.net', u'http://www.kath.net/2005/xml/index.xml')]
def print_version(self, url):
return url+"&print=yes"
return url+"/print/yes"
extra_css = 'td.textb {font-size: medium;}'

View File

@ -1,13 +0,0 @@
from calibre.web.feeds.news import CalibrePeriodical
class MiDDay(CalibrePeriodical):
title = 'MiDDay'
calibre_periodicals_slug = 'midday'
description = '''Get your dose of the latest news, views and fun - from the
world of politics, sports and Bollywood to the cartoons, comics and games of
the entertainment section - Indias leading tabloid has it all. To subscribe
visit <a href="http://news.calibre-ebook.com/periodical/midday">calibre
Periodicals</a>.'''
language = 'en_IN'

View File

@ -35,7 +35,10 @@ class NewYorkTimesBookReview(BasicNewsRecipe):
continue
if x['class'] in {'story', 'ledeStory'}:
tt = 'h3' if x['class'] == 'story' else 'h1'
a = x.find(tt).find('a', href=True)
try:
a = x.find(tt).find('a', href=True)
except AttributeError:
continue
title = self.tag_to_string(a)
url = a['href'] + '&pagewanted=all'
self.log('\tFound article:', title, url)

View File

@ -413,16 +413,16 @@ class WAYTEQ(USBMS):
name = 'WayteQ device interface'
gui_name = 'WayteQ xBook'
description = _('Communicate with the WayteQ Reader')
description = _('Communicate with the WayteQ and SPC Dickens Readers')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
FORMATS = ['epub', 'mobi', 'prc', 'fb2', 'txt', 'pdf', 'html', 'rtf', 'chm', 'djvu', 'doc']
VENDOR_ID = [0x05e3]
PRODUCT_ID = [0x0726]
BCD = [0x0222]
VENDOR_ID = [0x05e3, 0x0c45]
PRODUCT_ID = [0x0726, 0x0184]
BCD = [0x0222, 0x0100]
EBOOK_DIR_MAIN = 'Documents'
SCAN_FROM_ROOT = True
@ -431,6 +431,14 @@ class WAYTEQ(USBMS):
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'RK28_SDK_DEMO'
SUPPORTS_SUB_DIRS = True
def get_gui_name(self):
try:
if self.detected_device.idVendor == 0x0c45:
return 'SPC Dickens'
except Exception:
pass
return self.gui_name
def get_carda_ebook_dir(self, for_upload=False):
if for_upload:
return 'Documents'

View File

@ -11,18 +11,6 @@
const calibre_device_entry_t calibre_mtp_device_table[] = {
#include "upstream/music-players.h"
// Amazon Kindle Fire HD
, { "Amazon", 0x1949, "Fire HD", 0x0007, DEVICE_FLAGS_ANDROID_BUGS}
, { "Amazon", 0x1949, "Fire HD", 0x0008, DEVICE_FLAGS_ANDROID_BUGS}
, { "Amazon", 0x1949, "Fire HD", 0x000a, DEVICE_FLAGS_ANDROID_BUGS}
// Nexus 10
, { "Google", 0x18d1, "Nexus 10", 0x4ee2, DEVICE_FLAGS_ANDROID_BUGS}
, { "Google", 0x18d1, "Nexus 10", 0x4ee1, DEVICE_FLAGS_ANDROID_BUGS}
// Kobo Arc
, { "Kobo", 0x2237, "Arc", 0xd108, DEVICE_FLAGS_ANDROID_BUGS}
, { NULL, 0xffff, NULL, 0xffff, DEVICE_FLAG_NONE }
};

View File

@ -294,6 +294,13 @@
DEVICE_FLAG_UNIQUE_FILENAMES |
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST },
// The "YP-R2" (0x04e8/0x512d) is NOT MTP, it is UMS only.
// Guessing on device flags for the MTP mode...
{ "Samsung", 0x04e8, "YP-R2", 0x512e,
DEVICE_FLAG_UNLOAD_DRIVER |
DEVICE_FLAG_OGG_IS_UNKNOWN |
DEVICE_FLAG_UNIQUE_FILENAMES |
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_PLAYLIST_SPL_V1 },
// From Manuel Carro
// Copied from Q2
{ "Samsung", 0x04e8, "YP-Q3", 0x5130,
@ -309,6 +316,7 @@
DEVICE_FLAG_OGG_IS_UNKNOWN |
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_PLAYLIST_SPL_V1 },
// YP-F3 is NOT MTP - USB mass storage
// From a rouge .INF file
// this device ID seems to have been recycled for:
// the Samsung SGH-A707 Cingular cellphone
@ -393,7 +401,9 @@
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_UNLOAD_DRIVER |
DEVICE_FLAG_LONG_TIMEOUT |
DEVICE_FLAG_PROPLIST_OVERRIDES_OI },
DEVICE_FLAG_PROPLIST_OVERRIDES_OI |
DEVICE_FLAG_OGG_IS_UNKNOWN |
DEVICE_FLAG_FLAC_IS_UNKNOWN },
// Reported by David Goodenough <dfgdga@users.sourceforge.net>
// Guessing on flags.
{ "Samsung", 0x04e8, "Galaxy Y", 0x685e,
@ -401,14 +411,18 @@
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_UNLOAD_DRIVER |
DEVICE_FLAG_LONG_TIMEOUT |
DEVICE_FLAG_PROPLIST_OVERRIDES_OI },
DEVICE_FLAG_PROPLIST_OVERRIDES_OI |
DEVICE_FLAG_OGG_IS_UNKNOWN |
DEVICE_FLAG_FLAC_IS_UNKNOWN },
{ "Samsung", 0x04e8,
"Galaxy models (MTP)", 0x6860,
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL |
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_UNLOAD_DRIVER |
DEVICE_FLAG_LONG_TIMEOUT |
DEVICE_FLAG_PROPLIST_OVERRIDES_OI },
DEVICE_FLAG_PROPLIST_OVERRIDES_OI |
DEVICE_FLAG_OGG_IS_UNKNOWN |
DEVICE_FLAG_FLAC_IS_UNKNOWN },
// From: Erik Berglund <erikjber@users.sourceforge.net>
// Logs indicate this needs DEVICE_FLAG_NO_ZERO_READS
// No Samsung platlists on this device.
@ -419,7 +433,9 @@
{ "Samsung", 0x04e8, "Galaxy models Kies mode", 0x6877,
DEVICE_FLAG_UNLOAD_DRIVER |
DEVICE_FLAG_LONG_TIMEOUT |
DEVICE_FLAG_PROPLIST_OVERRIDES_OI },
DEVICE_FLAG_PROPLIST_OVERRIDES_OI |
DEVICE_FLAG_OGG_IS_UNKNOWN |
DEVICE_FLAG_FLAC_IS_UNKNOWN },
// From: John Gorkos <ab0oo@users.sourceforge.net> and
// Akos Maroy <darkeye@users.sourceforge.net>
{ "Samsung", 0x04e8, "Vibrant SGH-T959/Captivate/Media player mode", 0x68a9,
@ -439,7 +455,6 @@
*/
{ "Microsoft/Intel", 0x045e, "Bandon Portable Media Center", 0x00c9,
DEVICE_FLAG_NONE },
// Reported by anonymous sourceforge user
// HTC Mozart is using the PID, as is Nokia Lumia 800
// May need MTPZ to work
{ "Microsoft", 0x045e, "Windows Phone", 0x04ec, DEVICE_FLAG_NONE },
@ -450,12 +465,12 @@
{ "Microsoft", 0x045e, "Windows MTP Simulator", 0x0622, DEVICE_FLAG_NONE },
// Reported by Edward Hutchins (used for Zune HDs)
{ "Microsoft", 0x045e, "Zune HD", 0x063e, DEVICE_FLAG_NONE },
// Reported by anonymous sourceforge user
{ "Microsoft", 0x045e, "Kin 1", 0x0640, DEVICE_FLAG_NONE },
// Reported by anonymous sourceforge user
{ "Microsoft/Sharp/nVidia", 0x045e, "Kin TwoM", 0x0641, DEVICE_FLAG_NONE },
// Reported by Farooq Zaman (used for all Zunes)
{ "Microsoft", 0x045e, "Zune", 0x0710, DEVICE_FLAG_NONE },
// Reported by Olegs Jeremejevs
{ "Microsoft/HTC", 0x045e, "HTC 8S", 0xf0ca, DEVICE_FLAG_NONE },
/*
* JVC
@ -517,33 +532,52 @@
// From Anonymous SourceForge User
{ "Philips", 0x0471, "GoGear Vibe/02", 0x20e5,
DEVICE_FLAG_UNLOAD_DRIVER },
// Reported by Philip Rhoades
{ "Philips", 0x0471, "GoGear Ariaz/97", 0x2138,
DEVICE_FLAG_UNLOAD_DRIVER },
// from XNJB user
{ "Philips", 0x0471, "PSA235", 0x7e01, DEVICE_FLAG_NONE },
/*
* Acer
* Reporters:
* Franck VDL <franckv@users.sourceforge.net>
* Matthias Arndt <simonsunnyboy@users.sourceforge.net>
* Arvin Schnell <arvins@users.sourceforge.net>
* Philippe Marzouk <philm@users.sourceforge.net>
* nE0sIghT <ne0sight@users.sourceforge.net>
* Maxime de Roucy <maxime1986@users.sourceforge.net>
*/
// Reported by anonymous sourceforge user
{ "Acer", 0x0502, "Iconia TAB A500 (ID1)", 0x3325, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by: Franck VDL <franckv@users.sourceforge.net>
{ "Acer", 0x0502, "Iconia TAB A500 (ID2)", 0x3341, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by: Matthias Arndt <simonsunnyboy@users.sourceforge.net>
{ "Acer", 0x0502, "Iconia TAB A501", 0x3344, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by: anonymous sourceforge user
{ "Acer", 0x0502, "Iconia TAB A100 (ID1)", 0x3348, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by: Arvin Schnell <arvins@users.sourceforge.net>
{ "Acer", 0x0502, "Iconia TAB A100 (ID2)", 0x3349, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Philippe Marzouk <philm@users.sourceforge.net>
{ "Acer", 0x0502, "Iconia TAB A700", 0x3378, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous sourceforge user
{ "Acer", 0x0502, "Iconia TAB A200 (ID1)", 0x337c, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous sourceforge user
{ "Acer", 0x0502, "Iconia TAB A200 (ID2)", 0x337d, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by nE0sIghT <ne0sight@users.sourceforge.net>
{ "Acer", 0x0502, "Iconia TAB A510", 0x338a, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Maxime de Roucy <maxime1986@users.sourceforge.net>
{ "Acer", 0x0502, "Iconia TAB A500 (ID1)", 0x3325,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A500 (ID2)", 0x3341,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A501 (ID1)", 0x3344,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A501 (ID2)", 0x3345,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A100 (ID1)", 0x3348,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A100 (ID2)", 0x3349,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A700", 0x3378,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A200 (ID1)", 0x337c,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A200 (ID2)", 0x337d,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A510 (ID1)", 0x3389,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A510 (ID2)", 0x338a,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "E350 Liquid Gallant Duo", 0x33c3,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A210", 0x33cb,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Acer", 0x0502, "Iconia TAB A110", 0x33d8,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* SanDisk
@ -743,7 +777,11 @@
{ "iRiver", 0x4102, "E50", 0x1151,
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS |
DEVICE_FLAG_OGG_IS_UNKNOWN },
// Reported by Jakub Matraszek <jakub.matraszek@gmail.com>
// Reported by anonymous SourceForge user, guessing on flags
{ "iRiver", 0x4102, "E150", 0x1152,
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST | DEVICE_FLAG_NO_ZERO_READS |
DEVICE_FLAG_OGG_IS_UNKNOWN },
// Reported by Jakub Matraszek <jakub.matraszek@gmail.com>
{ "iRiver", 0x4102, "T5", 0x1153,
DEVICE_FLAG_UNLOAD_DRIVER | DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_NO_ZERO_READS | DEVICE_FLAG_OGG_IS_UNKNOWN },
@ -877,8 +915,10 @@
{ "Archos", 0x0e79, "SPOD (MTP mode)", 0x1341, DEVICE_FLAG_UNLOAD_DRIVER },
{ "Archos", 0x0e79, "5S IT (MTP mode)", 0x1351, DEVICE_FLAG_UNLOAD_DRIVER },
{ "Archos", 0x0e79, "5H IT (MTP mode)", 0x1357, DEVICE_FLAG_UNLOAD_DRIVER },
{ "Archos", 0x0e79, "Arnova Childpad", 0x1458, DEVICE_FLAG_UNLOAD_DRIVER },
// Reported by anonymous Sourceforge user
{ "Archos", 0x0e79, "Arnova Childpad", 0x1458, DEVICE_FLAGS_ANDROID_BUGS },
{ "Archos", 0x0e79, "Arnova 8c G3", 0x145e, DEVICE_FLAGS_ANDROID_BUGS },
{ "Archos", 0x0e79, "Arnova 10bG3 Tablet", 0x146b, DEVICE_FLAGS_ANDROID_BUGS },
{ "Archos", 0x0e79, "97 Xenon", 0x149a, DEVICE_FLAGS_ANDROID_BUGS },
{ "Archos", 0x0e79, "8o G9 (MTP mode)", 0x1508, DEVICE_FLAG_UNLOAD_DRIVER },
// Reported by Clément <clemvangelis@users.sourceforge.net>
{ "Archos", 0x0e79, "8o G9 Turbo (MTP mode)", 0x1509,
@ -887,14 +927,10 @@
{ "Archos", 0x0e79, "80G9", 0x1518, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Till <Till@users.sourceforge.net>
{ "Archos", 0x0e79, "101 G9", 0x1528, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous sourceforge user
{ "Archos", 0x0e79, "101 G9 (v2)", 0x1529, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous sourceforge user
{ "Archos", 0x0e79, "101 G9 Turbo 250 HD", 0x1538,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous sourceforge user
{ "Archos", 0x0e79, "101 G9 Turbo", 0x1539, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous sourceforge user
{ "Archos", 0x0e79, "70it2 (mode 1)", 0x1568, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Sebastien ROHAUT
{ "Archos", 0x0e79, "70it2 (mode 2)", 0x1569, DEVICE_FLAGS_ANDROID_BUGS },
@ -1075,6 +1111,10 @@
DEVICE_FLAG_UNLOAD_DRIVER },
// From: Maxin B. John <maxin.john@gmail.com>
{ "Nokia", 0x0421, "N9", 0x051a, DEVICE_FLAG_NONE },
{ "Nokia", 0x0421, "C5-00", 0x0592, DEVICE_FLAG_NONE },
// Reported by Sampo Savola
// Covers Lumia 920, 820 and probably any WP8 device.
{ "Nokia", 0x0421, "Nokia Lumia WP8", 0x0661, DEVICE_FLAG_NONE },
// Reported by Richard Wall <richard@the-moon.net>
{ "Nokia", 0x05c6, "5530 Xpressmusic", 0x0229, DEVICE_FLAG_NONE },
// Reported by anonymous SourceForge user
@ -1109,9 +1149,12 @@
{ "Thomson / RCA", 0x069b, "Lyra HC308A", 0x3035, DEVICE_FLAG_NONE },
/*
* NTT DoCoMo
* Fujitsu devices
*/
{ "FOMA", 0x04c5, "F903iX HIGH-SPEED", 0x1140, DEVICE_FLAG_NONE },
{ "Fujitsu, Ltd", 0x04c5, "F903iX HIGH-SPEED", 0x1140, DEVICE_FLAG_NONE },
// Reported by Thomas Bretthauer
{ "Fujitsu, Ltd", 0x04c5, "STYLISTIC M532", 0x133b,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Palm device userland program named Pocket Tunes
@ -1254,7 +1297,6 @@
/*
* LG Electronics
*/
// From anonymous SourceForge user
// Uncertain if this is really the MTP mode device ID...
{ "LG Electronics Inc.", 0x043e, "T54", 0x7040,
DEVICE_FLAG_UNLOAD_DRIVER },
@ -1271,20 +1313,20 @@
{ "LG Electronics Inc.", 0x1004, "GR-500 Music Player", 0x611b,
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_ALWAYS_PROBE_DESCRIPTOR },
// Reported by anonymous sourceforge user
{ "LG Electronics Inc.", 0x1004, "KM900", 0x6132,
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_UNLOAD_DRIVER },
// Reported by anonymous sourceforge user
{ "LG Electronics Inc.", 0x1004, "LG8575", 0x619a,
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_UNLOAD_DRIVER },
// Reported by anonymous sourceforge user
{ "LG Electronics Inc.", 0x1004, "V909 G-Slate", 0x61f9,
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_UNLOAD_DRIVER },
{ "LG Electronics Inc.", 0x1004, "LG2 Optimus", 0x6225,
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST |
DEVICE_FLAG_UNLOAD_DRIVER },
// Reported by Brian J. Murrell
{ "LG Electronics Inc.", 0x1004, "LG-E617G/P700", 0x631c,
{ "LG Electronics Inc.", 0x1004, "LG-E610/E612/E617G/E970/P700", 0x631c,
DEVICE_FLAGS_ANDROID_BUGS },
/*
@ -1294,69 +1336,69 @@
* reported to see a pattern here.
*/
// Reported by Alessandro Radaelli <alessandro.radaelli@aruba.it>
{ "Sony", 0x054c, "Walkman NWZ-A815/NWZ-A818", 0x0325,
{ "Sony", 0x054c, "NWZ-A815/NWZ-A818", 0x0325,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by anonymous Sourceforge user.
{ "Sony", 0x054c, "Walkman NWZ-S516", 0x0326,
{ "Sony", 0x054c, "NWZ-S516", 0x0326,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Endre Oma <endre.88.oma@gmail.com>
{ "Sony", 0x054c, "Walkman NWZ-S615F/NWZ-S616F/NWZ-S618F", 0x0327,
{ "Sony", 0x054c, "NWZ-S615F/NWZ-S616F/NWZ-S618F", 0x0327,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Jean-Marc Bourguet <jm@bourguet.org>
{ "Sony", 0x054c, "Walkman NWZ-S716F", 0x035a,
{ "Sony", 0x054c, "NWZ-S716F", 0x035a,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Anon SF User / Anthon van der Neut <avanderneut@avid.com>
{ "Sony", 0x054c, "Walkman NWZ-A826/NWZ-A828/NWZ-A829", 0x035b,
{ "Sony", 0x054c, "NWZ-A826/NWZ-A828/NWZ-A829", 0x035b,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Niek Klaverstijn <niekez@users.sourceforge.net>
{ "Sony", 0x054c, "Walkman NWZ-A726/NWZ-A728/NWZ-A768", 0x035c,
{ "Sony", 0x054c, "NWZ-A726/NWZ-A728/NWZ-A768", 0x035c,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Mehdi AMINI <mehdi.amini - at - ulp.u-strasbg.fr>
{ "Sony", 0x054c, "Walkman NWZ-B135", 0x036e,
{ "Sony", 0x054c, "NWZ-B135", 0x036e,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by <tiagoboldt@users.sourceforge.net>
{ "Sony", 0x054c, "Walkman NWZ-E436F", 0x0385,
{ "Sony", 0x054c, "NWZ-E436F", 0x0385,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Michael Wilkinson
{ "Sony", 0x054c, "Walkman NWZ-W202", 0x0388,
{ "Sony", 0x054c, "NWZ-W202", 0x0388,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Ondrej Sury <ondrej@sury.org>
{ "Sony", 0x054c, "Walkman NWZ-S739F", 0x038c,
{ "Sony", 0x054c, "NWZ-S739F", 0x038c,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Marco Filipe Nunes Soares Abrantes Pereira <marcopereira@ua.pt>
{ "Sony", 0x054c, "Walkman NWZ-S638F", 0x038e,
{ "Sony", 0x054c, "NWZ-S638F", 0x038e,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Elliot <orwells@users.sourceforge.net>
{ "Sony", 0x054c, "Walkman NWZ-X1050B/NWZ-X1060B",
{ "Sony", 0x054c, "NWZ-X1050B/NWZ-X1060B",
0x0397, DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Silvio J. Gutierrez <silviogutierrez@users.sourceforge.net>
{ "Sony", 0x054c, "Walkman NWZ-X1051/NWZ-X1061", 0x0398,
{ "Sony", 0x054c, "NWZ-X1051/NWZ-X1061", 0x0398,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Gregory Boddin <gregory@siwhine.net>
{ "Sony", 0x054c, "Walkman NWZ-B142F", 0x03d8,
{ "Sony", 0x054c, "NWZ-B142F", 0x03d8,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Rick Warner <rick@reptileroom.net>
{ "Sony", 0x054c, "Walkman NWZ-E344", 0x03fc,
{ "Sony", 0x054c, "NWZ-E344/E345", 0x03fc,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Jonathan Stowe <gellyfish@users.sourceforge.net>
{ "Sony", 0x054c, "Walkman NWZ-E445", 0x03fd,
{ "Sony", 0x054c, "NWZ-E445", 0x03fd,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Anonymous SourceForge user
{ "Sony", 0x054c, "Walkman NWZ-S545", 0x03fe,
{ "Sony", 0x054c, "NWZ-S545", 0x03fe,
DEVICE_FLAGS_SONY_NWZ_BUGS },
{ "Sony", 0x054c, "Walkman NWZ-A845", 0x0404,
{ "Sony", 0x054c, "NWZ-A845", 0x0404,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by anonymous SourceForge user
{ "Sony", 0x054c, "Walkman NWZ-W252B", 0x04bb,
{ "Sony", 0x054c, "NWZ-W252B", 0x04bb,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Suspect this device has strong DRM features
// See https://answers.launchpad.net/ubuntu/+source/libmtp/+question/149587
{ "Sony", 0x054c, "Walkman NWZ-B153F", 0x04be,
{ "Sony", 0x054c, "NWZ-B153F", 0x04be,
DEVICE_FLAGS_SONY_NWZ_BUGS },
{ "Sony", 0x054c, "Walkman NWZ-E354", 0x04cb,
{ "Sony", 0x054c, "NWZ-E354", 0x04cb,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Toni Burgarello
{ "Sony", 0x054c, "Walkman NWZ-S754", 0x04cc,
{ "Sony", 0x054c, "NWZ-S754", 0x04cc,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Hideki Yamane <henrich@debian.org>
{ "Sony", 0x054c, "Sony Tablet P1", 0x04d1,
@ -1364,9 +1406,7 @@
// Reported by dmiceman
{ "Sony", 0x054c, "NWZ-B163F", 0x059a,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by anonymous Sourceforge user
// guessing on device flags...
{ "Sony", 0x054c, "Walkman NWZ-E464", 0x05a6,
{ "Sony", 0x054c, "NWZ-E464", 0x05a6,
DEVICE_FLAGS_SONY_NWZ_BUGS },
// Reported by Jan Rheinlaender <jrheinlaender@users.sourceforge.net>
{ "Sony", 0x054c, "NWZ-S765", 0x05a8,
@ -1377,7 +1417,8 @@
// Reported by ghalambaz <ghalambaz@users.sourceforge.net>
{ "Sony", 0x054c, "Sony Tablet S1", 0x05b4,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Anonymous SourceForge user
{ "Sony", 0x054c, "NWZ-B173F", 0x0689,
DEVICE_FLAGS_SONY_NWZ_BUGS },
{ "Sony", 0x054c, "DCR-SR75", 0x1294,
DEVICE_FLAGS_SONY_NWZ_BUGS },
@ -1496,6 +1537,7 @@
* Jean-François B. <changi67@users.sourceforge.net>
* Eduard Bloch <blade@debian.org>
* Ah Hong <hongster@users.sourceforge.net>
* Eowyn Carter
*/
{ "SonyEricsson", 0x0fce, "LT15i Xperia arc S MTP", 0x014f,
DEVICE_FLAG_NONE },
@ -1503,6 +1545,8 @@
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "MK16i Xperia MTP", 0x015a,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "R800/R88i Xperia Play MTP", 0x015d,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "ST18a Xperia Ray MTP", 0x0161,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "SK17i Xperia Mini Pro MTP", 0x0166,
@ -1533,12 +1577,26 @@
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "ST27i/ST27a Xperia go MTP", 0x017e,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "ST23i Xperia Miro MTP", 0x0180,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "SO-05D Xperia SX MTP", 0x0181,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "LT30p Xperia T MTP", 0x0182,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "LT25i Xperia V MTP", 0x0186,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia J MTP", 0x0188,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia ZL MTP", 0x0189,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia E MTP", 0x018c,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Tablet Z MTP", 0x018D,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Z MTP", 0x0193,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Tablet Z MTP", 0x0194,
DEVICE_FLAG_NONE },
/*
* MTP+UMS personalities of MTP devices (see above)
@ -1565,12 +1623,26 @@
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "ST27i/ST27a Xperia go MTP+CDROM", 0x417e,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "ST23i Xperia Miro MTP+CDROM", 0x4180,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "SO-05D Xperia SX MTP+CDROM", 0x4181,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "LT30p Xperia T MTP+CDROM", 0x4182,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "LT25i Xperia V MTP+CDROM", 0x4186,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia J MTP+CDROM", 0x4188,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia ZL MTP", 0x4189,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia E MTP+CDROM", 0x418c,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Tablet Z MTP", 0x418d,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Z MTP", 0x4193,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Tablet Z MTP", 0x4194,
DEVICE_FLAG_NONE },
/*
* MTP+ADB personalities of MTP devices (see above)
@ -1579,20 +1651,20 @@
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "MT11i Xperia Neo MTP+ADB", 0x5156,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "ST17i Xperia Active MTP+ADB", 0x5168,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "LT26i Xperia S MTP+ADB", 0x5169,
DEVICE_FLAG_NO_ZERO_READS },
{ "SonyEricsson", 0x0fce, "MK16i Xperia MTP+ADB", 0x515a,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "R800/R88i Xperia Play MTP+ADB", 0x515d,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "ST18i Xperia Ray MTP+ADB", 0x5161,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "SK17i Xperia Mini Pro MTP+ADB", 0x5166,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "ST15i Xperia Mini MTP+ADB", 0x5167,
DEVICE_FLAG_NONE },
{ "SonyEricsson", 0x0fce, "LT26i Xperia S MTP+ADB", 0x5169,
{ "SonyEricsson", 0x0fce, "ST17i Xperia Active MTP+ADB", 0x5168,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "LT26i Xperia S MTP+ADB", 0x5169,
DEVICE_FLAG_NO_ZERO_READS },
{ "SonyEricsson", 0x0fce, "SK17i Xperia Mini Pro MTP+ADB", 0x516d,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "ST21i Xperia Tipo MTP+ADB", 0x5170,
@ -1613,12 +1685,26 @@
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "ST27i/ST27a Xperia go MTP+ADB", 0x517e,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "ST23i Xperia Miro MTP+ADB", 0x5180,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "SO-05D Xperia SX MTP+ADB", 0x5181,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "LT30p Xperia T MTP+ADB", 0x5182,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "LT25i Xperia V MTP+ADB", 0x5186,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia J MTP+ADB", 0x5188,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia ZL MTP", 0x5189,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia E MTP+ADB", 0x518c,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Tablet Z MTP", 0x518d,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Z MTP", 0x5193,
DEVICE_FLAG_NONE },
{ "SONY", 0x0fce, "Xperia Tablet Z MTP", 0x5194,
DEVICE_FLAG_NONE },
/*
* MTP+UMS modes
@ -1661,17 +1747,23 @@
{ "Motorola", 0x22b8, "V3m/V750 verizon", 0x2a65,
DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST |
DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL },
{ "Motorola", 0x22b8, "Atrix/Razr HD (MTP)", 0x2e32,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Motorola", 0x22b8, "Atrix/Razr HD (MTP+ADB)", 0x2e33,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Motorola", 0x22b8, "RAZR M (XT907)", 0x2e51,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Jader Rodrigues Simoes <jadersimoes@users.sourceforge.net>
{ "Motorola", 0x22b8, "Xoom 2 Media Edition (ID2)", 0x41cf,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Steven Roemen <sdroemen@users.sourceforge.net>
{ "Motorola", 0x22b8, "Droid X/MB525 (Defy)", 0x41d6,
DEVICE_FLAG_NONE },
// Reported by anonymous user
{ "Motorola", 0x22b8, "DROID2 (ID1)", 0x41da,
DEVICE_FLAG_NONE },
{ "Motorola", 0x22b8, "Milestone / Verizon Droid", 0x41dc,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous user
{ "Motorola", 0x22b8, "DROID2", 0x42a7,
{ "Motorola", 0x22b8, "DROID2 (ID2)", 0x42a7,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Motorola", 0x22b8, "Xoom 2 Media Edition", 0x4311,
DEVICE_FLAGS_ANDROID_BUGS },
@ -1693,6 +1785,9 @@
// Reported by anonymous user
{ "Motorola", 0x22b8, "RAZR2 V8/U9/Z6", 0x6415,
DEVICE_FLAG_BROKEN_SET_OBJECT_PROPLIST },
// Reported by Brian Dolbec <dol-sen@users.sourceforge.net>
{ "Motorola", 0x22b8, "Atrix MB860 (MTP)", 0x7088,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Motorola Xoom (Wingray) variants
*
@ -1719,9 +1814,15 @@
DEVICE_FLAGS_ANDROID_BUGS },
{ "Motorola", 0x22b8, "Xoom (MTP+ADB)", 0x70a9,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous Sourceforge user
// "carried by C Spire and other CDMA US carriers"
{ "Motorola", 0x22b8, "Milestone X2", 0x70ca, DEVICE_FLAGS_ANDROID_BUGS },
{ "Motorola", 0x22b8, "Milestone X2", 0x70ca,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Motorola", 0x22b8, "XT890/907 (MTP)", 0x710d,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Motorola", 0x22b8, "XT890/907 (MTP+ADB)", 0x710e,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Motorola", 0x22b8, "XT890/907 (MTP+?)", 0x710f,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Google
@ -1729,23 +1830,32 @@
* road to produce an Android tablet it seems... The Vendor ID
* was originally used for Nexus phones
*/
{ "Google Inc (for Ainol Novo)", 0x18d1, "Fire/Flame", 0x0007,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Google Inc (for Sony)", 0x18d1, "S1", 0x05b3,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous Sourceforge user
{ "Google Inc (for Barnes & Noble)", 0x18d1, "Nook Color", 0x2d02,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous Sourceforge user
{ "Google Inc (for Asus)", 0x18d1, "TF201 Transformer", 0x4d00,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous Sourceforge user
{ "Google Inc (for Asus)", 0x18d1, "TF101 Transformer", 0x4e0f,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Laurent Artaud <laurenta@users.sourceforge.net>
{ "Google Inc (for Samsung)", 0x18d1, "Nexus S", 0x4e21,
DEVICE_FLAGS_ANDROID_BUGS },
// 0x4e21 (Nexus S) is a USB Mass Storage device.
// Reported by Chris Smith <tcgsmythe@users.sourceforge.net>
{ "Google Inc (for Asus)", 0x18d1, "Nexus 7 (MTP)", 0x4e41,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Michael Hess <mhess126@gmail.com>
{ "Google Inc (for Asus)", 0x18d1, "Nexus 7 (MTP+ADB)", 0x4e42,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Google Inc (for LG Electronics/Samsung)", 0x18d1,
"Nexus 4/10 (MTP)", 0x4ee1,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Google Inc (for LG Electronics/Samsung)", 0x18d1,
"Nexus 4/10 (MTP+ADB)", 0x4ee2,
DEVICE_FLAGS_ANDROID_BUGS },
// WiFi-only version of Xoom
// See: http://bugzilla.gnome.org/show_bug.cgi?id=647506
{ "Google Inc (for Motorola)", 0x18d1, "Xoom (MZ604)", 0x70a8,
@ -1845,9 +1955,16 @@
*/
{ "Coby", 0x1e74, "COBY MP705", 0x6512, DEVICE_FLAG_NONE },
#if 0
/*
* Apple devices, which are not MTP natively but can be made to speak MTP
* using PwnTunes (http://www.pwntunes.net/)
* CURRENTLY COMMENTED OUT:
* These will make the UDEV rules flag these as MTP devices even if
* PwnTunes is NOT installed. That is unacceptable, so a better solution
* that actually inspects if the device has PwnTunes/MTP support needs
* to be found, see:
* https://sourceforge.net/p/libmtp/bugs/759/
*/
{ "Apple", 0x05ac, "iPhone", 0x1290, DEVICE_FLAG_NONE },
{ "Apple", 0x05ac, "iPod Touch 1st Gen", 0x1291, DEVICE_FLAG_NONE },
@ -1859,6 +1976,7 @@
{ "Apple", 0x05ac, "0x1298", 0x1298, DEVICE_FLAG_NONE },
{ "Apple", 0x05ac, "iPod Touch 3rd Gen", 0x1299, DEVICE_FLAG_NONE },
{ "Apple", 0x05ac, "iPad", 0x129a, DEVICE_FLAG_NONE },
#endif
// Reported by anonymous SourceForge user, also reported as
// Pantech Crux, claming to be:
@ -1872,30 +1990,48 @@
/*
* Asus
* Pattern of PIDs on Android devices seem to be:
* n+0 = MTP
* n+1 = MTP+ADB
* n+2 = ?
* n+3 = ?
* n+4 = PTP
*/
// Reported by Glen Overby
{ "Asus", 0x0b05, "TF300 Transformer", 0x4c80,
{ "Asus", 0x0b05, "TF300 Transformer (MTP)", 0x4c80,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by jaile <jaile@users.sourceforge.net>
{ "Asus", 0x0b05, "TF300 Transformer (USB debug mode)", 0x4c81,
{ "Asus", 0x0b05, "TF300 Transformer (MTP+ADB)", 0x4c81,
DEVICE_FLAGS_ANDROID_BUGS },
// Repored by Florian Apolloner <f-apolloner@users.sourceforge.net>
{ "Asus", 0x0b05, "TF700 Transformer", 0x4c90,
{ "Asus", 0x0b05, "TF700 Transformer (MTP)", 0x4c90,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "TF700 Transformer (MTP+ADB)", 0x4c91,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "MeMo Pad Smart 10", 0x4cd0,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous Sourceforge user
{ "Asus", 0x0b05, "TF201 Transformer Prime (keyboard dock)", 0x4d00,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "TF201 Transformer Prime (tablet only)", 0x4d01,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "TFXXX Transformer Prime (unknown version)", 0x4d04,
// 4d04 is the PTP mode, don't add it
{ "Asus", 0x0b05, "SL101 (MTP)", 0x4e00,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous Sourceforge user
{ "Asus", 0x0b05, "TF101 Eeepad Slider", 0x4e01,
{ "Asus", 0x0b05, "SL101 (MTP+ADB)", 0x4e01,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "TF101 Eeepad Transformer", 0x4e0f,
{ "Asus", 0x0b05, "TF101 Eeepad Transformer (MTP)", 0x4e0f,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "TF101 Eeepad Transformer (debug mode)", 0x4e1f,
{ "Asus", 0x0b05, "TF101 Eeepad Transformer (MTP+ADB)", 0x4e1f,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "PadFone (MTP)", 0x5200,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "PadFone (MTP+ADB)", 0x5201,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "PadFone 2 (MTP+?)", 0x5210,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Asus", 0x0b05, "PadFone 2 (MTP)", 0x5211,
DEVICE_FLAGS_ANDROID_BUGS },
/*
@ -1914,17 +2050,26 @@
// Reported by: anonymous sourceforge user
{ "Lenovo", 0x17ef, "Lifetab S9512", 0x74cc,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Brian J. Murrell
{ "Lenovo", 0x17ef, "IdeaTab A2109A", 0x7542,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Huawei
*/
// Reported by anonymous SourceForge user
{ "Huawei", 0x12d1, "Honor U8860", 0x1051, DEVICE_FLAGS_ANDROID_BUGS },
{ "Huawei", 0x12d1, "Honor U8860", 0x1051,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous SourceForge user
{ "Huawei", 0x12d1, "Mediapad (mode 0)", 0x360f, DEVICE_FLAGS_ANDROID_BUGS },
{ "Huawei", 0x12d1, "U8815/U9200", 0x1052,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous SourceForge user
{ "Huawei", 0x12d1, "Mediapad (mode 0)", 0x360f,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Bearsh <bearsh@users.sourceforge.net>
{ "Huawei", 0x12d1, "Mediapad (mode 1)", 0x361f, DEVICE_FLAGS_ANDROID_BUGS },
{ "Huawei", 0x12d1, "Mediapad (mode 1)", 0x361f,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* ZTE
@ -1932,27 +2077,61 @@
*/
{ "ZTE", 0x19d2, "V55 ID 1", 0x0244, DEVICE_FLAGS_ANDROID_BUGS },
{ "ZTE", 0x19d2, "V55 ID 2", 0x0245, DEVICE_FLAGS_ANDROID_BUGS },
{ "ZTE", 0x19d2, "v790/Blade 3", 0x0306, DEVICE_FLAGS_ANDROID_BUGS },
/*
* HTC (High Tech Computer Corp)
* Reporters:
* Steven Eastland <grassmonk@users.sourceforge.net>
* Kevin Cheng <kache@users.sf.net>
*/
{ "HTC", 0x0bb4, "Zopo ZP100 (ID1)", 0x0c02,
#if 0
/*
* This had to be commented out - the same VID+PID is used also for
* other modes than MTP, so we need to let mtp-probe do its job on this
* device instead of adding it to the database.
*/
{ "HTC", 0x0bb4, "Android Device ID1 (Zopo, HD2, Bird...)", 0x0c02,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Steven Eastland <grassmonk@users.sourceforge.net>
{ "HTC", 0x0bb4, "EVO 4G LTE", 0x0c93,
#endif
{ "HTC", 0x0bb4, "EVO 4G LTE/One V (ID1)", 0x0c93,
DEVICE_FLAGS_ANDROID_BUGS },
// Reported by Steven Eastland <grassmonk@users.sourceforge.net>
{ "HTC", 0x0bb4, "EVO 4G LTE (second ID)", 0x0ca8,
{ "HTC", 0x0bb4, "EVO 4G LTE/One V (ID2)", 0x0ca8,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC One S (ID1)", 0x0cec,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC Evo 4G LTE (ID1)", 0x0df5,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC One S (ID2)", 0x0df9,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC One X (ID1)", 0x0dfb,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC One X (ID2)", 0x0dfc,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC One X (ID3)", 0x0dfd,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC Butterfly (ID1)", 0x0dfe,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "Droid DNA (MTP+UMS+ADB)", 0x0dff,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC Droid Incredible 4G LTE (MTP)", 0x0e31,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC Droid Incredible 4G LTE (MTP+ADB)", 0x0e32,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "Droid DNA (MTP+UMS)", 0x0ebd,
DEVICE_FLAGS_ANDROID_BUGS },
{ "HTC", 0x0bb4, "HTC One X (ID2)", 0x0f91,
DEVICE_FLAGS_ANDROID_BUGS },
// These identify themselves as "cm_tenderloin", fun...
// Done by HTC for HP I guess.
{ "Hewlett-Packard", 0x0bb4, "HP Touchpad", 0x685c,
{ "Hewlett-Packard", 0x0bb4, "HP Touchpad (MTP)", 0x685c,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Hewlett-Packard", 0x0bb4, "HP Touchpad (debug mode)",
0x6860, DEVICE_FLAGS_ANDROID_BUGS },
// Reported by anonymous SourceForge user
{ "HTC", 0x0bb4, "Zopo ZP100 (ID2)", 0x2008,
{ "Hewlett-Packard", 0x0bb4, "HP Touchpad (MTP+ADB)", 0x6860,
DEVICE_FLAGS_ANDROID_BUGS },
#if 0
{ "HTC", 0x0bb4, "Android Device ID2 (Zopo, HD2...)", 0x2008,
DEVICE_FLAGS_ANDROID_BUGS },
#endif
/*
* NEC
@ -1963,7 +2142,12 @@
* nVidia
*/
// Found on Internet forum
{ "nVidia", 0x0955, "CM9-Adam", 0x70a9, DEVICE_FLAGS_ANDROID_BUGS },
{ "nVidia", 0x0955, "CM9-Adam", 0x70a9,
DEVICE_FLAGS_ANDROID_BUGS },
{ "nVidia", 0x0955, "Nabi2 Tablet (ID1)", 0x7100,
DEVICE_FLAGS_ANDROID_BUGS },
{ "nVidia", 0x0955, "Nabi2 Tablet (ID2)", 0x7102,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Vizio
@ -1972,10 +2156,60 @@
{ "Vizio", 0x0489, "VTAB1008", 0xe040, DEVICE_FLAGS_ANDROID_BUGS },
/*
* Viewpia, bq...
* Seems like some multi-branded OEM product.
* Amazon
*/
{ "Various", 0x2207, "Viewpia DR/bq Kepler", 0x0001, DEVICE_FLAGS_ANDROID_BUGS },
{ "Amazon", 0x1949, "Kindle Fire 2G (ID1)", 0x0005,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Amazon", 0x1949, "Kindle Fire (ID1)", 0x0007,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Amazon", 0x1949, "Kindle Fire (ID2)", 0x0008,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Amazon", 0x1949, "Kindle Fire (ID3)", 0x000a,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Barnes&Noble
*/
{ "Barnes&Noble", 0x2080, "Nook HD+", 0x0005,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Viewpia, bq, YiFang
* Seems like some multi-branded OEM product line.
*/
{ "Various", 0x2207, "Viewpia DR/bq Kepler", 0x0001,
DEVICE_FLAGS_ANDROID_BUGS },
{ "YiFang", 0x2207, "BQ Tesla", 0x0006,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Kobo
*/
// Reported by George Talusan
{ "Kobo", 0x2237, "Arc (ID1)", 0xd108,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Kobo", 0x2237, "Arc (ID2)", 0xd109,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Hisense
*/
// Reported by Anonymous SourceForge user
{ "Hisense", 0x109b, "E860", 0x9109, DEVICE_FLAGS_ANDROID_BUGS },
/*
* Intel
* Also sold rebranded as Orange products
*/
{ "Intel", 0x8087, "Xolo 900/AZ210A", 0x09fb, DEVICE_FLAGS_ANDROID_BUGS },
/*
* Xiaomi
*/
{ "Xiaomi", 0x2717, "Mi-2 (MTP+ADB)", 0x9039,
DEVICE_FLAGS_ANDROID_BUGS },
{ "Xiaomi", 0x2717, "Mi-2 (MTP)", 0xf003,
DEVICE_FLAGS_ANDROID_BUGS },
/*
* Other strange stuff.

View File

@ -7,14 +7,16 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
MP = 'http://libmtp.git.sourceforge.net/git/gitweb.cgi?p=libmtp/libmtp;a=blob_plain;f=src/music-players.h;hb=HEAD'
DF = 'http://libmtp.git.sourceforge.net/git/gitweb.cgi?p=libmtp/libmtp;a=blob_plain;f=src/device-flags.h;hb=HEAD'
import urllib, os, shutil
import os, shutil, subprocess
base = os.path.dirname(os.path.abspath(__file__))
for url, fname in [(MP, 'music-players.h'), (DF, 'device-flags.h')]:
with open(os.path.join(base, fname), 'wb') as f:
shutil.copyfileobj(urllib.urlopen(url), f)
os.chdir('/tmp')
if os.path.exists('libmtp'):
shutil.rmtree('libmtp')
subprocess.check_call(['git', 'clone', 'git://git.code.sf.net/p/libmtp/code',
'libmtp'])
for x in ('src/music-players.h', 'src/device-flags.h'):
with open(os.path.join(base, os.path.basename(x)), 'wb') as f:
shutil.copyfileobj(open('libmtp/'+x), f)

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import re, tempfile, os, imghdr
import re, tempfile, os
from functools import partial
from itertools import izip
from urllib import quote
@ -17,6 +17,7 @@ from calibre.customize.conversion import (InputFormatPlugin,
OptionRecommendation)
from calibre.utils.localization import get_lang
from calibre.utils.filenames import ascii_filename
from calibre.utils.imghdr import what
class HTMLInput(InputFormatPlugin):
@ -250,7 +251,7 @@ class HTMLInput(InputFormatPlugin):
if media_type == self.BINARY_MIME:
# Check for the common case, images
try:
img = imghdr.what(link)
img = what(link)
except EnvironmentError:
pass
else:

View File

@ -105,7 +105,7 @@ class RTFInput(InputFormatPlugin):
return f.read()
def extract_images(self, picts):
import imghdr
from calibre.utils.imghdr import what
self.log('Extracting images...')
with open(picts, 'rb') as f:
@ -120,7 +120,7 @@ class RTFInput(InputFormatPlugin):
if len(enc) % 2 == 1:
enc = enc[:-1]
data = enc.decode('hex')
fmt = imghdr.what(None, data)
fmt = what(None, data)
if fmt is None:
fmt = 'wmf'
count += 1

View File

@ -9,7 +9,7 @@ __copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net and ' \
'Marshall T. Vandegrift <llasram@gmail.com>'
__docformat__ = 'restructuredtext en'
import os, cStringIO, imghdr
import os, cStringIO
from struct import pack, unpack
from cStringIO import StringIO
@ -18,12 +18,13 @@ from calibre.ebooks.mobi import MobiError, MAX_THUMB_DIMEN
from calibre.ebooks.mobi.utils import rescale_image
from calibre.ebooks.mobi.langcodes import iana2mobi
from calibre.utils.date import now as nowf
from calibre.utils.imghdr import what
from calibre.utils.localization import canonicalize_lang, lang_as_iso639_1
def is_image(ss):
if ss is None:
return False
return imghdr.what(None, ss[:200]) is not None
return what(None, ss[:200]) is not None
class StreamSlicer(object):

View File

@ -8,7 +8,7 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys, os, imghdr, struct, textwrap
import sys, os, struct, textwrap
from itertools import izip
from calibre import CurrentDir
@ -18,6 +18,7 @@ from calibre.ebooks.mobi.debug.index import (SKELIndex, SECTIndex, NCXIndex,
from calibre.ebooks.mobi.utils import read_font_record, decode_tbs, RECORD_SIZE
from calibre.ebooks.mobi.debug import format_bytes
from calibre.ebooks.mobi.reader.headers import NULL_INDEX
from calibre.utils.imghdr import what
class FDST(object):
@ -173,7 +174,7 @@ class MOBIFile(object):
font['raw_data'])
prefix, ext = 'fonts', font['ext']
elif sig not in known_types:
q = imghdr.what(None, rec.raw)
q = what(None, rec.raw)
if q:
prefix, ext = 'images', q

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import struct, re, os, imghdr
import struct, re, os
from collections import namedtuple
from itertools import repeat, izip
from urlparse import urldefrag
@ -23,6 +23,7 @@ from calibre.ebooks.metadata.toc import TOC
from calibre.ebooks.mobi.utils import read_font_record
from calibre.ebooks.oeb.parse_utils import parse_html
from calibre.ebooks.oeb.base import XPath, XHTML, xml2text
from calibre.utils.imghdr import what
Part = namedtuple('Part',
'num type filename start end aid')
@ -403,7 +404,7 @@ class Mobi8Reader(object):
if font['encrypted']:
self.encrypted_fonts.append(href)
else:
imgtype = imghdr.what(None, data)
imgtype = what(None, data)
if imgtype is None:
imgtype = 'unknown'
href = 'images/%05d.%s'%(fname_idx, imgtype)

View File

@ -7,11 +7,12 @@ __license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import struct, string, imghdr, zlib, os
import struct, string, zlib, os
from collections import OrderedDict
from io import BytesIO
from calibre.utils.magick.draw import Image, save_cover_data_to, thumbnail
from calibre.utils.imghdr import what
from calibre.ebooks import normalize
IMAGE_MAX_SIZE = 10 * 1024 * 1024
@ -384,9 +385,9 @@ def to_base(num, base=32, min_num_digits=None):
def mobify_image(data):
'Convert PNG images to GIF as the idiotic Kindle cannot display some PNG'
what = imghdr.what(None, data)
fmt = what(None, data)
if what == 'png':
if fmt == 'png':
im = Image()
im.load(data)
data = im.export('gif')

View File

@ -7,13 +7,12 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import imghdr
from calibre.ebooks.mobi import MAX_THUMB_DIMEN, MAX_THUMB_SIZE
from calibre.ebooks.mobi.utils import (rescale_image, mobify_image,
write_font_record)
from calibre.ebooks import generate_masthead
from calibre.ebooks.oeb.base import OEB_RASTER_IMAGES
from calibre.utils.imghdr import what
PLACEHOLDER_GIF = b'GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\xff\xff\xff!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00@\x02\x01D\x00;'
@ -84,7 +83,7 @@ class Resources(object):
self.image_indices.add(len(self.records))
self.records.append(data)
self.item_map[item.href] = index
self.mime_map[item.href] = 'image/%s'%imghdr.what(None, data)
self.mime_map[item.href] = 'image/%s'%what(None, data)
index += 1
if cover_href and item.href == cover_href:

View File

@ -15,7 +15,7 @@ from functools import partial
from lxml import etree
from calibre import __version__
from calibre.ebooks.oeb.base import XPath, uuid_id, xml2text, NCX, NCX_NS, XML
from calibre.ebooks.oeb.base import XPath, uuid_id, xml2text, NCX, NCX_NS, XML, XHTML
from calibre.ebooks.oeb.polish.container import guess_type
from calibre.utils.localization import get_lang, canonicalize_lang, lang_as_iso639_1
@ -39,10 +39,17 @@ class TOC(object):
c.parent = self
return c
def remove(self, child):
self.children.remove(child)
child.parent = None
def __iter__(self):
for c in self.children:
yield c
def __len__(self):
return len(self.children)
def iterdescendants(self):
for child in self:
yield child
@ -169,6 +176,91 @@ def get_toc(container, verify_destinations=True):
verify_toc_destinations(container, ans)
return ans
def ensure_id(elem):
if elem.tag == XHTML('a'):
anchor = elem.get('name', None)
if anchor:
return False, anchor
elem_id = elem.get('id', None)
if elem_id:
return False, elem_id
elem.set('id', uuid_id())
return True, elem.get('id')
def elem_to_toc_text(elem):
text = xml2text(elem).strip()
if not text:
text = elem.get('title', '')
if not text:
text = elem.get('alt', '')
text = re.sub(r'\s+', ' ', text.strip())
text = text[:1000].strip()
if not text:
text = _('(Untitled)')
return text
def from_xpaths(container, xpaths):
tocroot = TOC()
xpaths = [XPath(xp) for xp in xpaths]
level_prev = {i+1:None for i in xrange(len(xpaths))}
level_prev[0] = tocroot
for spinepath in container.spine_items:
name = container.abspath_to_name(spinepath)
root = container.parsed(name)
level_item_map = {i+1:frozenset(xp(root)) for i, xp in enumerate(xpaths)}
item_level_map = {e:i for i, elems in level_item_map.iteritems() for e in elems}
item_dirtied = False
for item in root.iterdescendants(etree.Element):
lvl = plvl = item_level_map.get(item, None)
if lvl is None:
continue
parent = None
while parent is None:
plvl -= 1
parent = level_prev[plvl]
lvl = plvl + 1
dirtied, elem_id = ensure_id(item)
text = elem_to_toc_text(item)
item_dirtied = dirtied or item_dirtied
toc = parent.add(text, name, elem_id)
toc.dest_exists = True
level_prev[lvl] = toc
for i in xrange(lvl+1, len(xpaths)+1):
level_prev[i] = None
if item_dirtied:
container.commit_item(name, keep_parsed=True)
return tocroot
def from_links(container):
toc = TOC()
link_path = XPath('//h:a[@href]')
seen_titles, seen_dests = set(), set()
for spinepath in container.spine_items:
name = container.abspath_to_name(spinepath)
root = container.parsed(name)
for a in link_path(root):
href = a.get('href')
if not href or not href.strip():
continue
dest = container.href_to_name(href, base=name)
frag = href.rpartition('#')[-1] or None
if (dest, frag) in seen_dests:
continue
seen_dests.add((dest, frag))
text = elem_to_toc_text(a)
if text in seen_titles:
continue
seen_titles.add(text)
toc.add(text, dest, frag=frag)
verify_toc_destinations(container, toc)
for child in toc:
if not child.dest_exists:
toc.remove(child)
return toc
def add_id(container, name, loc):
root = container.parsed(name)

View File

@ -305,8 +305,17 @@ class Stylizer(object):
href = stylesheet.href
self.stylesheets.add(href)
for rule in stylesheet.cssRules:
rules.extend(self.flatten_rule(rule, href, index))
index = index + 1
if rule.type == rule.MEDIA_RULE:
media = {rule.media.item(i) for i in
xrange(rule.media.length)}
if not media.intersection({'all', 'screen', 'amzn-kf8'}):
continue
for subrule in rule.cssRules:
rules.extend(self.flatten_rule(subrule, href, index))
index += 1
else:
rules.extend(self.flatten_rule(rule, href, index))
index = index + 1
rules.sort()
self.rules = rules
self._styles = {}

View File

@ -11,21 +11,22 @@ import re, sys
from functools import partial
from calibre.ebooks.conversion.config import load_defaults
from calibre.gui2 import gprefs, open_url, question_dialog
from calibre.gui2 import gprefs, open_url, question_dialog, error_dialog
from calibre.utils.config import JSONConfig
from calibre.utils.icu import sort_key
from catalog_epub_mobi_ui import Ui_Form
from PyQt4.Qt import (Qt, QAbstractItemView, QCheckBox, QComboBox,
QDoubleSpinBox, QIcon, QLineEdit, QObject, QRadioButton, QSize, QSizePolicy,
QTableWidget, QTableWidgetItem, QTextEdit, QToolButton, QUrl,
QVBoxLayout, QWidget,
QDoubleSpinBox, QIcon, QInputDialog, QLineEdit, QObject, QRadioButton,
QSize, QSizePolicy, QTableWidget, QTableWidgetItem, QTextEdit, QToolButton,
QUrl, QVBoxLayout, QWidget,
SIGNAL)
class PluginWidget(QWidget,Ui_Form):
TITLE = _('E-book options')
HELP = _('Options specific to')+' AZW3/EPUB/MOBI '+_('output')
DEBUG = False
DEBUG = True
# Output synced to the connected device?
sync_enabled = True
@ -212,8 +213,8 @@ class PluginWidget(QWidget,Ui_Form):
else:
results = _truncated_results(excluded_tags)
finally:
if self.DEBUG:
print(results)
if False and self.DEBUG:
print("exclude_genre_changed(): %s" % results)
self.exclude_genre_results.clear()
self.exclude_genre_results.setText(results)
@ -239,11 +240,11 @@ class PluginWidget(QWidget,Ui_Form):
Toggle Description-related controls
'''
self.header_note_source_field.setEnabled(enabled)
self.thumb_width.setEnabled(enabled)
self.merge_source_field.setEnabled(enabled)
self.merge_before.setEnabled(enabled)
self.merge_after.setEnabled(enabled)
self.include_hr.setEnabled(enabled)
self.merge_after.setEnabled(enabled)
self.merge_before.setEnabled(enabled)
self.merge_source_field.setEnabled(enabled)
self.thumb_width.setEnabled(enabled)
def generate_genres_changed(self, enabled):
'''
@ -263,6 +264,22 @@ class PluginWidget(QWidget,Ui_Form):
self.genre_source_field_name = genre_source_spec['field']
self.exclude_genre_changed()
def get_format_and_title(self):
current_format = None
current_title = None
self.parentWidget().blockSignals(True)
for peer in self.parentWidget().children():
if peer == self:
continue
elif peer.children():
for child in peer.children():
if child.objectName() == 'format':
current_format = str(child.currentText()).strip()
elif child.objectName() == 'title':
current_title = str(child.text()).strip()
self.parentWidget().blockSignals(False)
return current_format, current_title
def header_note_source_field_changed(self,new_index):
'''
Process changes in the header_note_source_field combo box
@ -374,15 +391,20 @@ class PluginWidget(QWidget,Ui_Form):
# Initialize exclusion rules
self.exclusion_rules_table = ExclusionRules(self.exclusion_rules_gb,
"exclusion_rules_tw",exclusion_rules, self.eligible_custom_fields,self.db)
"exclusion_rules_tw", exclusion_rules, self.eligible_custom_fields, self.db)
# Initialize prefix rules
self.prefix_rules_table = PrefixRules(self.prefix_rules_gb,
"prefix_rules_tw",prefix_rules, self.eligible_custom_fields,self.db)
"prefix_rules_tw", prefix_rules, self.eligible_custom_fields, self.db)
# Initialize excluded genres preview
self.exclude_genre_changed()
# Hook Preset signals
self.preset_delete_pb.clicked.connect(self.preset_remove)
self.preset_save_pb.clicked.connect(self.preset_save)
self.preset_field.currentIndexChanged[str].connect(self.preset_change)
def merge_source_field_changed(self,new_index):
'''
Process changes in the merge_source_field combo box
@ -404,10 +426,12 @@ class PluginWidget(QWidget,Ui_Form):
self.include_hr.setEnabled(False)
def options(self):
# Save/return the current options
# exclude_genre stores literally
# Section switches store as True/False
# others store as lists
'''
Return, optionally save current options
exclude_genre stores literally
Section switches store as True/False
others store as lists
'''
opts_dict = {}
prefix_rules_processed = False
@ -469,7 +493,7 @@ class PluginWidget(QWidget,Ui_Form):
except:
opts_dict['output_profile'] = ['default']
if self.DEBUG:
if False and self.DEBUG:
print "opts_dict"
for opt in sorted(opts_dict.keys(), key=sort_key):
print " %s: %s" % (opt, repr(opts_dict[opt]))
@ -544,6 +568,215 @@ class PluginWidget(QWidget,Ui_Form):
self.genre_source_fields = custom_fields
self.genre_source_field.currentIndexChanged.connect(self.genre_source_field_changed)
# Populate the Presets combo box
self.presets = JSONConfig("catalog_presets")
self.preset_field.addItem("")
self.preset_field_values = sorted([p for p in self.presets], key=sort_key)
self.preset_field.addItems(self.preset_field_values)
def preset_change(self, item_name):
'''
Update catalog options from current preset
'''
if not item_name:
return
current_preset = str(self.preset_field.currentText())
options = self.presets[current_preset]
exclusion_rules = []
prefix_rules = []
for opt in self.OPTION_FIELDS:
c_name, c_def, c_type = opt
if c_name == 'preset_field':
continue
# Ignore extra entries in options for cli invocation
if c_name in options:
opt_value = options[c_name]
else:
continue
if c_type in ['check_box']:
getattr(self, c_name).setChecked(eval(str(opt_value)))
if c_name == 'generate_genres':
self.genre_source_field.setEnabled(eval(str(opt_value)))
elif c_type in ['combo_box']:
if opt_value is None:
index = 0
if c_name == 'genre_source_field':
index = self.genre_source_field.findText(_('Tags'))
else:
index = getattr(self,c_name).findText(opt_value)
if index == -1:
if c_name == 'read_source_field':
index = self.read_source_field.findText(_('Tags'))
elif c_name == 'genre_source_field':
index = self.genre_source_field.findText(_('Tags'))
getattr(self,c_name).setCurrentIndex(index)
elif c_type in ['line_edit']:
getattr(self, c_name).setText(opt_value if opt_value else '')
elif c_type in ['radio_button'] and opt_value is not None:
getattr(self, c_name).setChecked(opt_value)
elif c_type in ['spin_box']:
getattr(self, c_name).setValue(float(opt_value))
if c_type == 'table_widget':
if c_name == 'exclusion_rules_tw':
if opt_value not in exclusion_rules:
exclusion_rules.append(opt_value)
if c_name == 'prefix_rules_tw':
if opt_value not in prefix_rules:
prefix_rules.append(opt_value)
# Reset exclusion rules
self.exclusion_rules_table.clearLayout()
self.exclusion_rules_table = ExclusionRules(self.exclusion_rules_gb,
"exclusion_rules_tw", exclusion_rules, self.eligible_custom_fields, self.db)
# Reset prefix rules
self.prefix_rules_table.clearLayout()
self.prefix_rules_table = PrefixRules(self.prefix_rules_gb,
"prefix_rules_tw", prefix_rules, self.eligible_custom_fields, self.db)
# Reset excluded genres preview
self.exclude_genre_changed()
# Reset format and title
format = options['format']
title = options['catalog_title']
self.set_format_and_title(format, title)
# Reset Descriptions-related enable/disable switches
self.generate_descriptions_changed(self.generate_descriptions.isChecked())
def preset_remove(self):
if self.preset_field.currentIndex() == 0:
return
if not question_dialog(self, _("Delete saved catalog preset"),
_("The selected saved catalog preset will be deleted. "
"Are you sure?")):
return
item_id = self.preset_field.currentIndex()
item_name = unicode(self.preset_field.currentText())
self.preset_field.blockSignals(True)
self.preset_field.removeItem(item_id)
self.preset_field.blockSignals(False)
self.preset_field.setCurrentIndex(0)
if item_name in self.presets.keys():
del(self.presets[item_name])
self.presets.commit()
def preset_save(self):
names = ['']
names.extend(self.preset_field_values)
try:
dex = names.index(self.preset_search_name)
except:
dex = 0
name = ''
while not name:
name, ok = QInputDialog.getItem(self, _('Save catalog preset'),
_('Preset name:'), names, dex, True)
if not ok:
return
if not name:
error_dialog(self, _("Save catalog preset"),
_("You must provide a name."), show=True)
new = True
name = unicode(name)
if name in self.presets.keys():
if not question_dialog(self, _("Save catalog preset"),
_("That saved preset already exists and will be overwritten. "
"Are you sure?")):
return
new = False
preset = {}
prefix_rules_processed = False
exclusion_rules_processed = False
for opt in self.OPTION_FIELDS:
c_name, c_def, c_type = opt
if c_name == 'exclusion_rules_tw' and exclusion_rules_processed:
continue
if c_name == 'prefix_rules_tw' and prefix_rules_processed:
continue
if c_type in ['check_box', 'radio_button']:
opt_value = getattr(self, c_name).isChecked()
elif c_type in ['combo_box']:
if c_name == 'preset_field':
continue
opt_value = unicode(getattr(self,c_name).currentText()).strip()
elif c_type in ['line_edit']:
opt_value = unicode(getattr(self, c_name).text()).strip()
elif c_type in ['spin_box']:
opt_value = unicode(getattr(self, c_name).value())
elif c_type in ['table_widget']:
if c_name == 'prefix_rules_tw':
opt_value = self.prefix_rules_table.get_data()
prefix_rules_processed = True
if c_name == 'exclusion_rules_tw':
opt_value = self.exclusion_rules_table.get_data()
exclusion_rules_processed = True
preset[c_name] = opt_value
# Construct cli version of table rules
if c_name in ['exclusion_rules_tw','prefix_rules_tw']:
self.construct_tw_opts_object(c_name, opt_value, preset)
format, title = self.get_format_and_title()
preset['format'] = format
preset['catalog_title'] = title
# Additional items needed for cli invocation
# Generate specs for merge_comments, header_note_source_field, genre_source_field
checked = ''
if self.merge_before.isChecked():
checked = 'before'
elif self.merge_after.isChecked():
checked = 'after'
include_hr = self.include_hr.isChecked()
preset['merge_comments_rule'] = "%s:%s:%s" % \
(self.merge_source_field_name, checked, include_hr)
preset['header_note_source_field'] = unicode(self.header_note_source_field.currentText())
preset['genre_source_field'] = unicode(self.genre_source_field.currentText())
# Append the current output profile
try:
preset['output_profile'] = load_defaults('page_setup')['output_profile']
except:
preset['output_profile'] = 'default'
self.presets[name] = preset
self.presets.commit()
if new:
self.preset_field.blockSignals(True)
self.preset_field.clear()
self.preset_field.addItem('')
self.preset_field_values = sorted([q for q in self.presets], key=sort_key)
self.preset_field.addItems(self.preset_field_values)
self.preset_field.blockSignals(False)
self.preset_field.setCurrentIndex(self.preset_field.findText(name))
def set_format_and_title(self, format, title):
for peer in self.parentWidget().children():
if peer == self:
continue
elif peer.children():
for child in peer.children():
if child.objectName() == 'format':
index = child.findText(format)
child.blockSignals(True)
child.setCurrentIndex(index)
child.blockSignals(False)
elif child.objectName() == 'title':
child.setText(title)
def show_help(self):
'''
Display help file
@ -631,6 +864,7 @@ class GenericRulesTable(QTableWidget):
self.last_row_selected = self.currentRow()
self.last_rows_selected = self.selectionModel().selectedRows()
# Add the controls
self._init_controls()
# Hook check_box changes
@ -681,6 +915,21 @@ class GenericRulesTable(QTableWidget):
# In case table was empty
self.horizontalHeader().setStretchLastSection(True)
def clearLayout(self):
if self.layout is not None:
old_layout = self.layout
for child in old_layout.children():
for i in reversed(range(child.count())):
if child.itemAt(i).widget() is not None:
child.itemAt(i).widget().setParent(None)
import sip
sip.delete(child)
for i in reversed(range(old_layout.count())):
if old_layout.itemAt(i).widget() is not None:
old_layout.itemAt(i).widget().setParent(None)
def delete_row(self):
if self.DEBUG:
print("%s:delete_row()" % self.objectName())

View File

@ -20,6 +20,54 @@
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="catalogPresets">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Presets</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QComboBox" name="preset_field">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select catalog preset to load</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="preset_save_pb">
<property name="toolTip">
<string>Save current catalog settings as preset</string>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="preset_delete_pb">
<property name="toolTip">
<string>Delete current preset</string>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="includedSections">
<property name="sizePolicy">
@ -46,6 +94,9 @@
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>List of books, sorted by Author</string>
</property>
<property name="text">
<string>&amp;Authors</string>
</property>
@ -54,15 +105,21 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QCheckBox" name="generate_titles">
<property name="toolTip">
<string>List of books, sorted by Title</string>
</property>
<property name="text">
<string>&amp;Titles</string>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QCheckBox" name="generate_series">
<property name="toolTip">
<string>List of series books, sorted by Series</string>
</property>
<property name="text">
<string>&amp;Series</string>
</property>
@ -72,6 +129,9 @@
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="generate_genres">
<property name="toolTip">
<string>List of books, sorted by Genre</string>
</property>
<property name="text">
<string>&amp;Genres</string>
</property>
@ -80,13 +140,13 @@
<item>
<widget class="QComboBox" name="genre_source_field">
<property name="toolTip">
<string>Field containing Genre information</string>
<string>Field containing Genres</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="2">
<item row="2" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QCheckBox" name="generate_recently_added">
@ -96,6 +156,9 @@
<height>26</height>
</size>
</property>
<property name="toolTip">
<string>List of books, sorted by date added to calibre</string>
</property>
<property name="text">
<string>&amp;Recently Added</string>
</property>
@ -103,7 +166,7 @@
</item>
</layout>
</item>
<item row="3" column="2">
<item row="4" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QCheckBox" name="generate_descriptions">
@ -113,6 +176,9 @@
<height>26</height>
</size>
</property>
<property name="toolTip">
<string>Individual descriptions of books with cover thumbs, sorted by author</string>
</property>
<property name="text">
<string>&amp;Descriptions</string>
</property>
@ -120,6 +186,41 @@
</item>
</layout>
</item>
<item row="0" column="1" rowspan="5">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="Line" name="line_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -347,7 +448,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
</size>
</property>
<property name="toolTip">
<string>Custom column containing additional content to be merged with Comments metadata.</string>
<string>Custom column containing additional content to be merged with Comments metadata in Descriptions section.</string>
</property>
</widget>
</item>
@ -361,7 +462,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
<item>
<widget class="QRadioButton" name="merge_before">
<property name="toolTip">
<string>Merge additional content before Comments metadata.</string>
<string>Merge additional content before Comments in Descriptions section.</string>
</property>
<property name="text">
<string>&amp;Before</string>
@ -374,7 +475,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
<item>
<widget class="QRadioButton" name="merge_after">
<property name="toolTip">
<string>Merge additional content after Comments metadata.</string>
<string>Merge additional content after Comments in Descriptions section.</string>
</property>
<property name="text">
<string>&amp;After</string>
@ -394,7 +495,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
<item>
<widget class="QCheckBox" name="include_hr">
<property name="toolTip">
<string>Separate Comments metadata and additional content with a horizontal rule.</string>
<string>Separate Comments metadata and additional content with a horizontal rule in Descriptions section.</string>
</property>
<property name="text">
<string>Include &amp;Separator</string>
@ -514,7 +615,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
</size>
</property>
<property name="toolTip">
<string>Custom column source for text to include in Description section.</string>
<string>Custom column source for text to include in Descriptions section.</string>
</property>
</widget>
</item>

View File

@ -763,22 +763,24 @@ class EditRules(QWidget): # {{{
' double clicking it.'))
self.add_advanced_button.setVisible(False)
def _add_rule(self, dlg):
if dlg.exec_() == dlg.Accepted:
kind, col, r = dlg.rule
def add_rule(self):
d = RuleEditor(self.model.fm, self.pref_name)
d.add_blank_condition()
if d.exec_() == d.Accepted:
kind, col, r = d.rule
if kind and r and col:
idx = self.model.add_rule(kind, col, r)
self.rules_view.scrollTo(idx)
self.changed.emit()
def add_rule(self):
d = RuleEditor(self.model.fm, self.pref_name)
d.add_blank_condition()
self._add_rule(d)
def add_advanced(self):
td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, color_field='')
self._add_rule(('color', td[0], td[1]))
if td.exec_() == td.Accepted:
col, r = td.rule
if r and col:
idx = self.model.add_rule('color', col, r)
self.rules_view.scrollTo(idx)
self.changed.emit()
def edit_rule(self, index):
try:

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys, os
import sys, os, textwrap
from threading import Thread
from functools import partial
@ -17,20 +17,65 @@ from PyQt4.Qt import (QPushButton, QFrame, QVariant,
QToolButton, QItemSelectionModel)
from calibre.ebooks.oeb.polish.container import get_container, AZW3Container
from calibre.ebooks.oeb.polish.toc import get_toc, add_id, TOC, commit_toc
from calibre.ebooks.oeb.polish.toc import (
get_toc, add_id, TOC, commit_toc, from_xpaths, from_links)
from calibre.gui2 import Application, error_dialog, gprefs
from calibre.gui2.progress_indicator import ProgressIndicator
from calibre.gui2.toc.location import ItemEdit
from calibre.gui2.convert.xpath_wizard import XPathEdit
from calibre.utils.logging import GUILog
ICON_SIZE = 24
class XPathDialog(QDialog): # {{{
def __init__(self, parent):
QDialog.__init__(self, parent)
self.setWindowTitle(_('Create ToC from XPath'))
self.l = l = QVBoxLayout()
self.setLayout(l)
self.la = la = QLabel(_(
'Specify a series of XPath expressions for the different levels of'
' the Table of Contents. You can use the wizard buttons to help'
' you create XPath expressions.'))
la.setWordWrap(True)
l.addWidget(la)
self.widgets = []
for i in xrange(5):
la = _('Level %s ToC:')%('&%d'%(i+1))
xp = XPathEdit(self)
xp.set_msg(la)
self.widgets.append(xp)
l.addWidget(xp)
self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
l.addStretch()
l.addWidget(bb)
self.resize(self.sizeHint() + QSize(50, 75))
def accept(self):
for w in self.widgets:
if not w.check():
return error_dialog(self, _('Invalid XPath'),
_('The XPath expression %s is not valid.')%w.xpath,
show=True)
super(XPathDialog, self).accept()
@property
def xpaths(self):
return [w.xpath for w in self.widgets if w.xpath.strip()]
# }}}
class ItemView(QFrame): # {{{
add_new_item = pyqtSignal(object, object)
delete_item = pyqtSignal()
flatten_item = pyqtSignal()
go_to_root = pyqtSignal()
create_from_xpath = pyqtSignal(object)
create_from_links = pyqtSignal()
def __init__(self, parent):
QFrame.__init__(self, parent)
@ -60,6 +105,41 @@ class ItemView(QFrame): # {{{
self.add_new_to_root_button = b = QPushButton(_('Create a &new entry'))
b.clicked.connect(self.add_new_to_root)
l.addWidget(b)
l.addStretch()
self.cfmhb = b = QPushButton(_('Generate ToC from &major headings'))
b.clicked.connect(self.create_from_major_headings)
b.setToolTip(textwrap.fill(_(
'Generate a Table of Contents from the major headings in the book.'
' This will work if the book identifies its headings using HTML'
' heading tags. Uses the <h1>, <h2> and <h3> tags.')))
l.addWidget(b)
self.cfmab = b = QPushButton(_('Generate ToC from &all headings'))
b.clicked.connect(self.create_from_all_headings)
b.setToolTip(textwrap.fill(_(
'Generate a Table of Contents from all the headings in the book.'
' This will work if the book identifies its headings using HTML'
' heading tags. Uses the <h1-6> tags.')))
l.addWidget(b)
self.lb = b = QPushButton(_('Generate ToC from &links'))
b.clicked.connect(self.create_from_links)
b.setToolTip(textwrap.fill(_(
'Generate a Table of Contents from all the links in the book.'
' Links that point to destinations that do not exist in the book are'
' ignored. Also multiple links with the same destination or the same'
' text are ignored.'
)))
l.addWidget(b)
self.xpb = b = QPushButton(_('Generate ToC from &XPath'))
b.clicked.connect(self.create_from_user_xpath)
b.setToolTip(textwrap.fill(_(
'Generate a Table of Contents from arbitrary XPath expressions.'
)))
l.addWidget(b)
l.addStretch()
self.w1 = la = QLabel(_('<b>WARNING:</b> calibre only supports the '
'creation of linear ToCs in AZW3 files. In a '
@ -121,19 +201,21 @@ class ItemView(QFrame): # {{{
ip.b5 = b = QPushButton(QIcon(I('plus.png')), _('New entry &below this entry'))
b.clicked.connect(partial(self.add_new, 'after'))
l.addWidget(b, l.rowCount(), 0, 1, 2)
ip.hl4 = hl = QFrame()
hl.setFrameShape(hl.HLine)
l.addWidget(hl, l.rowCount(), 0, 1, 2)
l.setRowMinimumHeight(rs, 20)
# Flatten entry
rs = l.rowCount()
ip.b3 = b = QPushButton(QIcon(I('heuristics.png')), _('&Flatten this entry'))
b.clicked.connect(self.flatten_item)
b.setToolTip(_('All children of this entry are brought to the same '
'level as this entry.'))
l.addWidget(b, l.rowCount()+1, 0, 1, 2)
ip.b4 = b = QPushButton(QIcon(I('back.png')), _('&Return to root'))
ip.hl4 = hl = QFrame()
hl.setFrameShape(hl.HLine)
l.addWidget(hl, l.rowCount(), 0, 1, 2)
l.setRowMinimumHeight(rs, 20)
# Return to welcome
rs = l.rowCount()
ip.b4 = b = QPushButton(QIcon(I('back.png')), _('&Return to welcome screen'))
b.clicked.connect(self.go_to_root)
b.setToolTip(_('Go back to the top level view'))
l.addWidget(b, l.rowCount()+1, 0, 1, 2)
@ -147,6 +229,17 @@ class ItemView(QFrame): # {{{
self.w2.setWordWrap(True)
l.addWidget(la, l.rowCount(), 0, 1, 2)
def create_from_major_headings(self):
self.create_from_xpath.emit(['//h:h%d'%i for i in xrange(1, 4)])
def create_from_all_headings(self):
self.create_from_xpath.emit(['//h:h%d'%i for i in xrange(1, 7)])
def create_from_user_xpath(self):
d = XPathDialog(self)
if d.exec_() == d.Accepted and d.xpaths:
self.create_from_xpath.emit(d.xpaths)
def hide_azw3_warning(self):
self.w1.setVisible(False), self.w2.setVisible(False)
@ -242,6 +335,8 @@ class TOCView(QWidget): # {{{
self.item_view = i = ItemView(self)
self.item_view.delete_item.connect(self.delete_current_item)
i.add_new_item.connect(self.add_new_item)
i.create_from_xpath.connect(self.create_from_xpath)
i.create_from_links.connect(self.create_from_links)
i.flatten_item.connect(self.flatten_item)
i.go_to_root.connect(self.go_to_root)
l.addWidget(i, 0, 4, col, 1)
@ -443,6 +538,32 @@ class TOCView(QWidget): # {{{
process_node(self.tocw.invisibleRootItem(), root)
return root
def insert_toc_fragment(self, toc):
def process_node(root, tocparent, added):
for child in tocparent:
item = self.create_item(root, child)
added.append(item)
process_node(item, child, added)
nodes = []
process_node(self.root, toc, nodes)
self.highlight_item(nodes[0])
def create_from_xpath(self, xpaths):
toc = from_xpaths(self.ebook, xpaths)
if len(toc) == 0:
return error_dialog(self, _('No items found'),
_('No items were found that could be added to the Table of Contents.'), show=True)
self.insert_toc_fragment(toc)
def create_from_links(self):
toc = from_links(self.ebook)
if len(toc) == 0:
return error_dialog(self, _('No items found'),
_('No links were found that could be added to the Table of Contents.'), show=True)
self.insert_toc_fragment(toc)
# }}}
class TOCEditor(QDialog): # {{{

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
import datetime, os, time
from collections import namedtuple
from calibre import strftime
@ -17,11 +17,11 @@ from calibre.ebooks import calibre_cover
from calibre.library import current_library_name
from calibre.library.catalogs import AuthorSortMismatchException, EmptyCatalogException
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import JSONConfig
from calibre.utils.localization import calibre_langcode_to_name, canonicalize_lang, get_lang
Option = namedtuple('Option', 'option, default, dest, action, help')
class EPUB_MOBI(CatalogPlugin):
'ePub catalog generator'
@ -162,6 +162,14 @@ class EPUB_MOBI(CatalogPlugin):
"When multiple rules are defined, the first matching rule will be used.\n"
"Default:\n" + '"' + '%default' + '"' + "\n"
"Applies to AZW3, ePub, MOBI output formats")),
Option('--preset',
default=None,
dest='preset',
action=None,
help=_("Use a named preset created with the GUI Catalog builder.\n"
"A preset specifies all settings for building a catalog.\n"
"Default: '%default'\n"
"Applies to AZW3, ePub, MOBI output formats")),
Option('--use-existing-cover',
default=False,
dest='use_existing_cover',
@ -184,6 +192,43 @@ class EPUB_MOBI(CatalogPlugin):
from calibre.library.catalogs.epub_mobi_builder import CatalogBuilder
from calibre.utils.logging import default_log as log
# If preset specified from the cli, insert stored options from JSON file
if hasattr(opts, 'preset') and opts.preset:
available_presets = JSONConfig("catalog_presets")
if not opts.preset in available_presets:
if available_presets:
print(_('Error: Preset "%s" not found.' % opts.preset))
print(_('Stored presets: %s' % ', '.join([p for p in sorted(available_presets.keys())])))
else:
print(_('Error: No stored presets.'))
return 1
# Copy the relevant preset values to the opts object
for item in available_presets[opts.preset]:
if not item in ['exclusion_rules_tw', 'format', 'prefix_rules_tw']:
setattr(opts, item, available_presets[opts.preset][item])
# Provide an unconnected device
opts.connected_device = {
'is_device_connected': False,
'kind': None,
'name': None,
'save_template': None,
'serial': None,
'storage': None,
}
# Convert prefix_rules and exclusion_rules from JSON lists to tuples
prs = []
for rule in opts.prefix_rules:
prs.append(tuple(rule))
opts.prefix_rules = tuple(prs)
ers = []
for rule in opts.exclusion_rules:
ers.append(tuple(rule))
opts.exclusion_rules = tuple(ers)
opts.log = log
opts.fmt = self.fmt = path_to_output.rpartition('.')[2]
@ -329,32 +374,37 @@ class EPUB_MOBI(CatalogPlugin):
log.error("incorrect number of args for --exclusion-rules: %s" % repr(rule))
# Display opts
keys = opts_dict.keys()
keys.sort()
keys = sorted(opts_dict.keys())
build_log.append(" opts:")
for key in keys:
if key in ['catalog_title', 'author_clip', 'connected_kindle', 'creator',
'cross_reference_authors', 'description_clip', 'exclude_book_marker',
'exclude_genre', 'exclude_tags', 'exclusion_rules', 'fmt',
'genre_source_field', 'header_note_source_field', 'merge_comments_rule',
'output_profile', 'prefix_rules', 'read_book_marker',
'output_profile', 'prefix_rules', 'preset', 'read_book_marker',
'search_text', 'sort_by', 'sort_descriptions_by_author', 'sync',
'thumb_width', 'use_existing_cover', 'wishlist_tag']:
build_log.append(" %s: %s" % (key, repr(opts_dict[key])))
if opts.verbose:
log('\n'.join(line for line in build_log))
# Capture start_time
opts.start_time = time.time()
self.opts = opts
if opts.verbose:
log.info(" Begin catalog source generation (%s)" %
str(datetime.timedelta(seconds = int(time.time() - opts.start_time))))
# Launch the Catalog builder
catalog = CatalogBuilder(db, opts, self, report_progress=notification)
if opts.verbose:
log.info(" Begin catalog source generation")
try:
catalog.build_sources()
if opts.verbose:
log.info(" Completed catalog source generation\n")
log.info(" Completed catalog source generation (%s)\n" %
str(datetime.timedelta(seconds = int(time.time() - opts.start_time))))
except (AuthorSortMismatchException, EmptyCatalogException), e:
log.error(" *** Terminated catalog generation: %s ***" % e)
except:
@ -444,5 +494,9 @@ class EPUB_MOBI(CatalogPlugin):
os.remove(epub_shell)
zip_rebuilder(input_path, os.path.join(catalog_debug_path, 'input.epub'))
if opts.verbose:
log.info(" Catalog creation complete (%s)\n" %
str(datetime.timedelta(seconds = int(time.time() - opts.start_time))))
# returns to gui2.actions.catalog:catalog_generated()
return catalog.error

View File

@ -3,7 +3,7 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Greg Riker'
import datetime, htmlentitydefs, os, platform, re, shutil, unicodedata, zlib
import datetime, htmlentitydefs, os, platform, re, shutil, time, unicodedata, zlib
from copy import deepcopy
from xml.sax.saxutils import escape
@ -1277,7 +1277,6 @@ class CatalogBuilder(object):
self.opts.log.info('%s' % _format_tag_list(genre_tags_dict, header="enabled genres"))
self.opts.log.info('%s' % _format_tag_list(excluded_tags, header="excluded genres"))
print("genre_tags_dict: %s" % genre_tags_dict)
return genre_tags_dict
def filter_excluded_genres(self, tags, regex):
@ -4856,7 +4855,13 @@ class CatalogBuilder(object):
self.progress_int = 0.01
self.reporter(self.progress_int, self.progress_string)
if self.opts.cli_environment:
self.opts.log(u"%3.0f%% %s" % (self.progress_int * 100, self.progress_string))
log_msg = u"%3.0f%% %s" % (self.progress_int * 100, self.progress_string)
if self.opts.verbose:
log_msg += " (%s)" % str(datetime.timedelta(seconds=int(time.time() - self.opts.start_time)))
else:
log_msg = ("%s (%s)" % (self.progress_string,
str(datetime.timedelta(seconds=int(time.time() - self.opts.start_time)))))
self.opts.log(log_msg)
def update_progress_micro_step(self, description, micro_step_pct):
""" Update calibre's job status UI.

156
src/calibre/utils/imghdr.py Normal file
View File

@ -0,0 +1,156 @@
"""Recognize image file formats based on their first few bytes."""
__all__ = ["what"]
#-------------------------#
# Recognize image headers #
#-------------------------#
def what(file, h=None):
if h is None:
if isinstance(file, basestring):
f = open(file, 'rb')
h = f.read(32)
else:
location = file.tell()
h = file.read(32)
file.seek(location)
f = None
else:
f = None
try:
for tf in tests:
res = tf(h, f)
if res:
return res
finally:
if f: f.close()
return None
#---------------------------------#
# Subroutines per image file type #
#---------------------------------#
tests = []
def test_jpeg(h, f):
"""JPEG data in JFIF format (Changed by Kovid to mimic the file utility,
the original code was failing with some jpegs that included ICC_PROFILE
data, for example: http://nationalpostnews.files.wordpress.com/2013/03/budget.jpeg?w=300&h=1571)"""
if (h[6:10] in (b'JFIF', b'Exif')) or (h[:2] == b'\xff\xd8' and b'JFIF' in h[:32]):
return 'jpeg'
tests.append(test_jpeg)
def test_png(h, f):
if h[:8] == "\211PNG\r\n\032\n":
return 'png'
tests.append(test_png)
def test_gif(h, f):
"""GIF ('87 and '89 variants)"""
if h[:6] in ('GIF87a', 'GIF89a'):
return 'gif'
tests.append(test_gif)
def test_tiff(h, f):
"""TIFF (can be in Motorola or Intel byte order)"""
if h[:2] in ('MM', 'II'):
return 'tiff'
tests.append(test_tiff)
def test_rgb(h, f):
"""SGI image library"""
if h[:2] == '\001\332':
return 'rgb'
tests.append(test_rgb)
def test_pbm(h, f):
"""PBM (portable bitmap)"""
if len(h) >= 3 and \
h[0] == 'P' and h[1] in '14' and h[2] in ' \t\n\r':
return 'pbm'
tests.append(test_pbm)
def test_pgm(h, f):
"""PGM (portable graymap)"""
if len(h) >= 3 and \
h[0] == 'P' and h[1] in '25' and h[2] in ' \t\n\r':
return 'pgm'
tests.append(test_pgm)
def test_ppm(h, f):
"""PPM (portable pixmap)"""
if len(h) >= 3 and \
h[0] == 'P' and h[1] in '36' and h[2] in ' \t\n\r':
return 'ppm'
tests.append(test_ppm)
def test_rast(h, f):
"""Sun raster file"""
if h[:4] == '\x59\xA6\x6A\x95':
return 'rast'
tests.append(test_rast)
def test_xbm(h, f):
"""X bitmap (X10 or X11)"""
s = '#define '
if h[:len(s)] == s:
return 'xbm'
tests.append(test_xbm)
def test_bmp(h, f):
if h[:2] == 'BM':
return 'bmp'
tests.append(test_bmp)
#--------------------#
# Small test program #
#--------------------#
def test():
import sys
recursive = 0
if sys.argv[1:] and sys.argv[1] == '-r':
del sys.argv[1:2]
recursive = 1
try:
if sys.argv[1:]:
testall(sys.argv[1:], recursive, 1)
else:
testall(['.'], recursive, 1)
except KeyboardInterrupt:
sys.stderr.write('\n[Interrupted]\n')
sys.exit(1)
def testall(list, recursive, toplevel):
import sys
import os
for filename in list:
if os.path.isdir(filename):
print filename + '/:',
if recursive or toplevel:
print 'recursing down:'
import glob
names = glob.glob(os.path.join(filename, '*'))
testall(names, recursive, 0)
else:
print '*** directory (use -r) ***'
else:
print filename + ':',
sys.stdout.flush()
try:
print what(filename)
except IOError:
print '*** not found ***'

View File

@ -184,7 +184,12 @@ class Feed(object):
id = 'internal id#%s'%self.id_counter
if id in self.added_articles:
return
published = item.get('date_parsed', time.gmtime())
published = None
for date_field in ('date_parsed', 'published_parsed',
'updated_parsed'):
published = item.get(date_field, None)
if published is not None:
break
if not published:
published = time.gmtime()
self.added_articles.append(id)

View File

@ -355,6 +355,10 @@ class BasicNewsRecipe(Recipe):
#: The minimum jpeg quality will be 5/100 so it is possible this constraint
#: will not be met. This parameter can be overridden by the parameter
#: compress_news_images_max_size which provides a fixed maximum size for images.
#: Note that if you enable scale_news_images_to_device then the image will
#: first be scaled and then its quality lowered until its size is less than
#: (w * h)/factor where w and h are now the *scaled* image dimensions. In
#: other words, this compression happens after scaling.
compress_news_images_auto_size = 16
#: Set jpeg quality so images do not exceed the size given (in KBytes).

View File

@ -7,7 +7,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
Fetch a webpage and its links recursively. The webpages are saved to disk in
UTF-8 encoding with any charset declarations removed.
'''
import sys, socket, os, urlparse, re, time, copy, urllib2, threading, traceback, imghdr
import sys, socket, os, urlparse, re, time, copy, urllib2, threading, traceback
from urllib import url2pathname, quote
from httplib import responses
from base64 import b64decode
@ -21,6 +21,7 @@ from calibre.utils.config import OptionParser
from calibre.utils.logging import Log
from calibre.utils.magick import Image
from calibre.utils.magick.draw import identify_data, thumbnail
from calibre.utils.imghdr import what
class FetchError(Exception):
pass
@ -413,7 +414,7 @@ class RecursiveFetcher(object):
fname = ascii_filename('img'+str(c))
if isinstance(fname, unicode):
fname = fname.encode('ascii', 'replace')
itype = imghdr.what(None, data)
itype = what(None, data)
if itype is None and b'<svg' in data[:1024]:
# SVG image
imgpath = os.path.join(diskpath, fname+'.svg')