mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Merge from trunk
This commit is contained in:
commit
d488847a9d
@ -1,105 +0,0 @@
|
|||||||
__license__ = 'GPL v3'
|
|
||||||
__copyright__ = '2008 Kovid Goyal kovid@kovidgoyal.net, 2010 Darko Miletic <darko.miletic at gmail.com>'
|
|
||||||
'''
|
|
||||||
www.businessweek.com
|
|
||||||
'''
|
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
|
||||||
|
|
||||||
class BusinessWeek(BasicNewsRecipe):
|
|
||||||
title = 'Business Week'
|
|
||||||
__author__ = 'Kovid Goyal and Darko Miletic'
|
|
||||||
description = 'Read the latest international business news & stock market news. Get updated company profiles, financial advice, global economy and technology news.'
|
|
||||||
publisher = 'Bloomberg L.P.'
|
|
||||||
category = 'Business, business news, stock market, stock market news, financial advice, company profiles, financial advice, global economy, technology news'
|
|
||||||
oldest_article = 7
|
|
||||||
max_articles_per_feed = 200
|
|
||||||
no_stylesheets = True
|
|
||||||
encoding = 'utf8'
|
|
||||||
use_embedded_content = False
|
|
||||||
language = 'en'
|
|
||||||
remove_empty_feeds = True
|
|
||||||
publication_type = 'magazine'
|
|
||||||
cover_url = 'http://images.businessweek.com/mz/covers/current_120x160.jpg'
|
|
||||||
masthead_url = 'http://assets.businessweek.com/images/bw-logo.png'
|
|
||||||
extra_css = """
|
|
||||||
body{font-family: Helvetica,Arial,sans-serif }
|
|
||||||
img{margin-bottom: 0.4em; display:block}
|
|
||||||
.tagline{color: gray; font-style: italic}
|
|
||||||
.photoCredit{font-size: small; color: gray}
|
|
||||||
"""
|
|
||||||
|
|
||||||
conversion_options = {
|
|
||||||
'comment' : description
|
|
||||||
, 'tags' : category
|
|
||||||
, 'publisher' : publisher
|
|
||||||
, 'language' : language
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_tags = [
|
|
||||||
dict(attrs={'class':'inStory'})
|
|
||||||
,dict(name=['meta','link','iframe','base','embed','object','table','th','tr','td'])
|
|
||||||
,dict(attrs={'id':['inset','videoDisplay']})
|
|
||||||
]
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':['story-body','storyBody','article_body','articleBody']})]
|
|
||||||
remove_attributes = ['lang']
|
|
||||||
match_regexps = [r'http://www.businessweek.com/.*_page_[1-9].*']
|
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
|
||||||
(u'Top Stories', u'http://www.businessweek.com/topStories/rss/topStories.rss'),
|
|
||||||
(u'Top News' , u'http://www.businessweek.com/rss/bwdaily.rss' ),
|
|
||||||
(u'Asia', u'http://www.businessweek.com/rss/asia.rss'),
|
|
||||||
(u'Autos', u'http://www.businessweek.com/rss/autos/index.rss'),
|
|
||||||
(u'Classic Cars', u'http://rss.businessweek.com/bw_rss/classiccars'),
|
|
||||||
(u'Hybrids', u'http://rss.businessweek.com/bw_rss/hybrids'),
|
|
||||||
(u'Europe', u'http://www.businessweek.com/rss/europe.rss'),
|
|
||||||
(u'Auto Reviews', u'http://rss.businessweek.com/bw_rss/autoreviews'),
|
|
||||||
(u'Innovation & Design', u'http://www.businessweek.com/rss/innovate.rss'),
|
|
||||||
(u'Architecture', u'http://www.businessweek.com/rss/architecture.rss'),
|
|
||||||
(u'Brand Equity', u'http://www.businessweek.com/rss/brandequity.rss'),
|
|
||||||
(u'Auto Design', u'http://www.businessweek.com/rss/carbuff.rss'),
|
|
||||||
(u'Game Room', u'http://rss.businessweek.com/bw_rss/gameroom'),
|
|
||||||
(u'Technology', u'http://www.businessweek.com/rss/technology.rss'),
|
|
||||||
(u'Investing', u'http://rss.businessweek.com/bw_rss/investor'),
|
|
||||||
(u'Small Business', u'http://www.businessweek.com/rss/smallbiz.rss'),
|
|
||||||
(u'Careers', u'http://rss.businessweek.com/bw_rss/careers'),
|
|
||||||
(u'B-Schools', u'http://www.businessweek.com/rss/bschools.rss'),
|
|
||||||
(u'Magazine Selections', u'http://www.businessweek.com/rss/magazine.rss'),
|
|
||||||
(u'CEO Guide to Tech', u'http://www.businessweek.com/rss/ceo_guide_tech.rss'),
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_article_url(self, article):
|
|
||||||
url = article.get('guid', None)
|
|
||||||
if 'podcasts' in url:
|
|
||||||
return None
|
|
||||||
if 'surveys' in url:
|
|
||||||
return None
|
|
||||||
if 'images' in url:
|
|
||||||
return None
|
|
||||||
if 'feedroom' in url:
|
|
||||||
return None
|
|
||||||
if '/magazine/toc/' in url:
|
|
||||||
return None
|
|
||||||
rurl, sep, rest = url.rpartition('?')
|
|
||||||
if rurl:
|
|
||||||
return rurl
|
|
||||||
return rest
|
|
||||||
|
|
||||||
def print_version(self, url):
|
|
||||||
if '/news/' in url or '/blog/' in url:
|
|
||||||
return url
|
|
||||||
if '/magazine' in url:
|
|
||||||
rurl = url.replace('http://www.businessweek.com/','http://www.businessweek.com/printer/')
|
|
||||||
else:
|
|
||||||
rurl = url.replace('http://www.businessweek.com/','http://www.businessweek.com/print/')
|
|
||||||
return rurl.replace('/investing/','/investor/')
|
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
|
||||||
for item in soup.findAll(style=True):
|
|
||||||
del item['style']
|
|
||||||
for alink in soup.findAll('a'):
|
|
||||||
if alink.string is not None:
|
|
||||||
tstr = alink.string
|
|
||||||
alink.replaceWith(tstr)
|
|
||||||
return soup
|
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008-2011, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2008-2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
'''
|
'''
|
||||||
www.nin.co.rs
|
www.nin.co.rs
|
||||||
'''
|
'''
|
||||||
@ -15,11 +15,11 @@ class Nin(BasicNewsRecipe):
|
|||||||
publisher = 'NIN d.o.o. - Ringier d.o.o.'
|
publisher = 'NIN d.o.o. - Ringier d.o.o.'
|
||||||
category = 'news, politics, Serbia'
|
category = 'news, politics, Serbia'
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
delay = 1
|
|
||||||
oldest_article = 15
|
oldest_article = 15
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
needs_subscription = True
|
needs_subscription = True
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
|
auto_cleanup = False
|
||||||
PREFIX = 'http://www.nin.co.rs'
|
PREFIX = 'http://www.nin.co.rs'
|
||||||
INDEX = PREFIX + '/?change_lang=ls'
|
INDEX = PREFIX + '/?change_lang=ls'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
@ -63,7 +63,11 @@ class Nin(BasicNewsRecipe):
|
|||||||
keep_only_tags =[dict(name='td', attrs={'width':'520'})]
|
keep_only_tags =[dict(name='td', attrs={'width':'520'})]
|
||||||
remove_tags_before =dict(name='span', attrs={'class':'izjava'})
|
remove_tags_before =dict(name='span', attrs={'class':'izjava'})
|
||||||
remove_tags_after =dict(name='html')
|
remove_tags_after =dict(name='html')
|
||||||
remove_tags = [dict(name=['object','link','iframe','meta','base'])]
|
remove_tags = [
|
||||||
|
dict(name=['object','link','iframe','meta','base'])
|
||||||
|
,dict(attrs={'class':['fb-like','twitter-share-button']})
|
||||||
|
,dict(attrs={'rel':'nofollow'})
|
||||||
|
]
|
||||||
remove_attributes=['border','background','height','width','align','valign']
|
remove_attributes=['border','background','height','width','align','valign']
|
||||||
|
|
||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
@ -78,10 +82,6 @@ class Nin(BasicNewsRecipe):
|
|||||||
|
|
||||||
feeds = [(u'NIN Online', u'http://www.nin.co.rs/misc/rss.php?feed=RSS2.0')]
|
feeds = [(u'NIN Online', u'http://www.nin.co.rs/misc/rss.php?feed=RSS2.0')]
|
||||||
|
|
||||||
def get_article_url(self, article):
|
|
||||||
url = BasicNewsRecipe.get_article_url(self, article)
|
|
||||||
return url.replace('.co.yu', '.co.rs')
|
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
for item in soup.findAll(style=True):
|
for item in soup.findAll(style=True):
|
||||||
del item['style']
|
del item['style']
|
||||||
@ -99,4 +99,3 @@ class Nin(BasicNewsRecipe):
|
|||||||
img.extract()
|
img.extract()
|
||||||
tbl.replaceWith(img)
|
tbl.replaceWith(img)
|
||||||
return soup
|
return soup
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ let g:syntastic_cpp_include_dirs = [
|
|||||||
let g:syntastic_c_include_dirs = g:syntastic_cpp_include_dirs
|
let g:syntastic_c_include_dirs = g:syntastic_cpp_include_dirs
|
||||||
|
|
||||||
set wildignore+=resources/viewer/mathjax/**
|
set wildignore+=resources/viewer/mathjax/**
|
||||||
|
set wildignore+=build/**
|
||||||
|
|
||||||
fun! CalibreLog()
|
fun! CalibreLog()
|
||||||
" Setup buffers to edit the calibre changelog and version info prior to
|
" Setup buffers to edit the calibre changelog and version info prior to
|
||||||
|
@ -381,7 +381,6 @@ class Win32Freeze(Command, WixMixIn):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def build_portable_installer(self):
|
def build_portable_installer(self):
|
||||||
base = self.portable_base
|
|
||||||
zf = self.a(self.j('dist', 'calibre-portable-%s.zip.lz'%VERSION))
|
zf = self.a(self.j('dist', 'calibre-portable-%s.zip.lz'%VERSION))
|
||||||
usz = os.path.getsize(zf)
|
usz = os.path.getsize(zf)
|
||||||
def cc(src, obj):
|
def cc(src, obj):
|
||||||
@ -442,7 +441,7 @@ class Win32Freeze(Command, WixMixIn):
|
|||||||
'/RELEASE',
|
'/RELEASE',
|
||||||
'/ENTRY:wWinMainCRTStartup',
|
'/ENTRY:wWinMainCRTStartup',
|
||||||
'/OUT:'+exe, self.embed_resources(exe),
|
'/OUT:'+exe, self.embed_resources(exe),
|
||||||
obj, 'User32.lib', 'Shlwapi.lib']
|
obj, 'User32.lib']
|
||||||
self.run_builder(cmd)
|
self.run_builder(cmd)
|
||||||
|
|
||||||
self.info('Creating portable installer')
|
self.info('Creating portable installer')
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <Shlwapi.h>
|
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -90,7 +89,7 @@ LPTSTR get_app_dir() {
|
|||||||
return buf3;
|
return buf3;
|
||||||
}
|
}
|
||||||
|
|
||||||
void launch_calibre(LPCTSTR exe, LPCTSTR config_dir, LPCTSTR library_dir) {
|
void launch_calibre(LPCTSTR exe, LPCTSTR config_dir) {
|
||||||
DWORD dwFlags=0;
|
DWORD dwFlags=0;
|
||||||
STARTUPINFO si;
|
STARTUPINFO si;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
@ -108,13 +107,12 @@ void launch_calibre(LPCTSTR exe, LPCTSTR config_dir, LPCTSTR library_dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP;
|
dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP;
|
||||||
_sntprintf_s(cmdline, BUFSIZE, _TRUNCATE, _T(" \"--with-library=%s\""), library_dir);
|
|
||||||
|
|
||||||
ZeroMemory( &si, sizeof(si) );
|
ZeroMemory( &si, sizeof(si) );
|
||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
ZeroMemory( &pi, sizeof(pi) );
|
ZeroMemory( &pi, sizeof(pi) );
|
||||||
|
|
||||||
fSuccess = CreateProcess(exe, cmdline,
|
fSuccess = CreateProcess(exe, NULL,
|
||||||
NULL, // Process handle not inheritable
|
NULL, // Process handle not inheritable
|
||||||
NULL, // Thread handle not inheritable
|
NULL, // Thread handle not inheritable
|
||||||
FALSE, // Set handle inheritance to FALSE
|
FALSE, // Set handle inheritance to FALSE
|
||||||
@ -135,45 +133,6 @@ void launch_calibre(LPCTSTR exe, LPCTSTR config_dir, LPCTSTR library_dir) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL is_dots(LPCTSTR name) {
|
|
||||||
return _tcscmp(name, _T(".")) == 0 || _tcscmp(name, _T("..")) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void find_calibre_library(LPTSTR library_dir) {
|
|
||||||
TCHAR base[BUFSIZE] = {0}, buf[BUFSIZE] = {0};
|
|
||||||
WIN32_FIND_DATA fdFile;
|
|
||||||
HANDLE hFind = NULL;
|
|
||||||
|
|
||||||
_sntprintf_s(buf, BUFSIZE, _TRUNCATE, _T("%s\\metadata.db"), base);
|
|
||||||
|
|
||||||
if (PathFileExists(buf)) return; // Calibre Library/metadata.db exists, we use it
|
|
||||||
|
|
||||||
_tcscpy(base, library_dir);
|
|
||||||
PathRemoveFileSpec(base);
|
|
||||||
|
|
||||||
_sntprintf_s(buf, BUFSIZE, _TRUNCATE, _T("%s\\*"), base);
|
|
||||||
|
|
||||||
// Look for some other folder that contains a metadata.db file inside the Calibre Portable folder
|
|
||||||
if((hFind = FindFirstFileEx(buf, FindExInfoStandard, &fdFile, FindExSearchLimitToDirectories, NULL, 0))
|
|
||||||
!= INVALID_HANDLE_VALUE) {
|
|
||||||
do {
|
|
||||||
if(is_dots(fdFile.cFileName)) continue;
|
|
||||||
|
|
||||||
if(fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
||||||
_sntprintf_s(buf, BUFSIZE, _TRUNCATE, _T("%s\\%s\\metadata.db"), base, fdFile.cFileName);
|
|
||||||
if (PathFileExists(buf)) {
|
|
||||||
// some dir/metadata.db exists, we use it as the library
|
|
||||||
PathRemoveFileSpec(buf);
|
|
||||||
_tcscpy(library_dir, buf);
|
|
||||||
FindClose(hFind);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while(FindNextFile(hFind, &fdFile));
|
|
||||||
FindClose(hFind);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
||||||
{
|
{
|
||||||
@ -181,26 +140,14 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
|||||||
|
|
||||||
app_dir = get_app_dir();
|
app_dir = get_app_dir();
|
||||||
config_dir = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
config_dir = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
||||||
library_dir = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
|
||||||
exe = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
exe = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR));
|
||||||
|
|
||||||
_sntprintf_s(config_dir, BUFSIZE, _TRUNCATE, _T("%sCalibre Settings"), app_dir);
|
_sntprintf_s(config_dir, BUFSIZE, _TRUNCATE, _T("%sCalibre Settings"), app_dir);
|
||||||
_sntprintf_s(exe, BUFSIZE, _TRUNCATE, _T("%sCalibre\\calibre.exe"), app_dir);
|
_sntprintf_s(exe, BUFSIZE, _TRUNCATE, _T("%sCalibre\\calibre.exe"), app_dir);
|
||||||
_sntprintf_s(library_dir, BUFSIZE, _TRUNCATE, _T("%sCalibre Library"), app_dir);
|
|
||||||
|
|
||||||
find_calibre_library(library_dir);
|
launch_calibre(exe, config_dir);
|
||||||
|
|
||||||
if ( _tcscnlen(library_dir, BUFSIZE) <= 74 ) {
|
free(app_dir); free(config_dir); free(exe);
|
||||||
launch_calibre(exe, config_dir, library_dir);
|
|
||||||
} else {
|
|
||||||
too_long = (LPTSTR)calloc(BUFSIZE+300, sizeof(TCHAR));
|
|
||||||
_sntprintf_s(too_long, BUFSIZE+300, _TRUNCATE,
|
|
||||||
_T("Path to Calibre Portable (%s) too long. Must be less than 59 characters."), app_dir);
|
|
||||||
|
|
||||||
show_error(too_long);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(app_dir); free(config_dir); free(exe); free(library_dir);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -177,6 +177,11 @@ def get_version():
|
|||||||
v += '*'
|
v += '*'
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
def get_portable_base():
|
||||||
|
'Return path to the directory that contains calibre-portable.exe or None'
|
||||||
|
if isportable:
|
||||||
|
return os.path.dirname(os.path.dirname(os.environ['CALIBRE_PORTABLE_BUILD']))
|
||||||
|
|
||||||
def get_unicode_windows_env_var(name):
|
def get_unicode_windows_env_var(name):
|
||||||
import ctypes
|
import ctypes
|
||||||
name = unicode(name)
|
name = unicode(name)
|
||||||
|
@ -674,6 +674,7 @@ from calibre.devices.kobo.driver import KOBO
|
|||||||
from calibre.devices.bambook.driver import BAMBOOK
|
from calibre.devices.bambook.driver import BAMBOOK
|
||||||
from calibre.devices.boeye.driver import BOEYE_BEX, BOEYE_BDX
|
from calibre.devices.boeye.driver import BOEYE_BEX, BOEYE_BDX
|
||||||
from calibre.devices.smart_device_app.driver import SMART_DEVICE_APP
|
from calibre.devices.smart_device_app.driver import SMART_DEVICE_APP
|
||||||
|
from calibre.devices.mtp.driver import MTP_DEVICE
|
||||||
|
|
||||||
# Order here matters. The first matched device is the one used.
|
# Order here matters. The first matched device is the one used.
|
||||||
plugins += [
|
plugins += [
|
||||||
@ -745,14 +746,11 @@ plugins += [
|
|||||||
ITUNES,
|
ITUNES,
|
||||||
BOEYE_BEX,
|
BOEYE_BEX,
|
||||||
BOEYE_BDX,
|
BOEYE_BDX,
|
||||||
|
MTP_DEVICE,
|
||||||
SMART_DEVICE_APP,
|
SMART_DEVICE_APP,
|
||||||
USER_DEFINED,
|
USER_DEFINED,
|
||||||
]
|
]
|
||||||
|
|
||||||
from calibre.utils.config_base import tweaks
|
|
||||||
if tweaks.get('test_mtp_driver', False):
|
|
||||||
from calibre.devices.mtp.driver import MTP_DEVICE
|
|
||||||
plugins.append(MTP_DEVICE)
|
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
Embedded console for debugging.
|
Embedded console for debugging.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import sys, os
|
import sys, os, functools
|
||||||
from calibre.utils.config import OptionParser
|
from calibre.utils.config import OptionParser
|
||||||
from calibre.constants import iswindows
|
from calibre.constants import iswindows
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
@ -160,26 +160,32 @@ def add_simple_plugin(path_to_plugin):
|
|||||||
os.chdir(odir)
|
os.chdir(odir)
|
||||||
shutil.rmtree(tdir)
|
shutil.rmtree(tdir)
|
||||||
|
|
||||||
def run_debug_gui(logpath):
|
def print_basic_debug_info(out=None):
|
||||||
import time, platform
|
if out is None: out = sys.stdout
|
||||||
time.sleep(3) # Give previous GUI time to shutdown fully and release locks
|
out = functools.partial(prints, file=out)
|
||||||
from calibre.constants import __appname__, __version__, isosx
|
import platform
|
||||||
print __appname__, _('Debug log')
|
from calibre.constants import __appname__, get_version, isportable, isosx
|
||||||
print __appname__, __version__
|
out(__appname__, get_version(), 'Portable' if isportable else '')
|
||||||
print platform.platform()
|
out(platform.platform(), platform.system())
|
||||||
print platform.system()
|
out(platform.system_alias(platform.system(), platform.release(),
|
||||||
print platform.system_alias(platform.system(), platform.release(),
|
platform.version()))
|
||||||
platform.version())
|
out('Python', platform.python_version())
|
||||||
print 'Python', platform.python_version()
|
|
||||||
try:
|
try:
|
||||||
if iswindows:
|
if iswindows:
|
||||||
print 'Windows:', platform.win32_ver()
|
out('Windows:', platform.win32_ver())
|
||||||
elif isosx:
|
elif isosx:
|
||||||
print 'OSX:', platform.mac_ver()
|
out('OSX:', platform.mac_ver())
|
||||||
else:
|
else:
|
||||||
print 'Linux:', platform.linux_distribution()
|
out('Linux:', platform.linux_distribution())
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def run_debug_gui(logpath):
|
||||||
|
import time
|
||||||
|
time.sleep(3) # Give previous GUI time to shutdown fully and release locks
|
||||||
|
from calibre.constants import __appname__
|
||||||
|
prints(__appname__, _('Debug log'))
|
||||||
|
print_basic_debug_info()
|
||||||
from calibre.gui2.main import main
|
from calibre.gui2.main import main
|
||||||
main(['__CALIBRE_GUI_DEBUG__', logpath])
|
main(['__CALIBRE_GUI_DEBUG__', logpath])
|
||||||
|
|
||||||
@ -206,6 +212,7 @@ def main(args=sys.argv):
|
|||||||
opts, args = option_parser().parse_args(args)
|
opts, args = option_parser().parse_args(args)
|
||||||
if opts.gui:
|
if opts.gui:
|
||||||
from calibre.gui2.main import main
|
from calibre.gui2.main import main
|
||||||
|
print_basic_debug_info()
|
||||||
main(['calibre'])
|
main(['calibre'])
|
||||||
elif opts.gui_debug is not None:
|
elif opts.gui_debug is not None:
|
||||||
run_debug_gui(opts.gui_debug)
|
run_debug_gui(opts.gui_debug)
|
||||||
|
@ -62,10 +62,11 @@ def debug(ioreg_to_tmp=False, buf=None, plugins=None):
|
|||||||
already have been called (for example in the main GUI), pass in the list of
|
already have been called (for example in the main GUI), pass in the list of
|
||||||
device plugins as the plugins parameter.
|
device plugins as the plugins parameter.
|
||||||
'''
|
'''
|
||||||
import textwrap, platform
|
import textwrap
|
||||||
from calibre.customize.ui import device_plugins
|
from calibre.customize.ui import device_plugins
|
||||||
|
from calibre.debug import print_basic_debug_info
|
||||||
from calibre.devices.scanner import DeviceScanner, win_pnp_drives
|
from calibre.devices.scanner import DeviceScanner, win_pnp_drives
|
||||||
from calibre.constants import iswindows, isosx, __version__
|
from calibre.constants import iswindows, isosx
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
oldo, olde = sys.stdout, sys.stderr
|
oldo, olde = sys.stdout, sys.stderr
|
||||||
|
|
||||||
@ -85,21 +86,7 @@ def debug(ioreg_to_tmp=False, buf=None, plugins=None):
|
|||||||
out('Startup failed for device plugin: %s'%d)
|
out('Startup failed for device plugin: %s'%d)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
out('Calibre Version:', __version__)
|
print_basic_debug_info(out=buf)
|
||||||
out(platform.platform(), platform.system())
|
|
||||||
out(platform.system_alias(platform.system(), platform.release(),
|
|
||||||
platform.version()))
|
|
||||||
out('Python', platform.python_version())
|
|
||||||
try:
|
|
||||||
if iswindows:
|
|
||||||
out('Windows:', platform.win32_ver())
|
|
||||||
elif isosx:
|
|
||||||
out('OSX:', platform.mac_ver())
|
|
||||||
else:
|
|
||||||
out('Linux:', platform.linux_distribution())
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
s = DeviceScanner()
|
s = DeviceScanner()
|
||||||
s.scan()
|
s.scan()
|
||||||
devices = (s.devices)
|
devices = (s.devices)
|
||||||
|
@ -226,6 +226,7 @@ class TREKSTOR(USBMS):
|
|||||||
VENDOR_ID = [0x1e68]
|
VENDOR_ID = [0x1e68]
|
||||||
PRODUCT_ID = [0x0041, 0x0042, 0x0052, 0x004e, 0x0056,
|
PRODUCT_ID = [0x0041, 0x0042, 0x0052, 0x004e, 0x0056,
|
||||||
0x003e, # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091
|
0x003e, # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091
|
||||||
|
0x5cL, # This is for the 4ink http://www.mobileread.com/forums/showthread.php?t=191318
|
||||||
]
|
]
|
||||||
BCD = [0x0002, 0x100]
|
BCD = [0x0002, 0x100]
|
||||||
|
|
||||||
|
54
src/calibre/devices/mtp/defaults.py
Normal file
54
src/calibre/devices/mtp/defaults.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#!/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 traceback, re
|
||||||
|
|
||||||
|
from calibre.constants import iswindows
|
||||||
|
|
||||||
|
class DeviceDefaults(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.rules = (
|
||||||
|
# Amazon devices
|
||||||
|
({'vendor':0x1949}, {
|
||||||
|
'format_map': ['azw3', 'mobi', 'azw',
|
||||||
|
'azw1', 'azw4', 'pdf'],
|
||||||
|
'send_to': ['documents', 'books', 'kindle'],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __call__(self, device, driver):
|
||||||
|
if iswindows:
|
||||||
|
vid = pid = 0xffff
|
||||||
|
m = re.search(r'(?i)vid_([0-9a-fA-F]+)&pid_([0-9a-fA-F]+)', device)
|
||||||
|
if m is not None:
|
||||||
|
try:
|
||||||
|
vid, pid = int(m.group(1), 16), int(m.group(2), 16)
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
else:
|
||||||
|
vid, pid = device.vendor_id, device.product_id
|
||||||
|
|
||||||
|
for rule in self.rules:
|
||||||
|
tests = rule[0]
|
||||||
|
matches = True
|
||||||
|
for k, v in tests.iteritems():
|
||||||
|
if k == 'vendor' and v != vid:
|
||||||
|
matches = False
|
||||||
|
break
|
||||||
|
if k == 'product' and v != pid:
|
||||||
|
matches = False
|
||||||
|
break
|
||||||
|
if matches:
|
||||||
|
return rule[1]
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
@ -14,9 +14,11 @@ from itertools import izip
|
|||||||
from calibre import prints
|
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.devices.mtp.defaults import DeviceDefaults
|
||||||
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
||||||
from calibre.utils.config import from_json, to_json, JSONConfig
|
from calibre.utils.config import from_json, to_json, JSONConfig
|
||||||
from calibre.utils.date import now, isoformat, utcnow
|
from calibre.utils.date import now, isoformat, utcnow
|
||||||
|
from calibre.utils.filenames import shorten_components_to
|
||||||
|
|
||||||
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
||||||
'windows' if iswindows else 'unix')).MTP_DEVICE
|
'windows' if iswindows else 'unix')).MTP_DEVICE
|
||||||
@ -41,6 +43,8 @@ class MTP_DEVICE(BASE):
|
|||||||
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
|
self._prefs = None
|
||||||
|
self.device_defaults = DeviceDefaults()
|
||||||
|
self.current_device_defaults = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prefs(self):
|
def prefs(self):
|
||||||
@ -73,17 +77,19 @@ class MTP_DEVICE(BASE):
|
|||||||
for x in ('format_map', 'send_template', 'send_to'):
|
for x in ('format_map', 'send_template', 'send_to'):
|
||||||
del self.prefs[x]
|
del self.prefs[x]
|
||||||
|
|
||||||
def open(self, devices, library_uuid):
|
def open(self, device, library_uuid):
|
||||||
self.current_library_uuid = library_uuid
|
self.current_library_uuid = library_uuid
|
||||||
self.location_paths = None
|
self.location_paths = None
|
||||||
self.driveinfo = {}
|
self.driveinfo = {}
|
||||||
BASE.open(self, devices, library_uuid)
|
BASE.open(self, device, library_uuid)
|
||||||
h = self.prefs['history']
|
h = self.prefs['history']
|
||||||
if self.current_serial_num:
|
if self.current_serial_num:
|
||||||
h[self.current_serial_num] = (self.current_friendly_name,
|
h[self.current_serial_num] = (self.current_friendly_name,
|
||||||
isoformat(utcnow()))
|
isoformat(utcnow()))
|
||||||
self.prefs['history'] = h
|
self.prefs['history'] = h
|
||||||
|
|
||||||
|
self.current_device_defaults = self.device_defaults(device, self)
|
||||||
|
|
||||||
# Device information {{{
|
# Device information {{{
|
||||||
def _update_drive_info(self, storage, location_code, name=None):
|
def _update_drive_info(self, storage, location_code, name=None):
|
||||||
import uuid
|
import uuid
|
||||||
@ -264,7 +270,11 @@ class MTP_DEVICE(BASE):
|
|||||||
continue
|
continue
|
||||||
base = os.path.join(tdir, '%s'%f.object_id)
|
base = os.path.join(tdir, '%s'%f.object_id)
|
||||||
os.mkdir(base)
|
os.mkdir(base)
|
||||||
with open(os.path.join(base, f.name), 'wb') as out:
|
name = f.name
|
||||||
|
if iswindows:
|
||||||
|
plen = len(base)
|
||||||
|
name = ''.join(shorten_components_to(245-plen, [name]))
|
||||||
|
with open(os.path.join(base, name), 'wb') as out:
|
||||||
try:
|
try:
|
||||||
self.get_mtp_file(f, out)
|
self.get_mtp_file(f, out)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -434,8 +444,13 @@ class MTP_DEVICE(BASE):
|
|||||||
# Settings {{{
|
# Settings {{{
|
||||||
|
|
||||||
def get_pref(self, key):
|
def get_pref(self, key):
|
||||||
return self.prefs.get('device-%s'%self.current_serial_num, {}).get(key,
|
''' Get the setting named key. First looks for a device specific setting.
|
||||||
self.prefs[key])
|
If that is not found looks for a device default and if that is not
|
||||||
|
found uses the global default.'''
|
||||||
|
dd = self.current_device_defaults if self.is_mtp_device_connected else {}
|
||||||
|
dev_settings = self.prefs.get('device-%s'%self.current_serial_num, {})
|
||||||
|
default_value = dd.get(key, self.prefs[key])
|
||||||
|
return dev_settings.get(key, default_value)
|
||||||
|
|
||||||
def config_widget(self):
|
def config_widget(self):
|
||||||
from calibre.gui2.device_drivers.mtp_config import MTPConfig
|
from calibre.gui2.device_drivers.mtp_config import MTPConfig
|
||||||
@ -452,7 +467,7 @@ class MTP_DEVICE(BASE):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def save_template(self):
|
def save_template(self):
|
||||||
return self.prefs['send_template']
|
return self.get_pref('send_template')
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ from calibre.utils.date import local_tz, as_utc
|
|||||||
from calibre.utils.icu import sort_key, lower
|
from calibre.utils.icu import sort_key, lower
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
|
|
||||||
bexts = frozenset(BOOK_EXTENSIONS)
|
bexts = frozenset(BOOK_EXTENSIONS) - {'mbp', 'tan', 'rar', 'zip', 'xml'}
|
||||||
|
|
||||||
class FileOrFolder(object):
|
class FileOrFolder(object):
|
||||||
|
|
||||||
|
@ -137,6 +137,10 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
self.currently_connected_dev = None
|
self.currently_connected_dev = None
|
||||||
self.current_serial_num = None
|
self.current_serial_num = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_mtp_device_connected(self):
|
||||||
|
return self.currently_connected_dev is not None
|
||||||
|
|
||||||
@synchronous
|
@synchronous
|
||||||
def startup(self):
|
def startup(self):
|
||||||
p = plugins['libmtp']
|
p = plugins['libmtp']
|
||||||
@ -189,6 +193,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
if not self.current_friendly_name:
|
if not self.current_friendly_name:
|
||||||
self.current_friendly_name = self.dev.model_name or _('Unknown MTP device')
|
self.current_friendly_name = self.dev.model_name or _('Unknown MTP device')
|
||||||
self.current_serial_num = snum
|
self.current_serial_num = snum
|
||||||
|
self.currently_connected_dev = connected_device
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filesystem_cache(self):
|
def filesystem_cache(self):
|
||||||
@ -297,14 +302,16 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
def get_mtp_file(self, f, stream=None, callback=None):
|
def get_mtp_file(self, f, stream=None, callback=None):
|
||||||
if f.is_folder:
|
if f.is_folder:
|
||||||
raise ValueError('%s if a folder'%(f.full_path,))
|
raise ValueError('%s if a folder'%(f.full_path,))
|
||||||
|
set_name = stream is None
|
||||||
if stream is None:
|
if stream is None:
|
||||||
stream = SpooledTemporaryFile(5*1024*1024, '_wpd_receive_file.dat')
|
stream = SpooledTemporaryFile(5*1024*1024, '_wpd_receive_file.dat')
|
||||||
stream.name = f.name
|
|
||||||
ok, errs = self.dev.get_file(f.object_id, stream, callback)
|
ok, errs = self.dev.get_file(f.object_id, stream, callback)
|
||||||
if not ok:
|
if not ok:
|
||||||
raise DeviceError('Failed to get file: %s with errors: %s'%(
|
raise DeviceError('Failed to get file: %s with errors: %s'%(
|
||||||
f.full_path, self.format_errorstack(errs)))
|
f.full_path, self.format_errorstack(errs)))
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
|
if set_name:
|
||||||
|
stream.name = f.name
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
@synchronous
|
@synchronous
|
||||||
|
@ -246,6 +246,10 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
self.dev = self._filesystem_cache = None
|
self.dev = self._filesystem_cache = None
|
||||||
self.current_serial_num = None
|
self.current_serial_num = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_mtp_device_connected(self):
|
||||||
|
return self.currently_connected_pnp_id is not None
|
||||||
|
|
||||||
def eject(self):
|
def eject(self):
|
||||||
if self.currently_connected_pnp_id is None: return
|
if self.currently_connected_pnp_id is None: return
|
||||||
self.eject_dev_on_next_scan = True
|
self.eject_dev_on_next_scan = True
|
||||||
@ -321,9 +325,9 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
def get_mtp_file(self, f, stream=None, callback=None):
|
def get_mtp_file(self, f, stream=None, callback=None):
|
||||||
if f.is_folder:
|
if f.is_folder:
|
||||||
raise ValueError('%s if a folder'%(f.full_path,))
|
raise ValueError('%s if a folder'%(f.full_path,))
|
||||||
|
set_name = stream is None
|
||||||
if stream is None:
|
if stream is None:
|
||||||
stream = SpooledTemporaryFile(5*1024*1024, '_wpd_receive_file.dat')
|
stream = SpooledTemporaryFile(5*1024*1024, '_wpd_receive_file.dat')
|
||||||
stream.name = f.name
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
self.dev.get_file(f.object_id, stream, callback)
|
self.dev.get_file(f.object_id, stream, callback)
|
||||||
@ -332,8 +336,10 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
self.dev.get_file(f.object_id, stream, callback)
|
self.dev.get_file(f.object_id, stream, callback)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise DeviceError('Failed to fetch the file %s with error: %s'%
|
raise DeviceError('Failed to fetch the file %s with error: %s'%
|
||||||
f.full_path, as_unicode(e))
|
(f.full_path, as_unicode(e)))
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
|
if set_name:
|
||||||
|
stream.name = f.name
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
@same_thread
|
@same_thread
|
||||||
|
@ -96,7 +96,7 @@ class SVGRasterizer(object):
|
|||||||
|
|
||||||
def dataize_manifest(self):
|
def dataize_manifest(self):
|
||||||
for item in self.oeb.manifest.values():
|
for item in self.oeb.manifest.values():
|
||||||
if item.media_type == SVG_MIME:
|
if item.media_type == SVG_MIME and item.data is not None:
|
||||||
self.dataize_svg(item)
|
self.dataize_svg(item)
|
||||||
|
|
||||||
def dataize_svg(self, item, svg=None):
|
def dataize_svg(self, item, svg=None):
|
||||||
|
@ -13,7 +13,7 @@ from PyQt4.Qt import (QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt,
|
|||||||
ORG_NAME = 'KovidsBrain'
|
ORG_NAME = 'KovidsBrain'
|
||||||
APP_UID = 'libprs500'
|
APP_UID = 'libprs500'
|
||||||
from calibre.constants import (islinux, iswindows, isbsd, isfrozen, isosx,
|
from calibre.constants import (islinux, iswindows, isbsd, isfrozen, isosx,
|
||||||
config_dir, filesystem_encoding)
|
plugins, config_dir, filesystem_encoding, DEBUG)
|
||||||
from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
|
from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
from calibre.utils.date import UNDEFINED_DATE
|
from calibre.utils.date import UNDEFINED_DATE
|
||||||
@ -567,7 +567,8 @@ class FileDialog(QObject):
|
|||||||
modal = True,
|
modal = True,
|
||||||
name = '',
|
name = '',
|
||||||
mode = QFileDialog.ExistingFiles,
|
mode = QFileDialog.ExistingFiles,
|
||||||
default_dir='~'
|
default_dir='~',
|
||||||
|
no_save_dir=False
|
||||||
):
|
):
|
||||||
QObject.__init__(self)
|
QObject.__init__(self)
|
||||||
ftext = ''
|
ftext = ''
|
||||||
@ -586,6 +587,9 @@ class FileDialog(QObject):
|
|||||||
self.selected_files = None
|
self.selected_files = None
|
||||||
self.fd = None
|
self.fd = None
|
||||||
|
|
||||||
|
if no_save_dir:
|
||||||
|
initial_dir = os.path.expanduser(default_dir)
|
||||||
|
else:
|
||||||
initial_dir = dynamic.get(self.dialog_name,
|
initial_dir = dynamic.get(self.dialog_name,
|
||||||
os.path.expanduser(default_dir))
|
os.path.expanduser(default_dir))
|
||||||
if not isinstance(initial_dir, basestring):
|
if not isinstance(initial_dir, basestring):
|
||||||
@ -629,6 +633,7 @@ class FileDialog(QObject):
|
|||||||
saved_loc = self.selected_files[0]
|
saved_loc = self.selected_files[0]
|
||||||
if os.path.isfile(saved_loc):
|
if os.path.isfile(saved_loc):
|
||||||
saved_loc = os.path.dirname(saved_loc)
|
saved_loc = os.path.dirname(saved_loc)
|
||||||
|
if not no_save_dir:
|
||||||
dynamic[self.dialog_name] = saved_loc
|
dynamic[self.dialog_name] = saved_loc
|
||||||
self.accepted = bool(self.selected_files)
|
self.accepted = bool(self.selected_files)
|
||||||
|
|
||||||
@ -638,10 +643,10 @@ class FileDialog(QObject):
|
|||||||
return tuple(self.selected_files)
|
return tuple(self.selected_files)
|
||||||
|
|
||||||
|
|
||||||
def choose_dir(window, name, title, default_dir='~'):
|
def choose_dir(window, name, title, default_dir='~', no_save_dir=False):
|
||||||
fd = FileDialog(title=title, filters=[], add_all_files_filter=False,
|
fd = FileDialog(title=title, filters=[], add_all_files_filter=False,
|
||||||
parent=window, name=name, mode=QFileDialog.Directory,
|
parent=window, name=name, mode=QFileDialog.Directory,
|
||||||
default_dir=default_dir)
|
default_dir=default_dir, no_save_dir=no_save_dir)
|
||||||
dir = fd.get_files()
|
dir = fd.get_files()
|
||||||
fd.setParent(None)
|
fd.setParent(None)
|
||||||
if dir:
|
if dir:
|
||||||
@ -759,6 +764,9 @@ class Application(QApplication):
|
|||||||
if override_program_name:
|
if override_program_name:
|
||||||
args = [override_program_name] + args[1:]
|
args = [override_program_name] + args[1:]
|
||||||
qargs = [i.encode('utf-8') if isinstance(i, unicode) else i for i in args]
|
qargs = [i.encode('utf-8') if isinstance(i, unicode) else i for i in args]
|
||||||
|
self.pi = plugins['progress_indicator'][0]
|
||||||
|
if DEBUG:
|
||||||
|
self.redirect_notify = True
|
||||||
QApplication.__init__(self, qargs)
|
QApplication.__init__(self, qargs)
|
||||||
global gui_thread, qt_app
|
global gui_thread, qt_app
|
||||||
gui_thread = QThread.currentThread()
|
gui_thread = QThread.currentThread()
|
||||||
@ -769,15 +777,23 @@ class Application(QApplication):
|
|||||||
self._file_open_lock = RLock()
|
self._file_open_lock = RLock()
|
||||||
self.setup_styles(force_calibre_style)
|
self.setup_styles(force_calibre_style)
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
def notify(self, receiver, event):
|
||||||
|
if self.redirect_notify:
|
||||||
|
self.redirect_notify = False
|
||||||
|
return self.pi.do_notify(receiver, event)
|
||||||
|
else:
|
||||||
|
ret = QApplication.notify(self, receiver, event)
|
||||||
|
self.redirect_notify = True
|
||||||
|
return ret
|
||||||
|
|
||||||
def load_calibre_style(self):
|
def load_calibre_style(self):
|
||||||
# On OS X QtCurve resets the palette, so we preserve it explicitly
|
# On OS X QtCurve resets the palette, so we preserve it explicitly
|
||||||
orig_pal = QPalette(self.palette())
|
orig_pal = QPalette(self.palette())
|
||||||
|
|
||||||
from calibre.constants import plugins
|
|
||||||
pi = plugins['progress_indicator'][0]
|
|
||||||
path = os.path.join(sys.extensions_location, 'calibre_style.'+(
|
path = os.path.join(sys.extensions_location, 'calibre_style.'+(
|
||||||
'pyd' if iswindows else 'so'))
|
'pyd' if iswindows else 'so'))
|
||||||
pi.load_style(path, 'Calibre')
|
self.pi.load_style(path, 'Calibre')
|
||||||
# On OSX, on some machines, colors can be invalid. See https://bugs.launchpad.net/bugs/1014900
|
# On OSX, on some machines, colors can be invalid. See https://bugs.launchpad.net/bugs/1014900
|
||||||
for role in (orig_pal.Button, orig_pal.Window):
|
for role in (orig_pal.Button, orig_pal.Window):
|
||||||
c = orig_pal.brush(role).color()
|
c = orig_pal.brush(role).color()
|
||||||
|
@ -5,7 +5,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os, posixpath
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
|
from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
|
||||||
@ -13,7 +13,8 @@ from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
|
|||||||
QCoreApplication, pyqtSignal)
|
QCoreApplication, pyqtSignal)
|
||||||
|
|
||||||
from calibre import isbytestring, sanitize_file_name_unicode
|
from calibre import isbytestring, sanitize_file_name_unicode
|
||||||
from calibre.constants import filesystem_encoding, iswindows
|
from calibre.constants import (filesystem_encoding, iswindows,
|
||||||
|
get_portable_base)
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog,
|
from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog,
|
||||||
question_dialog, info_dialog, open_local_file, choose_dir)
|
question_dialog, info_dialog, open_local_file, choose_dir)
|
||||||
@ -25,6 +26,17 @@ class LibraryUsageStats(object): # {{{
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.stats = {}
|
self.stats = {}
|
||||||
self.read_stats()
|
self.read_stats()
|
||||||
|
base = get_portable_base()
|
||||||
|
if base is not None:
|
||||||
|
lp = prefs['library_path']
|
||||||
|
if lp:
|
||||||
|
# Rename the current library. Renaming of other libraries is
|
||||||
|
# handled by the switch function
|
||||||
|
q = os.path.basename(lp)
|
||||||
|
for loc in list(self.stats.iterkeys()):
|
||||||
|
bn = posixpath.basename(loc)
|
||||||
|
if bn.lower() == q.lower():
|
||||||
|
self.rename(loc, lp)
|
||||||
|
|
||||||
def read_stats(self):
|
def read_stats(self):
|
||||||
stats = gprefs.get('library_usage_stats', {})
|
stats = gprefs.get('library_usage_stats', {})
|
||||||
@ -417,6 +429,18 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
finally:
|
finally:
|
||||||
self.gui.status_bar.clear_message()
|
self.gui.status_bar.clear_message()
|
||||||
|
|
||||||
|
def look_for_portable_lib(self, db, location):
|
||||||
|
base = get_portable_base()
|
||||||
|
if base is None:
|
||||||
|
return False, None
|
||||||
|
loc = location.replace('/', os.sep)
|
||||||
|
candidate = os.path.join(base, os.path.basename(loc))
|
||||||
|
if db.exists_at(candidate):
|
||||||
|
newloc = candidate.replace(os.sep, '/')
|
||||||
|
self.stats.rename(location, newloc)
|
||||||
|
return True, newloc
|
||||||
|
return False, None
|
||||||
|
|
||||||
def switch_requested(self, location):
|
def switch_requested(self, location):
|
||||||
if not self.change_library_allowed():
|
if not self.change_library_allowed():
|
||||||
return
|
return
|
||||||
@ -425,6 +449,12 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
self.view_state_map[current_lib] = self.preserve_state_on_switch.state
|
self.view_state_map[current_lib] = self.preserve_state_on_switch.state
|
||||||
loc = location.replace('/', os.sep)
|
loc = location.replace('/', os.sep)
|
||||||
exists = db.exists_at(loc)
|
exists = db.exists_at(loc)
|
||||||
|
if not exists:
|
||||||
|
exists, new_location = self.look_for_portable_lib(db, location)
|
||||||
|
if exists:
|
||||||
|
location = new_location
|
||||||
|
loc = location.replace('/', os.sep)
|
||||||
|
|
||||||
if not exists:
|
if not exists:
|
||||||
d = MovedDialog(self.stats, location, self.gui)
|
d = MovedDialog(self.stats, location, self.gui)
|
||||||
ret = d.exec_()
|
ret = d.exec_()
|
||||||
|
@ -163,7 +163,7 @@ class IgnoredDevices(QWidget): # {{{
|
|||||||
self.l = l = QVBoxLayout()
|
self.l = l = QVBoxLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
self.la = la = QLabel('<p>'+_(
|
self.la = la = QLabel('<p>'+_(
|
||||||
'''Select the devices to be <b>ignored</b>. calibre will not
|
'''Select the devices to be <b>ignored</b>. calibre <b>will not</b>
|
||||||
connect to devices with a checkmark next to their names.'''))
|
connect to devices with a checkmark next to their names.'''))
|
||||||
la.setWordWrap(True)
|
la.setWordWrap(True)
|
||||||
l.addWidget(la)
|
l.addWidget(la)
|
||||||
@ -386,7 +386,7 @@ class MTPConfig(QTabWidget):
|
|||||||
self.device.prefs['blacklist'])
|
self.device.prefs['blacklist'])
|
||||||
self.addTab(self.igntab, _('Ignored devices'))
|
self.addTab(self.igntab, _('Ignored devices'))
|
||||||
|
|
||||||
self.setCurrentIndex(0)
|
self.setCurrentIndex(1 if msg else 0)
|
||||||
|
|
||||||
def ignore_device(self):
|
def ignore_device(self):
|
||||||
self.igntab.ignore_device(self.device.current_serial_num)
|
self.igntab.ignore_device(self.device.current_serial_num)
|
||||||
@ -400,7 +400,7 @@ class MTPConfig(QTabWidget):
|
|||||||
p = self.device.prefs.get(self.current_device_key, {})
|
p = self.device.prefs.get(self.current_device_key, {})
|
||||||
if not p:
|
if not p:
|
||||||
self.device.prefs[self.current_device_key] = p
|
self.device.prefs[self.current_device_key] = p
|
||||||
return p.get(key, self.device.prefs[key])
|
return self.device.get_pref(key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device(self):
|
def device(self):
|
||||||
|
@ -11,8 +11,9 @@ from PyQt4.Qt import QDialog
|
|||||||
|
|
||||||
from calibre.gui2.dialogs.choose_library_ui import Ui_Dialog
|
from calibre.gui2.dialogs.choose_library_ui import Ui_Dialog
|
||||||
from calibre.gui2 import error_dialog, choose_dir
|
from calibre.gui2 import error_dialog, choose_dir
|
||||||
from calibre.constants import filesystem_encoding, iswindows
|
from calibre.constants import (filesystem_encoding, iswindows,
|
||||||
from calibre import isbytestring, patheq
|
get_portable_base)
|
||||||
|
from calibre import isbytestring, patheq, force_unicode
|
||||||
from calibre.gui2.wizard import move_library
|
from calibre.gui2.wizard import move_library
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
from calibre.library.database2 import LibraryDatabase2
|
||||||
|
|
||||||
@ -39,18 +40,45 @@ class ChooseLibrary(QDialog, Ui_Dialog):
|
|||||||
self.copy_structure.setEnabled(to_what)
|
self.copy_structure.setEnabled(to_what)
|
||||||
|
|
||||||
def choose_loc(self, *args):
|
def choose_loc(self, *args):
|
||||||
|
base = get_portable_base()
|
||||||
|
if base is None:
|
||||||
loc = choose_dir(self, 'choose library location',
|
loc = choose_dir(self, 'choose library location',
|
||||||
_('Choose location for calibre library'))
|
_('Choose location for calibre library'))
|
||||||
|
else:
|
||||||
|
name = force_unicode('choose library loc at' + base,
|
||||||
|
filesystem_encoding)
|
||||||
|
loc = choose_dir(self, name,
|
||||||
|
_('Choose location for calibre library'), default_dir=base,
|
||||||
|
no_save_dir=True)
|
||||||
if loc is not None:
|
if loc is not None:
|
||||||
self.location.setText(loc)
|
self.location.setText(loc)
|
||||||
|
|
||||||
def check_action(self, ac, loc):
|
def check_action(self, ac, loc):
|
||||||
exists = self.db.exists_at(loc)
|
exists = self.db.exists_at(loc)
|
||||||
|
base = get_portable_base()
|
||||||
if patheq(loc, self.db.library_path):
|
if patheq(loc, self.db.library_path):
|
||||||
error_dialog(self, _('Same as current'),
|
error_dialog(self, _('Same as current'),
|
||||||
_('The location %s contains the current calibre'
|
_('The location %s contains the current calibre'
|
||||||
' library')%loc, show=True)
|
' library')%loc, show=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if base is not None and ac in ('new', 'move'):
|
||||||
|
abase = os.path.normcase(os.path.abspath(base))
|
||||||
|
cal = os.path.normcase(os.path.abspath(os.path.join(abase,
|
||||||
|
'Calibre')))
|
||||||
|
aloc = os.path.normcase(os.path.abspath(loc))
|
||||||
|
if (aloc.startswith(cal+os.sep) or aloc == cal):
|
||||||
|
error_dialog(self, _('Bad location'),
|
||||||
|
_('You should not create a library inside the Calibre'
|
||||||
|
' folder as this folder is automatically deleted during upgrades.'),
|
||||||
|
show=True)
|
||||||
|
return False
|
||||||
|
if aloc.startswith(abase) and os.path.dirname(aloc) != abase:
|
||||||
|
error_dialog(self, _('Bad location'),
|
||||||
|
_('You can only create libraries inside %s at the top '
|
||||||
|
'level, not in sub-folders')%base, show=True)
|
||||||
|
return False
|
||||||
|
|
||||||
empty = not os.listdir(loc)
|
empty = not os.listdir(loc)
|
||||||
if ac == 'existing' and not exists:
|
if ac == 'existing' and not exists:
|
||||||
error_dialog(self, _('No existing library found'),
|
error_dialog(self, _('No existing library found'),
|
||||||
|
@ -9,7 +9,7 @@ from PyQt4.Qt import (QCoreApplication, QIcon, QObject, QTimer,
|
|||||||
|
|
||||||
from calibre import prints, plugins, force_unicode
|
from calibre import prints, plugins, force_unicode
|
||||||
from calibre.constants import (iswindows, __appname__, isosx, DEBUG, islinux,
|
from calibre.constants import (iswindows, __appname__, isosx, DEBUG, islinux,
|
||||||
filesystem_encoding)
|
filesystem_encoding, get_portable_base)
|
||||||
from calibre.utils.ipc import gui_socket_address, RC
|
from calibre.utils.ipc import gui_socket_address, RC
|
||||||
from calibre.gui2 import (ORG_NAME, APP_UID, initialize_file_icon_provider,
|
from calibre.gui2 import (ORG_NAME, APP_UID, initialize_file_icon_provider,
|
||||||
Application, choose_dir, error_dialog, question_dialog, gprefs)
|
Application, choose_dir, error_dialog, question_dialog, gprefs)
|
||||||
@ -21,6 +21,9 @@ from calibre.library.sqlite import sqlite, DatabaseException
|
|||||||
if iswindows:
|
if iswindows:
|
||||||
winutil = plugins['winutil'][0]
|
winutil = plugins['winutil'][0]
|
||||||
|
|
||||||
|
class AbortInit(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def option_parser():
|
def option_parser():
|
||||||
parser = _option_parser('''\
|
parser = _option_parser('''\
|
||||||
%prog [opts] [path_to_ebook]
|
%prog [opts] [path_to_ebook]
|
||||||
@ -46,10 +49,43 @@ path_to_ebook to the database.
|
|||||||
'will be silently aborted, so use with care.'))
|
'will be silently aborted, so use with care.'))
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
def find_portable_library():
|
||||||
|
base = get_portable_base()
|
||||||
|
if base is None: return
|
||||||
|
import glob
|
||||||
|
candidates = [os.path.basename(os.path.dirname(x)) for x in glob.glob(
|
||||||
|
os.path.join(base, u'*%smetadata.db'%os.sep))]
|
||||||
|
if not candidates:
|
||||||
|
candidates = [u'Calibre Library']
|
||||||
|
lp = prefs['library_path']
|
||||||
|
if not lp:
|
||||||
|
lib = os.path.join(base, candidates[0])
|
||||||
|
else:
|
||||||
|
lib = None
|
||||||
|
q = os.path.basename(lp)
|
||||||
|
for c in candidates:
|
||||||
|
c = c
|
||||||
|
if c.lower() == q.lower():
|
||||||
|
lib = os.path.join(base, c)
|
||||||
|
break
|
||||||
|
if lib is None:
|
||||||
|
lib = os.path.join(base, candidates[0])
|
||||||
|
|
||||||
|
if len(lib) > 74:
|
||||||
|
error_dialog(None, _('Path too long'),
|
||||||
|
_("Path to Calibre Portable (%s) "
|
||||||
|
'too long. Must be less than 59 characters.')%base, show=True)
|
||||||
|
raise AbortInit()
|
||||||
|
|
||||||
|
prefs.set('library_path', lib)
|
||||||
|
if not os.path.exists(lib):
|
||||||
|
os.mkdir(lib)
|
||||||
|
|
||||||
def init_qt(args):
|
def init_qt(args):
|
||||||
from calibre.gui2.ui import Main
|
from calibre.gui2.ui import Main
|
||||||
parser = option_parser()
|
parser = option_parser()
|
||||||
opts, args = parser.parse_args(args)
|
opts, args = parser.parse_args(args)
|
||||||
|
find_portable_library()
|
||||||
if opts.with_library is not None:
|
if opts.with_library is not None:
|
||||||
if not os.path.exists(opts.with_library):
|
if not os.path.exists(opts.with_library):
|
||||||
os.makedirs(opts.with_library)
|
os.makedirs(opts.with_library)
|
||||||
@ -360,7 +396,10 @@ def main(args=sys.argv):
|
|||||||
gui_debug = args[1]
|
gui_debug = args[1]
|
||||||
args = ['calibre']
|
args = ['calibre']
|
||||||
|
|
||||||
|
try:
|
||||||
app, opts, args, actions = init_qt(args)
|
app, opts, args, actions = init_qt(args)
|
||||||
|
except AbortInit:
|
||||||
|
return 1
|
||||||
from calibre.utils.lock import singleinstance
|
from calibre.utils.lock import singleinstance
|
||||||
from multiprocessing.connection import Listener
|
from multiprocessing.connection import Listener
|
||||||
si = singleinstance('calibre GUI')
|
si = singleinstance('calibre GUI')
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <QPluginLoader>
|
#include <QPluginLoader>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
QProgressIndicator::QProgressIndicator(QWidget* parent, int size)
|
QProgressIndicator::QProgressIndicator(QWidget* parent, int size)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
@ -145,3 +146,16 @@ int load_style(QString &path, QString &name) {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool do_notify(QObject *receiver, QEvent *event) {
|
||||||
|
try {
|
||||||
|
return QApplication::instance()->notify(receiver, event);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
qCritical() << "C++ exception thrown in slot: " << e.what();
|
||||||
|
} catch (...) {
|
||||||
|
qCritical() << "Unknown C++ exception thrown in slot";
|
||||||
|
}
|
||||||
|
qCritical() << "Receiver name:" << receiver->objectName() << "Receiver class:" << receiver->metaObject()->className() << "Event type: " << event->type();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -100,3 +100,5 @@ private:
|
|||||||
*/
|
*/
|
||||||
int load_style(QString &path, QString &name);
|
int load_style(QString &path, QString &name);
|
||||||
|
|
||||||
|
bool do_notify(QObject *receiver, QEvent *event);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
%ModuleHeaderCode
|
%ModuleHeaderCode
|
||||||
int load_style(QString &path, QString &name);
|
int load_style(QString &path, QString &name);
|
||||||
|
bool do_notify(QObject *receiver, QEvent *event);
|
||||||
%End
|
%End
|
||||||
|
|
||||||
class QProgressIndicator : QWidget {
|
class QProgressIndicator : QWidget {
|
||||||
@ -57,3 +58,5 @@ protected:
|
|||||||
|
|
||||||
int load_style(QString &path, QString &name);
|
int load_style(QString &path, QString &name);
|
||||||
|
|
||||||
|
bool do_notify(QObject *receiver, QEvent *event);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user