mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
sync with Kovid's branch
This commit is contained in:
commit
c7635f262c
@ -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;}'
|
||||
|
||||
|
@ -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 - India’s leading tabloid has it all. To subscribe
|
||||
visit <a href="http://news.calibre-ebook.com/periodical/midday">calibre
|
||||
Periodicals</a>.'''
|
||||
language = 'en_IN'
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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 = {}
|
||||
|
@ -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())
|
||||
|
@ -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>&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>&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>&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>&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>&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>&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>&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>&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 &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>
|
||||
|
@ -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:
|
||||
|
@ -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): # {{{
|
||||
|
@ -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
|
||||
|
@ -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
156
src/calibre/utils/imghdr.py
Normal 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 ***'
|
@ -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)
|
||||
|
@ -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).
|
||||
|
@ -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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user