mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
0.8.68
This commit is contained in:
commit
4183aedf9f
@ -19,6 +19,48 @@
|
|||||||
# new recipes:
|
# new recipes:
|
||||||
# - title:
|
# - title:
|
||||||
|
|
||||||
|
- version: 0.8.68
|
||||||
|
date: 2012-09-07
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Drivers for the Nokia N9, Viewsonic 7e, Prestigio PER3274B and Coby Kyros 7035 "
|
||||||
|
tickets: [1046794,1046544]
|
||||||
|
|
||||||
|
- title: "Add a tutorial on creating catalogs to the User Manual and a link to it in the create catalogs dialog"
|
||||||
|
|
||||||
|
- title: "Wireless device connections: Add an option to force calibre to listen on a particular IP address. Access it by customizing the plugin in Preferences->Plugins"
|
||||||
|
|
||||||
|
- title: "Android driver: Add an extra customization option to configure the directory to which ebooks are sent on the storage cards."
|
||||||
|
tickets: [1045045]
|
||||||
|
|
||||||
|
- title: "Add an option under Preferences->Look & Feel->Book Details to hide the cover in the book details panel"
|
||||||
|
|
||||||
|
- title: "The Calibre Companion Android app that allows wireless connection of Android device to calibre is out of beta. See https://play.google.com/stor/apps/details?id=com.multipie.calibreandroid"
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Fix sorting by author not working in the device view in calibre when connected to iTunes"
|
||||||
|
tickets: [1044619]
|
||||||
|
|
||||||
|
- title: "Fix using the 'configure this device' menu action not validating settings"
|
||||||
|
|
||||||
|
- title: "Device drivers: Ignore corrupted entries in metadata.calibre, instead of raising an error"
|
||||||
|
|
||||||
|
- title: "PDF Output: Do not error out when generating an outline which points to pages that have been removed."
|
||||||
|
tickets: [1044799]
|
||||||
|
|
||||||
|
- title: "PDF Output: Fix incorrect page numbers being generated in the outline when converting some books"
|
||||||
|
|
||||||
|
- title: "PDF Output: Reduce memory consumption when writing out the PDF file, by using a stream"
|
||||||
|
|
||||||
|
- title: "EPUB metadata: When there are multiple <dc:date> tags use the one with the earliest date as the published date"
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Wall Street journal (subscription version)
|
||||||
|
- Houston Chronicle
|
||||||
|
- Various Romanian news sources
|
||||||
|
- Business Week Magazine
|
||||||
|
- Arcamax
|
||||||
|
|
||||||
- version: 0.8.67
|
- version: 0.8.67
|
||||||
date: 2012-08-31
|
date: 2012-08-31
|
||||||
|
|
||||||
|
@ -31,15 +31,13 @@ class Arcamax(BasicNewsRecipe):
|
|||||||
, 'language' : language
|
, 'language' : language
|
||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'class':['comics-header']}),
|
keep_only_tags = [dict(name='article', attrs={'class':['comic']}),
|
||||||
dict(name='b', attrs={'class':['current']}),
|
|
||||||
dict(name='article', attrs={'class':['comic']}),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
remove_tags = [dict(name='div', attrs={'id':['comicfull' ]}),
|
#remove_tags = [dict(name='div', attrs={'id':['comicfull' ]}),
|
||||||
dict(name='div', attrs={'class':['calendar' ]}),
|
#dict(name='div', attrs={'class':['calendar' ]}),
|
||||||
dict(name='nav', attrs={'class':['calendar-nav' ]}),
|
#dict(name='nav', attrs={'class':['calendar-nav' ]}),
|
||||||
]
|
#]
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
feeds = []
|
feeds = []
|
||||||
@ -48,20 +46,20 @@ class Arcamax(BasicNewsRecipe):
|
|||||||
#(u"9 Chickweed Lane", u"http://www.arcamax.com/ninechickweedlane"),
|
#(u"9 Chickweed Lane", u"http://www.arcamax.com/ninechickweedlane"),
|
||||||
#(u"Agnes", u"http://www.arcamax.com/agnes"),
|
#(u"Agnes", u"http://www.arcamax.com/agnes"),
|
||||||
#(u"Andy Capp", u"http://www.arcamax.com/andycapp"),
|
#(u"Andy Capp", u"http://www.arcamax.com/andycapp"),
|
||||||
(u"BC", u"http://www.arcamax.com/bc"),
|
(u"BC", u"http://www.arcamax.com/thefunnies/bc"),
|
||||||
#(u"Baby Blues", u"http://www.arcamax.com/babyblues"),
|
#(u"Baby Blues", u"http://www.arcamax.com/babyblues"),
|
||||||
#(u"Beetle Bailey", u"http://www.arcamax.com/beetlebailey"),
|
#(u"Beetle Bailey", u"http://www.arcamax.com/beetlebailey"),
|
||||||
(u"Blondie", u"http://www.arcamax.com/blondie"),
|
(u"Blondie", u"http://www.arcamax.com/thefunnies/blondie"),
|
||||||
#u"Boondocks", u"http://www.arcamax.com/boondocks"),
|
#u"Boondocks", u"http://www.arcamax.com/boondocks"),
|
||||||
#(u"Cathy", u"http://www.arcamax.com/cathy"),
|
#(u"Cathy", u"http://www.arcamax.com/cathy"),
|
||||||
#(u"Daddys Home", u"http://www.arcamax.com/daddyshome"),
|
#(u"Daddys Home", u"http://www.arcamax.com/daddyshome"),
|
||||||
(u"Dilbert", u"http://www.arcamax.com/dilbert"),
|
(u"Dilbert", u"http://www.arcamax.com/thefunnies/dilbert"),
|
||||||
#(u"Dinette Set", u"http://www.arcamax.com/thedinetteset"),
|
#(u"Dinette Set", u"http://www.arcamax.com/thedinetteset"),
|
||||||
(u"Dog Eat Doug", u"http://www.arcamax.com/dogeatdoug"),
|
(u"Dog Eat Doug", u"http://www.arcamax.com/thefunnies/dogeatdoug"),
|
||||||
(u"Doonesbury", u"http://www.arcamax.com/doonesbury"),
|
(u"Doonesbury", u"http://www.arcamax.com/thefunnies/doonesbury"),
|
||||||
#(u"Dustin", u"http://www.arcamax.com/dustin"),
|
#(u"Dustin", u"http://www.arcamax.com/dustin"),
|
||||||
(u"Family Circus", u"http://www.arcamax.com/familycircus"),
|
(u"Family Circus", u"http://www.arcamax.com/thefunnies/familycircus"),
|
||||||
(u"Garfield", u"http://www.arcamax.com/garfield"),
|
(u"Garfield", u"http://www.arcamax.com/thefunnies/garfield"),
|
||||||
#(u"Get Fuzzy", u"http://www.arcamax.com/getfuzzy"),
|
#(u"Get Fuzzy", u"http://www.arcamax.com/getfuzzy"),
|
||||||
#(u"Girls and Sports", u"http://www.arcamax.com/girlsandsports"),
|
#(u"Girls and Sports", u"http://www.arcamax.com/girlsandsports"),
|
||||||
#(u"Hagar the Horrible", u"http://www.arcamax.com/hagarthehorrible"),
|
#(u"Hagar the Horrible", u"http://www.arcamax.com/hagarthehorrible"),
|
||||||
@ -70,16 +68,16 @@ class Arcamax(BasicNewsRecipe):
|
|||||||
#(u"Luann", u"http://www.arcamax.com/luann"),
|
#(u"Luann", u"http://www.arcamax.com/luann"),
|
||||||
#(u"Momma", u"http://www.arcamax.com/momma"),
|
#(u"Momma", u"http://www.arcamax.com/momma"),
|
||||||
#(u"Mother Goose and Grimm", u"http://www.arcamax.com/mothergooseandgrimm"),
|
#(u"Mother Goose and Grimm", u"http://www.arcamax.com/mothergooseandgrimm"),
|
||||||
(u"Mutts", u"http://www.arcamax.com/mutts"),
|
(u"Mutts", u"http://www.arcamax.com/thefunnies/mutts"),
|
||||||
#(u"Non Sequitur", u"http://www.arcamax.com/nonsequitur"),
|
#(u"Non Sequitur", u"http://www.arcamax.com/nonsequitur"),
|
||||||
#(u"Pearls Before Swine", u"http://www.arcamax.com/pearlsbeforeswine"),
|
#(u"Pearls Before Swine", u"http://www.arcamax.com/pearlsbeforeswine"),
|
||||||
#(u"Pickles", u"http://www.arcamax.com/pickles"),
|
#(u"Pickles", u"http://www.arcamax.com/pickles"),
|
||||||
#(u"Red and Rover", u"http://www.arcamax.com/redandrover"),
|
#(u"Red and Rover", u"http://www.arcamax.com/redandrover"),
|
||||||
#(u"Rubes", u"http://www.arcamax.com/rubes"),
|
#(u"Rubes", u"http://www.arcamax.com/rubes"),
|
||||||
#(u"Rugrats", u"http://www.arcamax.com/rugrats"),
|
#(u"Rugrats", u"http://www.arcamax.com/rugrats"),
|
||||||
(u"Speed Bump", u"http://www.arcamax.com/speedbump"),
|
(u"Speed Bump", u"http://www.arcamax.com/thefunnies/speedbump"),
|
||||||
(u"Wizard of Id", u"http://www.arcamax.com/wizardofid"),
|
(u"Wizard of Id", u"http://www.arcamax.com/thefunnies/wizardofid"),
|
||||||
(u"Zits", u"http://www.arcamax.com/zits"),
|
(u"Zits", u"http://www.arcamax.com/thefunnies/zits"),
|
||||||
]:
|
]:
|
||||||
articles = self.make_links(url)
|
articles = self.make_links(url)
|
||||||
if articles:
|
if articles:
|
||||||
@ -93,11 +91,11 @@ class Arcamax(BasicNewsRecipe):
|
|||||||
for page in pages:
|
for page in pages:
|
||||||
page_soup = self.index_to_soup(url)
|
page_soup = self.index_to_soup(url)
|
||||||
if page_soup:
|
if page_soup:
|
||||||
title = self.tag_to_string(page_soup.find(name='div', attrs={'class':'comics-header'}).h1.contents[0])
|
title = self.tag_to_string(page_soup.find(name='div', attrs={'class':'columnheader'}).h1.contents[0])
|
||||||
page_url = url
|
page_url = url
|
||||||
# orig prev_page_url = 'http://www.arcamax.com' + page_soup.find('a', attrs={'class':'prev'}, text='Previous').parent['href']
|
# orig prev_page_url = 'http://www.arcamax.com' + page_soup.find('a', attrs={'class':'prev'}, text='Previous').parent['href']
|
||||||
prev_page_url = 'http://www.arcamax.com' + page_soup.find('span', text='Previous').parent.parent['href']
|
prev_page_url = 'http://www.arcamax.com' + page_soup.find(name='a', attrs={'class':['prev']})['href']
|
||||||
date = self.tag_to_string(page_soup.find(name='b', attrs={'class':['current']}))
|
date = self.tag_to_string(page_soup.find(name='span', attrs={'class':['cur']}))
|
||||||
current_articles.append({'title': title, 'url': page_url, 'description':'', 'date': date})
|
current_articles.append({'title': title, 'url': page_url, 'description':'', 'date': date})
|
||||||
url = prev_page_url
|
url = prev_page_url
|
||||||
current_articles.reverse()
|
current_articles.reverse()
|
||||||
@ -127,3 +125,4 @@ class Arcamax(BasicNewsRecipe):
|
|||||||
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = u'calibre'
|
__appname__ = u'calibre'
|
||||||
numeric_version = (0, 8, 67)
|
numeric_version = (0, 8, 68)
|
||||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ class ANDROID(USBMS):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def configure_for_kindle_app(cls):
|
def configure_for_kindle_app(cls):
|
||||||
proxy = cls._configProxy()
|
proxy = cls._configProxy()
|
||||||
proxy['format_map'] = ['mobi', 'azw', 'azw1', 'azw4', 'pdf']
|
proxy['format_map'] = ['azw3', 'mobi', 'azw', 'azw1', 'azw4', 'pdf']
|
||||||
proxy['use_subdirs'] = False
|
proxy['use_subdirs'] = False
|
||||||
proxy['extra_customization'] = [
|
proxy['extra_customization'] = [
|
||||||
','.join(['kindle']+cls.EBOOK_DIR_MAIN), '']
|
','.join(['kindle']+cls.EBOOK_DIR_MAIN), '']
|
||||||
|
@ -46,9 +46,8 @@ class MTPDeviceBase(DevicePlugin):
|
|||||||
def set_progress_reporter(self, report_progress):
|
def set_progress_reporter(self, report_progress):
|
||||||
self.report_progress = report_progress
|
self.report_progress = report_progress
|
||||||
|
|
||||||
@classmethod
|
def get_gui_name(self):
|
||||||
def get_gui_name(cls):
|
return getattr(self, 'current_friendly_name', self.gui_name)
|
||||||
return getattr(cls, 'current_friendly_name', cls.gui_name)
|
|
||||||
|
|
||||||
def is_usb_connected(self, devices_on_system, debug=False,
|
def is_usb_connected(self, devices_on_system, debug=False,
|
||||||
only_presence=False):
|
only_presence=False):
|
||||||
@ -60,13 +59,4 @@ class MTPDeviceBase(DevicePlugin):
|
|||||||
from calibre.devices.utils import build_template_regexp
|
from calibre.devices.utils import build_template_regexp
|
||||||
return build_template_regexp(self.save_template)
|
return build_template_regexp(self.save_template)
|
||||||
|
|
||||||
@property
|
|
||||||
def default_save_template(cls):
|
|
||||||
from calibre.library.save_to_disk import config
|
|
||||||
return config().parse().send_template
|
|
||||||
|
|
||||||
@property
|
|
||||||
def save_template(self):
|
|
||||||
# TODO: Use the device specific template here
|
|
||||||
return self.default_save_template
|
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from calibre import prints
|
|||||||
from calibre.constants import iswindows, numeric_version
|
from calibre.constants import iswindows, numeric_version
|
||||||
from calibre.devices.mtp.base import debug
|
from calibre.devices.mtp.base import debug
|
||||||
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
||||||
from calibre.utils.config import from_json, to_json
|
from calibre.utils.config import from_json, to_json, JSONConfig
|
||||||
from calibre.utils.date import now, isoformat
|
from calibre.utils.date import now, isoformat
|
||||||
|
|
||||||
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
||||||
@ -39,9 +39,40 @@ class MTP_DEVICE(BASE):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
BASE.__init__(self, *args, **kwargs)
|
BASE.__init__(self, *args, **kwargs)
|
||||||
self.plugboards = self.plugboard_func = None
|
self.plugboards = self.plugboard_func = None
|
||||||
|
self._prefs = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prefs(self):
|
||||||
|
if self._prefs is None:
|
||||||
|
from calibre.library.save_to_disk import config
|
||||||
|
self._prefs = p = JSONConfig('mtp_devices')
|
||||||
|
p.defaults['format_map'] = self.FORMATS
|
||||||
|
p.defaults['send_to'] = ['eBooks/import',
|
||||||
|
'wordplayer/calibretransfer', 'Books', 'sdcard/ebooks',
|
||||||
|
'eBooks', 'kindle']
|
||||||
|
p.defaults['send_template'] = config().parse().send_template
|
||||||
|
|
||||||
|
return self._prefs
|
||||||
|
|
||||||
|
def configure_for_kindle_app(self):
|
||||||
|
proxy = self.prefs
|
||||||
|
with proxy:
|
||||||
|
proxy['format_map'] = ['azw3', 'mobi', 'azw', 'azw1', 'azw4', 'pdf']
|
||||||
|
proxy['send_template'] = '{title} - {authors}'
|
||||||
|
orig = list(proxy['send_to'])
|
||||||
|
if 'kindle' in orig:
|
||||||
|
orig.remove('kindle')
|
||||||
|
orig.insert(0, 'kindle')
|
||||||
|
proxy['send_to'] = orig
|
||||||
|
|
||||||
|
def configure_for_generic_epub_app(self):
|
||||||
|
with self.prefs:
|
||||||
|
for x in ('format_map', 'send_template', 'send_to'):
|
||||||
|
del self.prefs[x]
|
||||||
|
|
||||||
def open(self, devices, library_uuid):
|
def open(self, devices, library_uuid):
|
||||||
self.current_library_uuid = library_uuid
|
self.current_library_uuid = library_uuid
|
||||||
|
self.location_paths = None
|
||||||
BASE.open(self, devices, library_uuid)
|
BASE.open(self, devices, library_uuid)
|
||||||
|
|
||||||
# Device information {{{
|
# Device information {{{
|
||||||
@ -248,8 +279,24 @@ class MTP_DEVICE(BASE):
|
|||||||
return tuple(x for x in filepath.split('/'))
|
return tuple(x for x in filepath.split('/'))
|
||||||
|
|
||||||
def prefix_for_location(self, on_card):
|
def prefix_for_location(self, on_card):
|
||||||
# TODO: Implement this
|
if self.location_paths is None:
|
||||||
return 'calibre'
|
self.location_paths = {}
|
||||||
|
for sid, loc in ( (self._main_id, None), (self._carda_id, 'carda'),
|
||||||
|
(self._cardb_id, 'cardb') ):
|
||||||
|
if sid is not None:
|
||||||
|
storage = self.filesystem_cache.storage(sid)
|
||||||
|
prefixes = self.get_pref('send_to')
|
||||||
|
p = None
|
||||||
|
for path in prefixes:
|
||||||
|
path = path.replace(os.sep, '/')
|
||||||
|
if storage.find_path(path.split('/')) is not None:
|
||||||
|
p = path
|
||||||
|
break
|
||||||
|
if p is None:
|
||||||
|
p = 'eBooks'
|
||||||
|
self.location_paths[loc] = p
|
||||||
|
|
||||||
|
return self.location_paths[on_card]
|
||||||
|
|
||||||
def ensure_parent(self, storage, path):
|
def ensure_parent(self, storage, path):
|
||||||
parent = storage
|
parent = storage
|
||||||
@ -366,14 +413,28 @@ class MTP_DEVICE(BASE):
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Settings {{{
|
# Settings {{{
|
||||||
@classmethod
|
|
||||||
|
def get_pref(self, key):
|
||||||
|
return self.prefs.get('device-%s'%self.current_serial_num, {}).get(key,
|
||||||
|
self.prefs[key])
|
||||||
|
|
||||||
|
def config_widget(self):
|
||||||
|
from calibre.gui2.device_drivers.mtp_config import MTPConfig
|
||||||
|
return MTPConfig(self)
|
||||||
|
|
||||||
|
def save_settings(self, cw):
|
||||||
|
cw.commit()
|
||||||
|
|
||||||
def settings(self):
|
def settings(self):
|
||||||
# TODO: Implement this
|
|
||||||
class Opts(object):
|
class Opts(object):
|
||||||
def __init__(s):
|
def __init__(s):
|
||||||
s.format_map = self.FORMATS
|
s.format_map = self.get_pref('format_map')
|
||||||
return Opts()
|
return Opts()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def save_template(self):
|
||||||
|
return self.prefs['send_template']
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@ -390,6 +451,7 @@ if __name__ == '__main__':
|
|||||||
dev.set_progress_reporter(prints)
|
dev.set_progress_reporter(prints)
|
||||||
dev.open(cd, None)
|
dev.open(cd, None)
|
||||||
dev.filesystem_cache.dump()
|
dev.filesystem_cache.dump()
|
||||||
|
print ('Prefix for main mem:', dev.prefix_for_location(None))
|
||||||
finally:
|
finally:
|
||||||
dev.shutdown()
|
dev.shutdown()
|
||||||
|
|
||||||
|
@ -36,13 +36,13 @@ class N770(USBMS):
|
|||||||
|
|
||||||
class N810(N770):
|
class N810(N770):
|
||||||
name = 'Nokia 810 Device Interface'
|
name = 'Nokia 810 Device Interface'
|
||||||
gui_name = 'Nokia 810/900'
|
gui_name = 'Nokia 810/900/9'
|
||||||
description = _('Communicate with the Nokia 810/900 internet tablet.')
|
description = _('Communicate with the Nokia 810/900 internet tablet.')
|
||||||
|
|
||||||
PRODUCT_ID = [0x96, 0x1c7]
|
PRODUCT_ID = [0x96, 0x1c7, 0x0518]
|
||||||
BCD = [0x316]
|
BCD = [0x316]
|
||||||
|
|
||||||
WINDOWS_MAIN_MEM = ['N810', 'N900']
|
WINDOWS_MAIN_MEM = ['N810', 'N900', 'NOKIA_N9']
|
||||||
|
|
||||||
MAIN_MEMORY_VOLUME_LABEL = 'Nokia Tablet Main Memory'
|
MAIN_MEMORY_VOLUME_LABEL = 'Nokia Tablet Main Memory'
|
||||||
|
|
||||||
|
@ -993,10 +993,10 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
if on_card == 'carda':
|
if on_card == 'carda':
|
||||||
candidates = self.get_carda_ebook_dir(for_upload=True)
|
candidates = self.get_carda_ebook_dir(for_upload=True)
|
||||||
path = get_dest_dir(self._carda_prefix, candidates)
|
path = get_dest_dir(self._card_a_prefix, candidates)
|
||||||
elif on_card == 'cardb':
|
elif on_card == 'cardb':
|
||||||
candidates = self.get_cardb_ebook_dir(for_upload=True)
|
candidates = self.get_cardb_ebook_dir(for_upload=True)
|
||||||
path = get_dest_dir(self._cardb_prefix, candidates)
|
path = get_dest_dir(self._card_b_prefix, candidates)
|
||||||
else:
|
else:
|
||||||
candidates = self.get_main_ebook_dir(for_upload=True)
|
candidates = self.get_main_ebook_dir(for_upload=True)
|
||||||
path = get_dest_dir(self._main_prefix, candidates)
|
path = get_dest_dir(self._main_prefix, candidates)
|
||||||
|
@ -328,6 +328,19 @@ def info_dialog(parent, title, msg, det_msg='', show=False,
|
|||||||
return d.exec_()
|
return d.exec_()
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
def show_restart_warning(msg, parent=None):
|
||||||
|
d = warning_dialog(parent, _('Restart needed'), msg,
|
||||||
|
show_copy_button=False)
|
||||||
|
b = d.bb.addButton(_('Restart calibre now'), d.bb.AcceptRole)
|
||||||
|
b.setIcon(QIcon(I('lt.png')))
|
||||||
|
d.do_restart = False
|
||||||
|
def rf():
|
||||||
|
d.do_restart = True
|
||||||
|
b.clicked.connect(rf)
|
||||||
|
d.set_details('')
|
||||||
|
d.exec_()
|
||||||
|
b.clicked.disconnect()
|
||||||
|
return d.do_restart
|
||||||
|
|
||||||
|
|
||||||
class Dispatcher(QObject):
|
class Dispatcher(QObject):
|
||||||
|
@ -19,7 +19,8 @@ from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog
|
|||||||
from calibre.utils.ipc.job import BaseJob
|
from calibre.utils.ipc.job import BaseJob
|
||||||
from calibre.devices.scanner import DeviceScanner
|
from calibre.devices.scanner import DeviceScanner
|
||||||
from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic,
|
from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic,
|
||||||
warning_dialog, info_dialog, choose_dir, FunctionDispatcher)
|
warning_dialog, info_dialog, choose_dir, FunctionDispatcher,
|
||||||
|
show_restart_warning)
|
||||||
from calibre.ebooks.metadata import authors_to_string
|
from calibre.ebooks.metadata import authors_to_string
|
||||||
from calibre import preferred_encoding, prints, force_unicode, as_unicode
|
from calibre import preferred_encoding, prints, force_unicode, as_unicode
|
||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename
|
||||||
@ -889,12 +890,16 @@ class DeviceMixin(object): # {{{
|
|||||||
bb.rejected.connect(d.reject)
|
bb.rejected.connect(d.reject)
|
||||||
l.addWidget(cw)
|
l.addWidget(cw)
|
||||||
l.addWidget(bb)
|
l.addWidget(bb)
|
||||||
|
def validate():
|
||||||
|
if cw.validate():
|
||||||
|
QDialog.accept(d)
|
||||||
|
d.accept = validate
|
||||||
if d.exec_() == d.Accepted:
|
if d.exec_() == d.Accepted:
|
||||||
dev.save_settings(cw)
|
dev.save_settings(cw)
|
||||||
warning_dialog(self, _('Disconnect device'),
|
do_restart = show_restart_warning(_('Restart calibre for the changes to %s'
|
||||||
_('Disconnect and re-connect the %s for your changes to'
|
' to be applied.')%dev.get_gui_name(), parent=self)
|
||||||
' be applied.')%dev.get_gui_name(), show=True,
|
if do_restart:
|
||||||
show_copy_button=False)
|
self.quit(restart=True)
|
||||||
|
|
||||||
def _sync_action_triggered(self, *args):
|
def _sync_action_triggered(self, *args):
|
||||||
m = getattr(self, '_sync_menu', None)
|
m = getattr(self, '_sync_menu', None)
|
||||||
@ -972,6 +977,7 @@ class DeviceMixin(object): # {{{
|
|||||||
connected = False
|
connected = False
|
||||||
self.set_device_menu_items_state(connected)
|
self.set_device_menu_items_state(connected)
|
||||||
if connected:
|
if connected:
|
||||||
|
self.device_connected = device_kind
|
||||||
self.device_manager.get_device_information(\
|
self.device_manager.get_device_information(\
|
||||||
FunctionDispatcher(self.info_read))
|
FunctionDispatcher(self.info_read))
|
||||||
self.set_default_thumbnail(\
|
self.set_default_thumbnail(\
|
||||||
@ -979,7 +985,6 @@ class DeviceMixin(object): # {{{
|
|||||||
self.status_bar.show_message(_('Device: ')+\
|
self.status_bar.show_message(_('Device: ')+\
|
||||||
self.device_manager.device.get_gui_name()+\
|
self.device_manager.device.get_gui_name()+\
|
||||||
_(' detected.'), 3000)
|
_(' detected.'), 3000)
|
||||||
self.device_connected = device_kind
|
|
||||||
self.library_view.set_device_connected(self.device_connected)
|
self.library_view.set_device_connected(self.device_connected)
|
||||||
self.refresh_ondevice(reset_only=True)
|
self.refresh_ondevice(reset_only=True)
|
||||||
else:
|
else:
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
from PyQt4.Qt import QWidget, QListWidgetItem, Qt, QVariant, SIGNAL, \
|
from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QVariant, QLabel,
|
||||||
QLabel, QLineEdit, QCheckBox
|
QLineEdit, QCheckBox)
|
||||||
|
|
||||||
from calibre.gui2 import error_dialog, question_dialog
|
from calibre.gui2 import error_dialog, question_dialog
|
||||||
from calibre.gui2.device_drivers.configwidget_ui import Ui_ConfigWidget
|
from calibre.gui2.device_drivers.configwidget_ui import Ui_ConfigWidget
|
||||||
@ -40,8 +40,8 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
|
|||||||
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
|
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
|
||||||
item.setCheckState(Qt.Checked if format in format_map else Qt.Unchecked)
|
item.setCheckState(Qt.Checked if format in format_map else Qt.Unchecked)
|
||||||
|
|
||||||
self.connect(self.column_up, SIGNAL('clicked()'), self.up_column)
|
self.column_up.clicked.connect(self.up_column)
|
||||||
self.connect(self.column_down, SIGNAL('clicked()'), self.down_column)
|
self.column_down.clicked.connect(self.down_column)
|
||||||
|
|
||||||
if device.HIDE_FORMATS_CONFIG_BOX:
|
if device.HIDE_FORMATS_CONFIG_BOX:
|
||||||
self.groupBox.hide()
|
self.groupBox.hide()
|
||||||
@ -157,3 +157,5 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
|
|||||||
'<br>'+unicode(err), show=True)
|
'<br>'+unicode(err), show=True)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
239
src/calibre/gui2/device_drivers/mtp_config.py
Normal file
239
src/calibre/gui2/device_drivers/mtp_config.py
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import weakref
|
||||||
|
|
||||||
|
from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QToolButton, QLabel,
|
||||||
|
QTabWidget, QGridLayout, QListWidget, QIcon, QLineEdit, QVBoxLayout,
|
||||||
|
QPushButton)
|
||||||
|
|
||||||
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
|
from calibre.gui2 import error_dialog
|
||||||
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||||
|
|
||||||
|
class FormatsConfig(QWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, all_formats, format_map):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
self.l = l = QGridLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
|
||||||
|
self.f = f = QListWidget(self)
|
||||||
|
l.addWidget(f, 0, 0, 3, 1)
|
||||||
|
unchecked_formats = sorted(all_formats - set(format_map))
|
||||||
|
for fmt in format_map + unchecked_formats:
|
||||||
|
item = QListWidgetItem(fmt, f)
|
||||||
|
item.setData(Qt.UserRole, fmt)
|
||||||
|
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
|
||||||
|
item.setCheckState(Qt.Checked if fmt in format_map else Qt.Unchecked)
|
||||||
|
|
||||||
|
self.button_up = b = QToolButton(self)
|
||||||
|
b.setIcon(QIcon(I('arrow-up.png')))
|
||||||
|
l.addWidget(b, 0, 1)
|
||||||
|
b.clicked.connect(self.up)
|
||||||
|
|
||||||
|
self.button_down = b = QToolButton(self)
|
||||||
|
b.setIcon(QIcon(I('arrow-down.png')))
|
||||||
|
l.addWidget(b, 2, 1)
|
||||||
|
b.clicked.connect(self.down)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def format_map(self):
|
||||||
|
return [unicode(self.f.item(i).data(Qt.UserRole).toString()) for i in
|
||||||
|
xrange(self.f.count()) if self.f.item(i).checkState()==Qt.Checked]
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
if not self.format_map:
|
||||||
|
error_dialog(self, _('No formats selected'),
|
||||||
|
_('You must choose at least one format to send to the'
|
||||||
|
' device'), show=True)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def up(self):
|
||||||
|
idx = self.f.currentRow()
|
||||||
|
if idx > 0:
|
||||||
|
self.f.insertItem(idx-1, self.f.takeItem(idx))
|
||||||
|
self.f.setCurrentRow(idx-1)
|
||||||
|
|
||||||
|
def down(self):
|
||||||
|
idx = self.f.currentRow()
|
||||||
|
if idx < self.f.count()-1:
|
||||||
|
self.f.insertItem(idx+1, self.f.takeItem(idx))
|
||||||
|
self.f.setCurrentRow(idx+1)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class TemplateConfig(QWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
self.t = t = QLineEdit(self)
|
||||||
|
t.setText(val or '')
|
||||||
|
t.setCursorPosition(0)
|
||||||
|
self.setMinimumWidth(400)
|
||||||
|
self.l = l = QGridLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
self.m = m = QLabel('<p>'+_('''<b>Save &template</b> to control the filename and
|
||||||
|
location of files sent to the device:'''))
|
||||||
|
m.setWordWrap(True)
|
||||||
|
m.setBuddy(t)
|
||||||
|
l.addWidget(m, 0, 0, 1, 2)
|
||||||
|
l.addWidget(t, 1, 0, 1, 1)
|
||||||
|
b = self.b = QPushButton(_('Template editor'))
|
||||||
|
l.addWidget(b, 1, 1, 1, 1)
|
||||||
|
b.clicked.connect(self.edit_template)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def template(self):
|
||||||
|
return unicode(self.t.text()).strip()
|
||||||
|
|
||||||
|
def edit_template(self):
|
||||||
|
t = TemplateDialog(self, self.template)
|
||||||
|
t.setWindowTitle(_('Edit template'))
|
||||||
|
if t.exec_():
|
||||||
|
self.t.setText(t.rule[1])
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
from calibre.utils.formatter import validation_formatter
|
||||||
|
tmpl = self.template
|
||||||
|
try:
|
||||||
|
validation_formatter.validate(tmpl)
|
||||||
|
return True
|
||||||
|
except Exception as err:
|
||||||
|
error_dialog(self, _('Invalid template'),
|
||||||
|
'<p>'+_('The template %s is invalid:')%tmpl + \
|
||||||
|
'<br>'+unicode(err), show=True)
|
||||||
|
|
||||||
|
return False
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class SendToConfig(QWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
self.t = t = QLineEdit(self)
|
||||||
|
t.setText(', '.join(val or []))
|
||||||
|
t.setCursorPosition(0)
|
||||||
|
self.l = l = QVBoxLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
self.m = m = QLabel('<p>'+_('''A <b>list of &folders</b> on the device to
|
||||||
|
which to send ebooks. The first one that exists will be used:'''))
|
||||||
|
m.setWordWrap(True)
|
||||||
|
m.setBuddy(t)
|
||||||
|
l.addWidget(m)
|
||||||
|
l.addWidget(t)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
ans = [x.strip() for x in unicode(self.t.text()).strip().split(',')]
|
||||||
|
return [x for x in ans if x]
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class MTPConfig(QTabWidget):
|
||||||
|
|
||||||
|
def __init__(self, device, parent=None):
|
||||||
|
QTabWidget.__init__(self, parent)
|
||||||
|
self._device = weakref.ref(device)
|
||||||
|
|
||||||
|
cd = msg = None
|
||||||
|
if device.current_friendly_name is not None:
|
||||||
|
if device.current_serial_num is None:
|
||||||
|
msg = '<p>' + _('The <b>%s</b> device has no serial number, '
|
||||||
|
'it cannot be configured'%device.current_friendly_name)
|
||||||
|
else:
|
||||||
|
cd = 'device-'+device.current_serial_num
|
||||||
|
else:
|
||||||
|
msg = '<p>' + _('<b>No MTP device connected.</b><p>'
|
||||||
|
' You can only configure the MTP device plugin when a device'
|
||||||
|
' is connected.')
|
||||||
|
|
||||||
|
self.current_device_key = cd
|
||||||
|
|
||||||
|
if msg:
|
||||||
|
msg += '<p>' + _('If you want to un-ignore a previously'
|
||||||
|
' ignored MTP device, use the "Ignored devices" tab.')
|
||||||
|
l = QLabel(msg)
|
||||||
|
l.setWordWrap(True)
|
||||||
|
l.setStyleSheet('QLabel { margin-left: 2em }')
|
||||||
|
self.insertTab(0, l, _('Cannot configure'))
|
||||||
|
else:
|
||||||
|
self.base = QWidget(self)
|
||||||
|
self.insertTab(0, self.base, _('Configure %s')%self.device.current_friendly_name)
|
||||||
|
l = self.base.l = QGridLayout(self.base)
|
||||||
|
self.base.setLayout(l)
|
||||||
|
|
||||||
|
self.formats = FormatsConfig(set(BOOK_EXTENSIONS),
|
||||||
|
self.get_pref('format_map'))
|
||||||
|
self.send_to = SendToConfig(self.get_pref('send_to'))
|
||||||
|
self.template = TemplateConfig(self.get_pref('send_template'))
|
||||||
|
self.base.la = la = QLabel(_('Choose the formats to send to the %s')%self.device.current_friendly_name)
|
||||||
|
la.setWordWrap(True)
|
||||||
|
l.addWidget(la, 0, 0, 1, 1)
|
||||||
|
l.addWidget(self.formats, 1, 0, 3, 1)
|
||||||
|
l.addWidget(self.send_to, 1, 1, 1, 1)
|
||||||
|
l.addWidget(self.template, 2, 1, 1, 1)
|
||||||
|
l.setRowStretch(2, 10)
|
||||||
|
|
||||||
|
self.setCurrentIndex(0)
|
||||||
|
|
||||||
|
def get_pref(self, key):
|
||||||
|
p = self.device.prefs.get(self.current_device_key, {})
|
||||||
|
if not p:
|
||||||
|
self.device.prefs[self.current_device_key] = p
|
||||||
|
return p.get(key, self.device.prefs[key])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device(self):
|
||||||
|
return self._device()
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
if not self.formats.validate():
|
||||||
|
return False
|
||||||
|
if not self.template.validate():
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
p = self.device.prefs.get(self.current_device_key, {})
|
||||||
|
|
||||||
|
p.pop('format_map', None)
|
||||||
|
f = self.formats.format_map
|
||||||
|
if f and f != self.device.prefs['format_map']:
|
||||||
|
p['format_map'] = f
|
||||||
|
|
||||||
|
p.pop('send_template', None)
|
||||||
|
t = self.template.template
|
||||||
|
if t and t != self.device.prefs['send_template']:
|
||||||
|
p['send_template'] = t
|
||||||
|
|
||||||
|
p.pop('send_to', None)
|
||||||
|
s = self.send_to.value
|
||||||
|
if s and s != self.device.prefs['send_to']:
|
||||||
|
p['send_to'] = s
|
||||||
|
|
||||||
|
self.device.prefs[self.current_device_key] = p
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from calibre.gui2 import Application
|
||||||
|
from calibre.devices.mtp.driver import MTP_DEVICE
|
||||||
|
from calibre.devices.scanner import DeviceScanner
|
||||||
|
s = DeviceScanner()
|
||||||
|
s.scan()
|
||||||
|
app = Application([])
|
||||||
|
dev = MTP_DEVICE(None)
|
||||||
|
dev.startup()
|
||||||
|
cd = dev.detect_managed_devices(s.devices)
|
||||||
|
dev.open(cd, 'test')
|
||||||
|
cw = dev.config_widget()
|
||||||
|
cw.show()
|
||||||
|
app.exec_()
|
||||||
|
dev.shutdown()
|
||||||
|
|
||||||
|
|
@ -9,14 +9,14 @@ import textwrap
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \
|
from PyQt4.Qt import (QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget,
|
||||||
QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \
|
QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence,
|
||||||
QToolBar, QSize, pyqtSignal, QPixmap, QToolButton, QAction, \
|
QToolBar, QSize, pyqtSignal, QPixmap, QToolButton, QAction,
|
||||||
QDialogButtonBox, QHBoxLayout
|
QDialogButtonBox, QHBoxLayout)
|
||||||
|
|
||||||
from calibre.constants import __appname__, __version__, islinux
|
from calibre.constants import __appname__, __version__, islinux
|
||||||
from calibre.gui2 import gprefs, min_available_height, available_width, \
|
from calibre.gui2 import (gprefs, min_available_height, available_width,
|
||||||
warning_dialog
|
show_restart_warning)
|
||||||
from calibre.gui2.preferences import init_gui, AbortCommit, get_plugin
|
from calibre.gui2.preferences import init_gui, AbortCommit, get_plugin
|
||||||
from calibre.customize.ui import preferences_plugins
|
from calibre.customize.ui import preferences_plugins
|
||||||
|
|
||||||
@ -346,19 +346,8 @@ class Preferences(QMainWindow):
|
|||||||
'restarted immediately. You will not be allowed to '
|
'restarted immediately. You will not be allowed to '
|
||||||
'set any more preferences, until you restart.')
|
'set any more preferences, until you restart.')
|
||||||
|
|
||||||
|
do_restart = show_restart_warning(msg, parent=self)
|
||||||
|
|
||||||
d = warning_dialog(self, _('Restart needed'), msg,
|
|
||||||
show_copy_button=False)
|
|
||||||
b = d.bb.addButton(_('Restart calibre now'), d.bb.AcceptRole)
|
|
||||||
b.setIcon(QIcon(I('lt.png')))
|
|
||||||
d.do_restart = False
|
|
||||||
def rf():
|
|
||||||
d.do_restart = True
|
|
||||||
b.clicked.connect(rf)
|
|
||||||
d.set_details('')
|
|
||||||
d.exec_()
|
|
||||||
b.clicked.disconnect()
|
|
||||||
do_restart = d.do_restart
|
|
||||||
self.showing_widget.refresh_gui(self.gui)
|
self.showing_widget.refresh_gui(self.gui)
|
||||||
self.hide_plugin()
|
self.hide_plugin()
|
||||||
if self.close_after_initial or (must_restart and rc) or do_restart:
|
if self.close_after_initial or (must_restart and rc) or do_restart:
|
||||||
|
@ -267,7 +267,7 @@ class Android(Device):
|
|||||||
def commit(cls):
|
def commit(cls):
|
||||||
super(Android, cls).commit()
|
super(Android, cls).commit()
|
||||||
for plugin in device_plugins(include_disabled=True):
|
for plugin in device_plugins(include_disabled=True):
|
||||||
if plugin.name == 'Android driver':
|
if hasattr(plugin, 'configure_for_generic_epub_app'):
|
||||||
plugin.configure_for_generic_epub_app()
|
plugin.configure_for_generic_epub_app()
|
||||||
|
|
||||||
class AndroidTablet(Android):
|
class AndroidTablet(Android):
|
||||||
@ -287,7 +287,7 @@ class AndroidPhoneWithKindle(Android):
|
|||||||
def commit(cls):
|
def commit(cls):
|
||||||
super(Android, cls).commit()
|
super(Android, cls).commit()
|
||||||
for plugin in device_plugins(include_disabled=True):
|
for plugin in device_plugins(include_disabled=True):
|
||||||
if plugin.name == 'Android driver':
|
if hasattr(plugin, 'configure_for_kindle_app'):
|
||||||
plugin.configure_for_kindle_app()
|
plugin.configure_for_kindle_app()
|
||||||
|
|
||||||
class AndroidTabletWithKindle(AndroidPhoneWithKindle):
|
class AndroidTabletWithKindle(AndroidPhoneWithKindle):
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user