mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Merge upstream changes
This commit is contained in:
commit
e73639e5d3
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.4.127'
|
||||
__version__ = '0.4.128'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
'''
|
||||
Various run time constants.
|
||||
|
@ -28,6 +28,7 @@ class CYBOOKG3(USBMS):
|
||||
STORAGE_CARD_VOLUME_LABEL = 'Cybook Gen 3 Storage Card'
|
||||
|
||||
EBOOK_DIR_MAIN = "eBooks"
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
def delete_books(self, paths, end_session=True):
|
||||
for path in paths:
|
||||
@ -46,3 +47,8 @@ class CYBOOKG3(USBMS):
|
||||
for filen in fnmatch.filter(files, filename + "*.t2b"):
|
||||
os.unlink(os.path.join(p, filen))
|
||||
|
||||
try:
|
||||
os.removedirs(os.path.dirname(path))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -60,8 +60,9 @@ class DeviceScanner(object):
|
||||
def is_device_connected(self, device):
|
||||
if iswindows:
|
||||
vid, pid = 'vid_%4.4x'%device.VENDOR_ID, 'pid_%4.4x'%device.PRODUCT_ID
|
||||
vidd, pidd = 'vid_%i'%device.VENDOR_ID, 'pid_%i'%device.PRODUCT_ID
|
||||
for device_id in self.devices:
|
||||
if vid in device_id and pid in device_id:
|
||||
if (vid in device_id or vidd in device_id) and (pid in device_id or pidd in device_id):
|
||||
if self.test_bcd_windows(device_id, getattr(device, 'BCD', None)):
|
||||
if device.can_handle(device_id):
|
||||
return True
|
||||
|
@ -15,9 +15,10 @@ from calibre.devices.errors import FreeSpaceError
|
||||
from calibre.devices.mime import MIME_MAP
|
||||
|
||||
class USBMS(Device):
|
||||
FORMATS = []
|
||||
EBOOK_DIR_MAIN = ''
|
||||
EBOOK_DIR_CARD = ''
|
||||
FORMATS = []
|
||||
SUPPORTS_SUB_DIRS = False
|
||||
|
||||
def __init__(self, key='-1', log_packets=False, report_progress=None):
|
||||
pass
|
||||
@ -57,10 +58,18 @@ class USBMS(Device):
|
||||
path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN)
|
||||
else:
|
||||
path = os.path.join(self._card_prefix, self.EBOOK_DIR_CARD)
|
||||
|
||||
sizes = map(os.path.getsize, files)
|
||||
|
||||
def get_size(obj):
|
||||
if hasattr(obj, 'seek'):
|
||||
obj.seek(0, os.SEEK_END)
|
||||
size = obj.tell()
|
||||
obj.seek(0)
|
||||
return size
|
||||
return os.path.getsize(obj)
|
||||
|
||||
sizes = map(get_size, files)
|
||||
size = sum(sizes)
|
||||
|
||||
|
||||
if on_card and size > self.free_space()[2] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
if not on_card and size > self.free_space()[0] - 2*1024*1024:
|
||||
@ -68,17 +77,42 @@ class USBMS(Device):
|
||||
|
||||
paths = []
|
||||
names = iter(names)
|
||||
metadata = iter(metadata)
|
||||
|
||||
for infile in files:
|
||||
filepath = os.path.join(path, names.next())
|
||||
newpath = path
|
||||
|
||||
if self.SUPPORTS_SUB_DIRS:
|
||||
mdata = metadata.next()
|
||||
|
||||
if 'tags' in mdata.keys():
|
||||
for tag in mdata['tags']:
|
||||
if tag.startswith('/'):
|
||||
newpath += tag
|
||||
newpath = os.path.normpath(newpath)
|
||||
break
|
||||
|
||||
if not os.path.exists(newpath):
|
||||
os.makedirs(newpath)
|
||||
|
||||
filepath = os.path.join(newpath, names.next())
|
||||
paths.append(filepath)
|
||||
|
||||
shutil.copy2(infile, filepath)
|
||||
if hasattr(infile, 'read'):
|
||||
infile.seek(0)
|
||||
|
||||
dest = open(filepath, 'wb')
|
||||
shutil.copyfileobj(infile, dest, 10*1024*1024)
|
||||
|
||||
dest.flush()
|
||||
dest.close()
|
||||
else:
|
||||
shutil.copy2(infile, filepath)
|
||||
|
||||
return zip(paths, cycle([on_card]))
|
||||
|
||||
@classmethod
|
||||
def add_books_to_metadata(cls, locations, metadata, booklists):
|
||||
def add_books_to_metadata(cls, locations, metadata, booklists):
|
||||
for location in locations:
|
||||
path = location[0]
|
||||
on_card = 1 if location[1] else 0
|
||||
@ -91,6 +125,10 @@ class USBMS(Device):
|
||||
if os.path.exists(path):
|
||||
# Delete the ebook
|
||||
os.unlink(path)
|
||||
try:
|
||||
os.removedirs(os.path.dirname(path))
|
||||
except:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def remove_books_from_metadata(cls, paths, booklists):
|
||||
@ -99,7 +137,6 @@ class USBMS(Device):
|
||||
for book in bl:
|
||||
if path.endswith(book.path):
|
||||
bl.remove(book)
|
||||
break
|
||||
|
||||
def sync_booklists(self, booklists, end_session=True):
|
||||
# There is no meta data on the device to update. The device is treated
|
||||
|
@ -77,6 +77,8 @@ def check_links(opf_path, pretty_print):
|
||||
html_files.append(os.path.abspath(content(f)))
|
||||
|
||||
for path in html_files:
|
||||
if not os.access(path, os.R_OK):
|
||||
continue
|
||||
base = os.path.dirname(path)
|
||||
root = html.fromstring(open(content(path), 'rb').read(), parser=parser)
|
||||
for element, attribute, link, pos in list(root.iterlinks()):
|
||||
|
@ -249,7 +249,7 @@ class MetaInformation(object):
|
||||
ans = u''
|
||||
ans += u'Title : ' + unicode(self.title) + u'\n'
|
||||
if self.authors:
|
||||
ans += u'Author : ' + (', '.join(self.authors) if self.authors is not None else u'None')
|
||||
ans += u'Author : ' + (' & '.join(self.authors) if self.authors is not None else _('Unknown'))
|
||||
ans += ((' [' + self.author_sort + ']') if self.author_sort else '') + u'\n'
|
||||
if self.publisher:
|
||||
ans += u'Publisher: '+ unicode(self.publisher) + u'\n'
|
||||
|
@ -33,7 +33,7 @@ class EXTHHeader(object):
|
||||
self.length, self.num_items = struct.unpack('>LL', raw[4:12])
|
||||
raw = raw[12:]
|
||||
pos = 0
|
||||
self.mi = MetaInformation('Unknown', ['Unknown'])
|
||||
self.mi = MetaInformation(_('Unknown'), [_('Unknown')])
|
||||
self.has_fake_cover = True
|
||||
|
||||
for i in range(self.num_items):
|
||||
@ -63,7 +63,9 @@ class EXTHHeader(object):
|
||||
|
||||
def process_metadata(self, id, content, codec):
|
||||
if id == 100:
|
||||
self.mi.authors = [content.decode(codec, 'ignore').strip()]
|
||||
if self.mi.authors == [_('Unknown')]:
|
||||
self.mi.authors = []
|
||||
self.mi.authors.append(content.decode(codec, 'ignore').strip())
|
||||
elif id == 101:
|
||||
self.mi.publisher = content.decode(codec, 'ignore').strip()
|
||||
elif id == 103:
|
||||
|
@ -28,9 +28,6 @@
|
||||
<property name="readOnly" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="maximumBlockCount" >
|
||||
<number>400</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
BIN
src/calibre/gui2/images/news/tomshardware_de.png
Normal file
BIN
src/calibre/gui2/images/news/tomshardware_de.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 992 B |
@ -7,8 +7,8 @@ var column_titles = {
|
||||
'rating' : 'Rating',
|
||||
'date' : 'Date',
|
||||
'tags' : 'Tags',
|
||||
'series' : 'Series',
|
||||
}
|
||||
'series' : 'Series'
|
||||
};
|
||||
|
||||
String.prototype.format = function() {
|
||||
var pattern = /\{\d+\}/g;
|
||||
@ -47,7 +47,7 @@ function render_book(book) {
|
||||
// Render title cell
|
||||
var title = '<i>{0}</i>'.format(book.attr("title")) + '<br /><span class="subtitle">';
|
||||
var id = book.attr("id");
|
||||
var comments = $.trim(book.text()).replace(/\n\n/, '<br/>');
|
||||
var comments = $.trim(book.text()).replace(/\n\n/, '<br/>');
|
||||
var formats = new Array();
|
||||
var size = (parseFloat(book.attr('size'))/(1024*1024)).toFixed(1);
|
||||
var tags = book.attr('tags').replace(/,/g, ', ');
|
||||
@ -70,22 +70,22 @@ function render_book(book) {
|
||||
authors += jQuery.trim(_authors[i]).replace(/ /g, ' ')+'<br />';
|
||||
}
|
||||
if (authors) { authors = authors.slice(0, authors.length-6); }
|
||||
|
||||
|
||||
// Render rating cell
|
||||
var _rating = parseFloat(book.attr('rating'))/2.;
|
||||
var rating = '';
|
||||
for (i = 0; i < _rating; i++) { rating += '★'}
|
||||
|
||||
|
||||
// Render date cell
|
||||
var _date = Date.parseExact(book.attr('timestamp'), 'yyyy/MM/dd HH:mm:ss');
|
||||
var date = _date.toString('d MMM yyyy').replace(/ /g, ' ');
|
||||
|
||||
|
||||
// Render series cell
|
||||
var series = book.attr("series")
|
||||
if (series) {
|
||||
series += ' [{0}]'.format(book.attr('series_index'));
|
||||
}
|
||||
|
||||
|
||||
var cells = {
|
||||
'title' : title,
|
||||
'authors' : authors,
|
||||
@ -93,12 +93,12 @@ function render_book(book) {
|
||||
'date' : date,
|
||||
'series' : series
|
||||
};
|
||||
|
||||
|
||||
var row = '';
|
||||
for (i = 0; i < cmap.length; i++) {
|
||||
row += '<td class="{0}">{1}</td>'.format(cmap[i], cells[cmap[i]]);
|
||||
}
|
||||
return '<tr id="{0}">{1}</tr>'.format(id, row);
|
||||
return '<tr id="{0}">{1}</tr>'.format(id, row);
|
||||
}
|
||||
|
||||
function fetch_library_books(start, num, timeout, sort, order, search) {
|
||||
@ -112,15 +112,15 @@ function fetch_library_books(start, num, timeout, sort, order, search) {
|
||||
last_search = search;
|
||||
last_sort = sort;
|
||||
last_sort_order = order;
|
||||
|
||||
|
||||
if (current_library_request != null) {
|
||||
current_library_request.abort();
|
||||
current_library_request = null;
|
||||
}
|
||||
|
||||
|
||||
$('#cover_pane').css('visibility', 'hidden');
|
||||
$('#loading').css('visibility', 'visible');
|
||||
|
||||
|
||||
current_library_request = $.ajax({
|
||||
type: "GET",
|
||||
url: "library",
|
||||
@ -128,18 +128,18 @@ function fetch_library_books(start, num, timeout, sort, order, search) {
|
||||
cache: false,
|
||||
timeout: timeout, //milliseconds
|
||||
dataType: "xml",
|
||||
|
||||
|
||||
error : function(XMLHttpRequest, textStatus, errorThrown) {
|
||||
alert('Error: '+textStatus+'\n\n'+errorThrown);
|
||||
alert('Error: '+textStatus+'\n\n'+errorThrown);
|
||||
},
|
||||
|
||||
|
||||
success : function(xml, textStatus) {
|
||||
var library = $(xml).find('library');
|
||||
total = parseInt(library.attr('total'));
|
||||
var num = parseInt(library.attr('num'));
|
||||
var start = parseInt(library.attr('start'));
|
||||
update_count_bar(start, num, total);
|
||||
var display = '';
|
||||
var display = '';
|
||||
library.find('book').each( function() {
|
||||
var book = $(this);
|
||||
var row = render_book(book);
|
||||
@ -170,18 +170,18 @@ function fetch_library_books(start, num, timeout, sort, order, search) {
|
||||
$('#cover_pane').css('visibility', 'visible');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
layout();
|
||||
$('#book_list tbody tr:even()').css('background-color', '#eeeeee');
|
||||
},
|
||||
|
||||
|
||||
complete : function(XMLHttpRequest, textStatus) {
|
||||
current_library_request = null;
|
||||
document.getElementById('main').scrollTop = 0;
|
||||
$('#loading').css('visibility', 'hidden');
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@ -196,7 +196,7 @@ function update_count_bar(start, num, total) {
|
||||
left.css('opacity', (start <= 0) ? 0.3 : 1);
|
||||
var right = cb.find('#right');
|
||||
right.css('opacity', (start + num >= total) ? 0.3 : 1);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function setup_count_bar() {
|
||||
@ -205,7 +205,7 @@ function setup_count_bar() {
|
||||
fetch_library_books(0, last_num, LIBRARY_FETCH_TIMEOUT, last_sort, last_sort_order, last_search);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('#count_bar * img:eq(1)').click(function(){
|
||||
if (last_start > 0) {
|
||||
var new_start = last_start - last_num;
|
||||
@ -215,14 +215,14 @@ function setup_count_bar() {
|
||||
fetch_library_books(new_start, last_num, LIBRARY_FETCH_TIMEOUT, last_sort, last_sort_order, last_search);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('#count_bar * img:eq(2)').click(function(){
|
||||
if (last_start + last_num < total) {
|
||||
var new_start = last_start + last_num;
|
||||
fetch_library_books(new_start, last_num, LIBRARY_FETCH_TIMEOUT, last_sort, last_sort_order, last_search);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('#count_bar * img:eq(3)').click(function(){
|
||||
if (total - last_num > 0) {
|
||||
fetch_library_books(total - last_num, last_num, LIBRARY_FETCH_TIMEOUT, last_sort, last_sort_order, last_search);
|
||||
@ -234,7 +234,7 @@ function setup_count_bar() {
|
||||
|
||||
function search() {
|
||||
var search = $.trim($('#search_box * #s').val());
|
||||
fetch_library_books(0, last_num, LIBRARY_FETCH_TIMEOUT,
|
||||
fetch_library_books(0, last_num, LIBRARY_FETCH_TIMEOUT,
|
||||
last_sort, last_sort_order, search);
|
||||
}
|
||||
|
||||
@ -245,11 +245,11 @@ function setup_sorting() {
|
||||
$('table#book_list thead tr td').mouseover(function() {
|
||||
this.style.backgroundColor = "#fff2a8";
|
||||
});
|
||||
|
||||
|
||||
$('table#book_list thead tr td').mouseout(function() {
|
||||
this.style.backgroundColor = "inherit";
|
||||
});
|
||||
|
||||
|
||||
for (i = 0; i < cmap.length; i++) {
|
||||
$('table#book_list span#{0}_sort'.format(cmap[i])).parent().click(function() {
|
||||
var sort_indicator = $($(this).find('span'));
|
||||
@ -258,7 +258,7 @@ function setup_sorting() {
|
||||
var col = id.slice(0, id.indexOf("_"));
|
||||
var order = 'ascending';
|
||||
var html = '↑';
|
||||
|
||||
|
||||
if (sort_indicator.html() == '↑') {
|
||||
order = 'descending'; html = '↓';
|
||||
}
|
||||
@ -291,13 +291,13 @@ function layout() {
|
||||
$(function() {
|
||||
// document is ready
|
||||
create_table_headers();
|
||||
|
||||
|
||||
// Setup widgets
|
||||
setup_sorting();
|
||||
setup_count_bar();
|
||||
$('#search_box * #s').val('');
|
||||
$(window).resize(layout);
|
||||
|
||||
|
||||
$($('#book_list * span#date_sort').parent()).click();
|
||||
|
||||
});
|
||||
|
@ -102,7 +102,7 @@ Device Integration
|
||||
|
||||
What devices does |app| support?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
At the moment |app| has full support for the SONY PRS 500/505/700 as well as the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
||||
At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3 as well as the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
||||
|
||||
I used |app| to transfer some books to my reader, and now the SONY software hangs every time I connect the reader?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -286,7 +286,7 @@ def write(socket, msg, timeout=5):
|
||||
def read(socket, timeout=5):
|
||||
'''
|
||||
Read a message from `socket`. The message must have been sent with the :function:`write`
|
||||
function. Raises a `RuntimeError` if the message is corrpted. Can return an
|
||||
function. Raises a `RuntimeError` if the message is corrupted. Can return an
|
||||
empty string.
|
||||
'''
|
||||
if isworker:
|
||||
@ -299,7 +299,12 @@ def read(socket, timeout=5):
|
||||
if not msg:
|
||||
break
|
||||
if length is None:
|
||||
length, msg = int(msg[:12]), msg[12:]
|
||||
try:
|
||||
length, msg = int(msg[:12]), msg[12:]
|
||||
except ValueError:
|
||||
if DEBUG:
|
||||
print >>sys.__stdout__, 'read(%s):'%('worker' if isworker else 'overseer'), 'no length in', msg
|
||||
return ''
|
||||
buf.write(msg)
|
||||
if buf.tell() >= length:
|
||||
break
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,8 @@ recipe_modules = ['recipe_' + r for r in (
|
||||
'linux_magazine', 'telegraph_uk', 'utne', 'sciencedaily', 'forbes',
|
||||
'time_magazine', 'endgadget', 'fudzilla', 'nspm_int', 'nspm', 'pescanik',
|
||||
'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet',
|
||||
'joelonsoftware', 'telepolis', 'common_dreams', 'nin',
|
||||
'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de',
|
||||
|
||||
)]
|
||||
|
||||
import re, imp, inspect, time, os
|
||||
|
@ -6,7 +6,6 @@ __copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
tomshardware.com
|
||||
'''
|
||||
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class Tomshardware(BasicNewsRecipe):
|
||||
@ -50,7 +49,7 @@ class Tomshardware(BasicNewsRecipe):
|
||||
rmain, rsep, article_id = main.rpartition(',')
|
||||
tmain, tsep, trest = rmain.rpartition('/reviews/')
|
||||
if tsep:
|
||||
return 'http://www.tomshardware.com/review_print.php?p1=' + article_id
|
||||
return 'http://www.tomshardware.com/review_print.php?p1=' + article_id
|
||||
return 'http://www.tomshardware.com/news_print.php?p1=' + article_id
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
|
54
src/calibre/web/feeds/recipes/recipe_tomshardware_de.py
Normal file
54
src/calibre/web/feeds/recipes/recipe_tomshardware_de.py
Normal file
@ -0,0 +1,54 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
'''
|
||||
Fetch tomshardware.
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
|
||||
class TomsHardwareDe(BasicNewsRecipe):
|
||||
|
||||
title = 'Tom\'s Hardware German'
|
||||
description = 'Computer news in german'
|
||||
__author__ = 'Oliver Niesner'
|
||||
use_embedded_content = False
|
||||
timefmt = ' [%d %b %Y]'
|
||||
max_articles_per_feed = 50
|
||||
no_stylesheets = True
|
||||
encoding = 'utf-8'
|
||||
|
||||
#preprocess_regexps = \
|
||||
# [(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
||||
# [
|
||||
# (r'<84>', lambda match: ''),
|
||||
# (r'<93>', lambda match: ''),
|
||||
# ]
|
||||
# ]
|
||||
|
||||
remove_tags = [dict(id='outside-advert'),
|
||||
dict(id='advertRightWhite'),
|
||||
dict(id='header-advert'),
|
||||
dict(id='header-banner'),
|
||||
dict(id='header-menu'),
|
||||
dict(id='header-top'),
|
||||
dict(id='header-tools'),
|
||||
dict(id='nbComment'),
|
||||
dict(id='internalSidebar'),
|
||||
dict(id='header-news-infos'),
|
||||
dict(id='breadcrumbs'),
|
||||
dict(id=''),
|
||||
dict(name='div', attrs={'class':'pyjama'}),
|
||||
dict(name='href', attrs={'class':'comment'}),
|
||||
dict(name='div', attrs={'class':'greyBoxR clearfix'}),
|
||||
dict(name='div', attrs={'class':'greyBoxL clearfix'}),
|
||||
dict(name='div', attrs={'class':'greyBox clearfix'}),
|
||||
dict(id='')]
|
||||
#remove_tags_before = [dict(id='header-news-title')]
|
||||
remove_tags_after = [dict(name='div', attrs={'class':'news-elm'})]
|
||||
#remove_tags_after = [dict(name='div', attrs={'class':'intelliTXT'})]
|
||||
|
||||
feeds = [ ('tomshardware', 'http://www.tomshardware.com/de/feeds/rss2/tom-s-hardware-de,12-1.xml') ]
|
||||
|
Loading…
x
Reference in New Issue
Block a user