From bc4b6fdf486b48a2bf771f57025bd3cd2795176f Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Tue, 10 Feb 2009 10:29:09 -0800
Subject: [PATCH] New recipe for Pobjeda by Darko Miletic
---
src/calibre/gui2/__init__.py | 39 ++-
src/calibre/gui2/add.py | 224 ++++++++++++++++++
src/calibre/gui2/dialogs/confirm_delete.py | 3 +-
src/calibre/gui2/dialogs/progress.py | 1 +
src/calibre/gui2/images/news/pobjeda.png | Bin 0 -> 285 bytes
src/calibre/gui2/main.py | 145 +++++-------
src/calibre/library/database.py | 114 +--------
src/calibre/library/database2.py | 112 ++++++++-
src/calibre/web/feeds/recipes/__init__.py | 1 +
.../web/feeds/recipes/recipe_pobjeda.py | 102 ++++++++
10 files changed, 532 insertions(+), 209 deletions(-)
create mode 100644 src/calibre/gui2/add.py
create mode 100644 src/calibre/gui2/images/news/pobjeda.png
create mode 100644 src/calibre/web/feeds/recipes/recipe_pobjeda.py
diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py
index 084b352f48..c33036e183 100644
--- a/src/calibre/gui2/__init__.py
+++ b/src/calibre/gui2/__init__.py
@@ -1,7 +1,7 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal '
""" The GUI """
-import sys, os, re, StringIO, traceback
+import sys, os, re, StringIO, traceback, time
from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSize, \
QByteArray, QLocale, QUrl, QTranslator, QCoreApplication, \
QModelIndex
@@ -14,6 +14,9 @@ from calibre import __author__, islinux, iswindows, isosx
from calibre.startup import get_lang
from calibre.utils.config import Config, ConfigProxy, dynamic
import calibre.resources as resources
+from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats
+from calibre.ebooks.metadata import MetaInformation
+
NONE = QVariant() #: Null value to return from the data function of item models
@@ -148,7 +151,41 @@ class Dispatcher(QObject):
def dispatch(self, args, kwargs):
self.func(*args, **kwargs)
+
+class GetMetadata(QObject):
+ '''
+ Convenience class to ensure that metadata readers are used only in the
+ GUI thread. Must be instantiated in the GUI thread.
+ '''
+
+ def __init__(self):
+ QObject.__init__(self)
+ self.connect(self, SIGNAL('edispatch(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'),
+ self._get_metadata, Qt.QueuedConnection)
+ self.connect(self, SIGNAL('idispatch(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'),
+ self._from_formats, Qt.QueuedConnection)
+ def __call__(self, id, *args, **kwargs):
+ self.emit(SIGNAL('edispatch(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'),
+ id, args, kwargs)
+
+ def from_formats(self, id, *args, **kwargs):
+ self.emit(SIGNAL('idispatch(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'),
+ id, args, kwargs)
+
+ def _from_formats(self, id, args, kwargs):
+ try:
+ mi = metadata_from_formats(*args, **kwargs)
+ except:
+ mi = MetaInformation('', [_('Unknown')])
+ self.emit(SIGNAL('metadataf(PyQt_PyObject, PyQt_PyObject)'), id, mi)
+
+ def _get_metadata(self, id, args, kwargs):
+ try:
+ mi = get_metadata(*args, **kwargs)
+ except:
+ mi = MetaInformation('', [_('Unknown')])
+ self.emit(SIGNAL('metadata(PyQt_PyObject, PyQt_PyObject)'), id, mi)
class TableView(QTableView):
def __init__(self, parent):
diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py
new file mode 100644
index 0000000000..a6c6c699e8
--- /dev/null
+++ b/src/calibre/gui2/add.py
@@ -0,0 +1,224 @@
+'''
+UI for adding books to the database
+'''
+import os
+
+from PyQt4.Qt import QThread, SIGNAL, QMutex, QWaitCondition, Qt
+
+from calibre.gui2.dialogs.progress import ProgressDialog
+from calibre.constants import preferred_encoding
+from calibre.gui2.widgets import WarningDialog
+
+class Add(QThread):
+
+ def __init__(self):
+ QThread.__init__(self)
+ self._lock = QMutex()
+ self._waiting = QWaitCondition()
+
+ def is_canceled(self):
+ if self.pd.canceled:
+ self.canceled = True
+ return self.canceled
+
+ def wait_for_condition(self):
+ self._lock.lock()
+ self._waiting.wait(self._lock)
+ self._lock.unlock()
+
+ def wake_up(self):
+ self._waiting.wakeAll()
+
+class AddFiles(Add):
+
+ def __init__(self, paths, default_thumbnail, get_metadata, db=None):
+ Add.__init__(self)
+ self.paths = paths
+ self.get_metadata = get_metadata
+ self.default_thumbnail = default_thumbnail
+ self.db = db
+ self.formats, self.metadata, self.names, self.infos = [], [], [], []
+ self.duplicates = []
+ self.number_of_books_added = 0
+ self.connect(self.get_metadata,
+ SIGNAL('metadata(PyQt_PyObject, PyQt_PyObject)'),
+ self.metadata_delivered)
+
+ def metadata_delivered(self, id, mi):
+ if self.is_canceled():
+ self.reading.wakeAll()
+ return
+ if not mi.title:
+ mi.title = os.path.splitext(self.names[id])[0]
+ mi.title = mi.title if isinstance(mi.title, unicode) else \
+ mi.title.decode(preferred_encoding, 'replace')
+ self.metadata.append(mi)
+ self.infos.append({'title':mi.title,
+ 'authors':', '.join(mi.authors),
+ 'cover':self.default_thumbnail, 'tags':[]})
+ if self.db is not None:
+ duplicates, num = self.db.add_books(self.paths[id:id+1],
+ self.formats[id:id+1], [mi],
+ add_duplicates=False)
+ self.number_of_books_added += num
+ if duplicates:
+ if not self.duplicates:
+ self.duplicates = [[], [], [], []]
+ for i in range(4):
+ self.duplicates[i] += duplicates[i]
+ self.emit(SIGNAL('processed(PyQt_PyObject,PyQt_PyObject)'),
+ mi.title, id)
+ self.wake_up()
+
+ def create_progress_dialog(self, title, msg, parent):
+ self._parent = parent
+ self.pd = ProgressDialog(title, msg, -1, len(self.paths)-1, parent)
+ self.connect(self, SIGNAL('processed(PyQt_PyObject,PyQt_PyObject)'),
+ self.update_progress_dialog)
+ self.pd.setModal(True)
+ self.pd.show()
+ self.connect(self, SIGNAL('finished()'), self.pd.hide)
+ return self.pd
+
+
+ def update_progress_dialog(self, title, count):
+ self.pd.set_value(count)
+ if self.db is not None:
+ self.pd.set_msg(_('Added %s to library')%title)
+ else:
+ self.pd.set_msg(_('Read metadata from ')+title)
+
+
+ def run(self):
+ self.canceled = False
+ for c, book in enumerate(self.paths):
+ if self.pd.canceled:
+ self.canceled = True
+ break
+ format = os.path.splitext(book)[1]
+ format = format[1:] if format else None
+ stream = open(book, 'rb')
+ self.formats.append(format)
+ self.names.append(os.path.basename(book))
+ self.get_metadata(c, stream, stream_type=format,
+ use_libprs_metadata=True)
+ self.wait_for_condition()
+
+
+ def process_duplicates(self):
+ if self.duplicates:
+ files = _('Books with the same title as the following already '
+ 'exist in the database. Add them anyway?
')
+ for mi in self.duplicates[2]:
+ files += '- '+mi.title+'
\n'
+ d = WarningDialog (_('Duplicates found!'),
+ _('Duplicates found!'),
+ files+'
', parent=self._parent)
+ if d.exec_() == d.Accepted:
+ num = self.db.add_books(*self.duplicates,
+ **dict(add_duplicates=True))[1]
+ self.number_of_books_added += num
+
+
+class AddRecursive(Add):
+
+ def __init__(self, path, db, get_metadata, single_book_per_directory, parent):
+ self.path = path
+ self.db = db
+ self.get_metadata = get_metadata
+ self.single_book_per_directory = single_book_per_directory
+ self.duplicates, self.books, self.metadata = [], [], []
+ self.number_of_books_added = 0
+ self.canceled = False
+ Add.__init__(self)
+ self.connect(self.get_metadata,
+ SIGNAL('metadataf(PyQt_PyObject, PyQt_PyObject)'),
+ self.metadata_delivered, Qt.QueuedConnection)
+ self.connect(self, SIGNAL('searching_done()'), self.searching_done,
+ Qt.QueuedConnection)
+ self._parent = parent
+ self.pd = ProgressDialog(_('Adding books recursively...'),
+ _('Searching for books in all sub-directories...'),
+ 0, 0, parent)
+ self.connect(self, SIGNAL('processed(PyQt_PyObject,PyQt_PyObject)'),
+ self.update_progress_dialog)
+ self.connect(self, SIGNAL('update(PyQt_PyObject)'), self.pd.set_msg,
+ Qt.QueuedConnection)
+ self.connect(self, SIGNAL('pupdate(PyQt_PyObject)'), self.pd.set_value,
+ Qt.QueuedConnection)
+ self.pd.setModal(True)
+ self.pd.show()
+ self.connect(self, SIGNAL('finished()'), self.pd.hide)
+
+ def update_progress_dialog(self, title, count):
+ self.pd.set_value(count)
+ if title:
+ self.pd.set_msg(_('Read metadata from ')+title)
+
+ def metadata_delivered(self, id, mi):
+ if self.is_canceled():
+ self.reading.wakeAll()
+ return
+ self.emit(SIGNAL('processed(PyQt_PyObject,PyQt_PyObject)'),
+ mi.title, id)
+ self.metadata.append((mi if mi.title else None, self.books[id]))
+ if len(self.metadata) >= len(self.books):
+ self.metadata = [x for x in self.metadata if x[0] is not None]
+ self.pd.set_min(-1)
+ self.pd.set_max(len(self.metadata)-1)
+ self.pd.set_value(-1)
+ self.pd.set_msg(_('Adding books to database...'))
+ self.wake_up()
+
+ def searching_done(self):
+ self.pd.set_min(-1)
+ self.pd.set_max(len(self.books)-1)
+ self.pd.set_value(-1)
+ self.pd.set_msg(_('Reading metadata...'))
+
+
+ def run(self):
+ root = os.path.abspath(self.path)
+ for dirpath in os.walk(root):
+ if self.is_canceled():
+ return
+ self.emit(SIGNAL('update(PyQt_PyObject)'),
+ _('Searching in')+' '+dirpath[0])
+ self.books += list(self.db.find_books_in_directory(dirpath[0],
+ self.single_book_per_directory))
+ self.books = [formats for formats in self.books if formats]
+ # Reset progress bar
+ self.emit(SIGNAL('searching_done()'))
+
+ for c, formats in enumerate(self.books):
+ self.get_metadata.from_formats(c, formats)
+ self.wait_for_condition()
+
+ # Add books to database
+ for c, x in enumerate(self.metadata):
+ mi, formats = x
+ if self.is_canceled():
+ break
+ if self.db.has_book(mi):
+ self.duplicates.append((mi, formats))
+ else:
+ self.db.import_book(mi, formats, notify=False)
+ self.number_of_books_added += 1
+ self.emit(SIGNAL('pupdate(PyQt_PyObject)'), c)
+
+
+ def process_duplicates(self):
+ if self.duplicates:
+ files = _('Books with the same title as the following already '
+ 'exist in the database. Add them anyway?
')
+ for mi in self.duplicates:
+ files += '- '+mi[0].title+'
\n'
+ d = WarningDialog (_('Duplicates found!'),
+ _('Duplicates found!'),
+ files+'
', parent=self._parent)
+ if d.exec_() == d.Accepted:
+ for mi, formats in self.duplicates:
+ self.db.import_book(mi, formats, notify=False)
+ self.number_of_books_added += 1
+
+
\ No newline at end of file
diff --git a/src/calibre/gui2/dialogs/confirm_delete.py b/src/calibre/gui2/dialogs/confirm_delete.py
index 08db53e9a7..8c496987fb 100644
--- a/src/calibre/gui2/dialogs/confirm_delete.py
+++ b/src/calibre/gui2/dialogs/confirm_delete.py
@@ -5,7 +5,7 @@ __docformat__ = 'restructuredtext en'
from calibre.gui2 import dynamic
from calibre.gui2.dialogs.confirm_delete_ui import Ui_Dialog
-from PyQt4.Qt import QDialog, SIGNAL
+from PyQt4.Qt import QDialog, SIGNAL, Qt
def _config_name(name):
return name + '_again'
@@ -19,6 +19,7 @@ class Dialog(QDialog, Ui_Dialog):
self.msg.setText(msg)
self.name = name
self.connect(self.again, SIGNAL('stateChanged(int)'), self.toggle)
+ self.buttonBox.setFocus(Qt.OtherFocusReason)
def toggle(self, x):
diff --git a/src/calibre/gui2/dialogs/progress.py b/src/calibre/gui2/dialogs/progress.py
index 2543cefb4d..0f64d7b041 100644
--- a/src/calibre/gui2/dialogs/progress.py
+++ b/src/calibre/gui2/dialogs/progress.py
@@ -20,6 +20,7 @@ class ProgressDialog(QDialog, Ui_Dialog):
self.setWindowModality(Qt.ApplicationModal)
self.set_min(min)
self.set_max(max)
+ self.bar.setValue(min)
self.canceled = False
self.connect(self.button_box, SIGNAL('rejected()'), self._canceled)
diff --git a/src/calibre/gui2/images/news/pobjeda.png b/src/calibre/gui2/images/news/pobjeda.png
new file mode 100644
index 0000000000000000000000000000000000000000..d7612b4e9ef1866298a80491456c439b1f36d225
GIT binary patch
literal 285
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b
zK-vS0-A-oPfdtD69Mgd`SU*F|v9*VR95+uF#}JFt$uf6Z@XI>3~C!+wYBq+`uo
zSv(-%Gv$V#D6_=G!wfg24b+7uNmuCiNICd2UzluUBtQSof$GMIm)`I&Y`redYv0m(
z1!$sbiEBhja#3nxNvduNkYF$}FtF4$Fw!+N2r;yBooks with the same title as the following already exist in the database. Add them anyway?')
- for mi, formats in duplicates:
- files += '- '+mi.title+'
\n'
- d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'),
- files+'
', self)
- if d.exec_() == QDialog.Accepted:
- for mi, formats in duplicates:
- self.library_view.model().db.import_book(mi, formats )
-
- self.library_view.model().resort()
- self.library_view.model().research()
-
+ from calibre.gui2.add import AddRecursive
+ self._add_recursive_thread = AddRecursive(root,
+ self.library_view.model().db, self.get_metadata,
+ single, self)
+ self.connect(self._add_recursive_thread, SIGNAL('finished()'),
+ self._recursive_files_added)
+ self._add_recursive_thread.start()
+
+ def _recursive_files_added(self):
+ self._add_recursive_thread.process_duplicates()
+ if self._add_recursive_thread.number_of_books_added > 0:
+ self.library_view.model().resort(reset=False)
+ self.library_view.model().research()
+ self.library_view.model().count_changed()
+ self._add_recursive_thread = None
+
def add_recursive_single(self, checked):
'''
Add books from the local filesystem to either the library or the device
@@ -686,66 +676,41 @@ class Main(MainWindow, Ui_MainWindow):
return
to_device = self.stack.currentIndex() != 0
self._add_books(books, to_device)
- if to_device:
- self.status_bar.showMessage(_('Uploading books to device.'), 2000)
+
def _add_books(self, paths, to_device, on_card=None):
if on_card is None:
on_card = self.stack.currentIndex() == 2
if not paths:
return
- # Get format and metadata information
- formats, metadata, names, infos = [], [], [], []
- progress = ProgressDialog(_('Adding books...'), _('Reading metadata...'),
- min=0, max=len(paths), parent=self)
- progress.show()
- try:
- for c, book in enumerate(paths):
- progress.set_value(c+1)
- if progress.canceled:
- return
- format = os.path.splitext(book)[1]
- format = format[1:] if format else None
- stream = open(book, 'rb')
- try:
- mi = get_metadata(stream, stream_type=format, use_libprs_metadata=True)
- except:
- mi = MetaInformation(None, None)
- if not mi.title:
- mi.title = os.path.splitext(os.path.basename(book))[0]
- if not mi.authors:
- mi.authors = [_('Unknown')]
- formats.append(format)
- metadata.append(mi)
- names.append(os.path.basename(book))
- infos.append({'title':mi.title, 'authors':', '.join(mi.authors),
- 'cover':self.default_thumbnail, 'tags':[]})
- title = mi.title if isinstance(mi.title, unicode) else mi.title.decode(preferred_encoding, 'replace')
- progress.set_msg(_('Read metadata from ')+title)
- QApplication.processEvents()
-
- if not to_device:
- progress.set_msg(_('Adding books to database...'))
- QApplication.processEvents()
- model = self.library_view.model()
-
- paths = list(paths)
- duplicates, number_added = model.add_books(paths, formats, metadata)
- if duplicates:
- files = _('Books with the same title as the following already exist in the database. Add them anyway?
')
- for mi in duplicates[2]:
- files += '- '+mi.title+'
\n'
- d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'), files+'
', parent=self)
- if d.exec_() == QDialog.Accepted:
- num = model.add_books(*duplicates, **dict(add_duplicates=True))[1]
- number_added += num
- model.books_added(number_added)
+ from calibre.gui2.add import AddFiles
+ self._add_files_thread = AddFiles(paths, self.default_thumbnail,
+ self.get_metadata,
+ None if to_device else \
+ self.library_view.model().db
+ )
+ self._add_files_thread.send_to_device = to_device
+ self._add_files_thread.on_card = on_card
+ self._add_files_thread.create_progress_dialog(_('Adding books...'),
+ _('Reading metadata...'), self)
+ self.connect(self._add_files_thread, SIGNAL('finished()'),
+ self._files_added)
+ self._add_files_thread.start()
+
+ def _files_added(self):
+ t = self._add_files_thread
+ self._add_files_thread = None
+ if not t.canceled:
+ if t.send_to_device:
+ self.upload_books(t.paths,
+ list(map(sanitize_file_name, t.names)),
+ t.infos, on_card=t.on_card)
+ self.status_bar.showMessage(_('Uploading books to device.'), 2000)
else:
- self.upload_books(paths, list(map(sanitize_file_name, names)), infos, on_card=on_card)
- finally:
- QApplication.processEvents()
- progress.hide()
-
+ t.process_duplicates()
+ if t.number_of_books_added > 0:
+ self.library_view.model().books_added(t.number_of_books_added)
+
def upload_books(self, files, names, metadata, on_card=False, memory=None):
'''
Upload books to device.
@@ -801,7 +766,10 @@ class Main(MainWindow, Ui_MainWindow):
if not rows or len(rows) == 0:
return
if self.stack.currentIndex() == 0:
- if not confirm(''+_('The selected books will be permanently deleted and the files removed from your computer. Are you sure?')+'
', 'library_delete_books', self):
+ if not confirm(''+_('The selected books will be '
+ 'permanently deleted and the files '
+ 'removed from your computer. Are you sure?')
+ +'
', 'library_delete_books', self):
return
view.model().delete_books(rows)
else:
@@ -1410,8 +1378,15 @@ class Main(MainWindow, Ui_MainWindow):
def initialize_database(self):
self.library_path = prefs['library_path']
if self.library_path is None: # Need to migrate to new database layout
+ base = os.path.expanduser('~')
+ if iswindows:
+ from calibre import plugins
+ from PyQt4.Qt import QDir
+ base = plugins['winutil'][0].special_folder_path(plugins['winutil'][0].CSIDL_PERSONAL)
+ if not base or not os.path.exists(base):
+ base = unicode(QDir.homePath()).replace('/', os.sep)
dir = unicode(QFileDialog.getExistingDirectory(self,
- _('Choose a location for your ebook library.'), os.getcwd()))
+ _('Choose a location for your ebook library.'), base))
if not dir:
dir = os.path.expanduser('~/Library')
self.library_path = os.path.abspath(dir)
diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py
index 37fdeb4ce4..e4fa71feed 100644
--- a/src/calibre/library/database.py
+++ b/src/calibre/library/database.py
@@ -4,13 +4,10 @@ __copyright__ = '2008, Kovid Goyal '
Backend that implements storage of ebooks in an sqlite database.
'''
import sqlite3 as sqlite
-import datetime, re, os, cPickle, sre_constants
+import datetime, re, cPickle, sre_constants
from zlib import compress, decompress
-from calibre import sanitize_file_name
-from calibre.ebooks.metadata.meta import set_metadata, metadata_from_formats
from calibre.ebooks.metadata import MetaInformation
-from calibre.ebooks import BOOK_EXTENSIONS
from calibre.web.feeds.recipes import migrate_automatic_profile_to_automatic_recipe
class Concatenate(object):
@@ -1391,117 +1388,12 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
- def import_book(self, mi, formats):
- series_index = 1 if mi.series_index is None else mi.series_index
- if not mi.authors:
- mi.authors = [_('Unknown')]
- aus = mi.author_sort if mi.author_sort else ', '.join(mi.authors)
- obj = self.conn.execute('INSERT INTO books(title, uri, series_index, author_sort) VALUES (?, ?, ?, ?)',
- (mi.title, None, series_index, aus))
- id = obj.lastrowid
- self.conn.commit()
- self.set_metadata(id, mi)
- for path in formats:
- ext = os.path.splitext(path)[1][1:].lower()
- stream = open(path, 'rb')
- stream.seek(0, 2)
- usize = stream.tell()
- stream.seek(0)
- data = sqlite.Binary(compress(stream.read()))
- try:
- self.conn.execute('INSERT INTO data(book, format, uncompressed_size, data) VALUES (?,?,?,?)',
- (id, ext, usize, data))
- except sqlite.IntegrityError:
- self.conn.execute('UPDATE data SET uncompressed_size=?, data=? WHERE book=? AND format=?',
- (usize, data, id, ext))
- self.conn.commit()
-
- def import_book_directory_multiple(self, dirpath, callback=None):
- dirpath = os.path.abspath(dirpath)
- duplicates = []
- books = {}
- for path in os.listdir(dirpath):
- if callable(callback):
- callback('.')
- path = os.path.abspath(os.path.join(dirpath, path))
- if os.path.isdir(path) or not os.access(path, os.R_OK):
- continue
- ext = os.path.splitext(path)[1]
- if not ext:
- continue
- ext = ext[1:].lower()
- if ext not in BOOK_EXTENSIONS:
- continue
-
- key = os.path.splitext(path)[0]
- if not books.has_key(key):
- books[key] = []
-
- books[key].append(path)
-
- for formats in books.values():
- mi = metadata_from_formats(formats)
- if mi.title is None:
- continue
- if self.has_book(mi):
- duplicates.append((mi, formats))
- continue
- self.import_book(mi, formats)
- if callable(callback):
- if callback(mi.title):
- break
- return duplicates
-
-
- def import_book_directory(self, dirpath, callback=None):
- dirpath = os.path.abspath(dirpath)
- formats = []
- for path in os.listdir(dirpath):
- if callable(callback):
- callback('.')
- path = os.path.abspath(os.path.join(dirpath, path))
- if os.path.isdir(path) or not os.access(path, os.R_OK):
- continue
- ext = os.path.splitext(path)[1]
- if not ext:
- continue
- ext = ext[1:].lower()
- if ext not in BOOK_EXTENSIONS:
- continue
- formats.append(path)
-
- if not formats:
- return
-
- mi = metadata_from_formats(formats)
- if mi.title is None:
- return
- if self.has_book(mi):
- return [(mi, formats)]
- self.import_book(mi, formats)
- if callable(callback):
- callback(mi.title)
-
-
+
def has_id(self, id):
return self.conn.get('SELECT id FROM books where id=?', (id,), all=False) is not None
- def recursive_import(self, root, single_book_per_directory=True, callback=None):
- root = os.path.abspath(root)
- duplicates = []
- for dirpath in os.walk(root):
- res = self.import_book_directory(dirpath[0], callback=callback) if \
- single_book_per_directory else \
- self.import_book_directory_multiple(dirpath[0], callback=callback)
- if res is not None:
- duplicates.extend(res)
- if callable(callback):
- if callback(''):
- break
-
- return duplicates
-
+
class SearchToken(object):
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 8c762f8680..a7f522cad0 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -12,7 +12,7 @@ from itertools import repeat
from datetime import datetime
from PyQt4.QtCore import QCoreApplication, QThread, QReadWriteLock
-from PyQt4.QtGui import QApplication, QPixmap, QImage
+from PyQt4.QtGui import QApplication, QImage
__app = None
from calibre.library import title_sort
@@ -20,12 +20,15 @@ from calibre.library.database import LibraryDatabase
from calibre.library.sqlite import connect, IntegrityError
from calibre.utils.search_query_parser import SearchQueryParser
from calibre.ebooks.metadata import string_to_authors, authors_to_string, MetaInformation
-from calibre.ebooks.metadata.meta import get_metadata, set_metadata
+from calibre.ebooks.metadata.meta import get_metadata, set_metadata, \
+ metadata_from_formats
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_encoding
from calibre.ptempfile import PersistentTemporaryFile
from calibre.customize.ui import run_plugins_on_import
+
from calibre import sanitize_file_name
+from calibre.ebooks import BOOK_EXTENSIONS
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
@@ -627,7 +630,7 @@ class LibraryDatabase2(LibraryDatabase):
if not QCoreApplication.instance():
global __app
__app = QApplication([])
- p = QPixmap()
+ p = QImage()
if callable(getattr(data, 'read', None)):
data = data.read()
p.loadFromData(data)
@@ -1142,7 +1145,7 @@ class LibraryDatabase2(LibraryDatabase):
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=True):
'''
Add a book to the database. The result cache is not updated.
- @param paths: List of paths to book files or file-like objects
+ :param:`paths` List of paths to book files or file-like objects
'''
formats, metadata, uris = iter(formats), iter(metadata), iter(uris)
duplicates = []
@@ -1180,17 +1183,17 @@ class LibraryDatabase2(LibraryDatabase):
self.conn.commit()
self.data.refresh_ids(self.conn, ids) # Needed to update format list and size
if duplicates:
- paths = tuple(duplicate[0] for duplicate in duplicates)
- formats = tuple(duplicate[1] for duplicate in duplicates)
- metadata = tuple(duplicate[2] for duplicate in duplicates)
- uris = tuple(duplicate[3] for duplicate in duplicates)
+ paths = list(duplicate[0] for duplicate in duplicates)
+ formats = list(duplicate[1] for duplicate in duplicates)
+ metadata = list(duplicate[2] for duplicate in duplicates)
+ uris = list(duplicate[3] for duplicate in duplicates)
return (paths, formats, metadata, uris), len(ids)
return None, len(ids)
- def import_book(self, mi, formats):
+ def import_book(self, mi, formats, notify=True):
series_index = 1 if mi.series_index is None else mi.series_index
if not mi.authors:
- mi.authors = ['Unknown']
+ mi.authors = [_('Unknown')]
aus = mi.author_sort if mi.author_sort else ', '.join(mi.authors)
obj = self.conn.execute('INSERT INTO books(title, uri, series_index, author_sort) VALUES (?, ?, ?, ?)',
(mi.title, None, series_index, aus))
@@ -1204,7 +1207,8 @@ class LibraryDatabase2(LibraryDatabase):
self.add_format(id, ext, stream, index_is_id=True)
self.conn.commit()
self.data.refresh_ids(self.conn, [id]) # Needed to update format list and size
- self.notify('add', [id])
+ if notify:
+ self.notify('add', [id])
def move_library_to(self, newloc, progress=None):
header = _(u'Copying books to %s
')%newloc
@@ -1388,6 +1392,10 @@ books_series_link feeds
f = open(os.path.join(base, sanitize_file_name(name)+'.opf'), 'wb')
if not mi.authors:
mi.authors = [_('Unknown')]
+ cdata = self.cover(id, index_is_id=True)
+ cname = sanitize_file_name(name)+'.jpg'
+ open(os.path.join(base, cname), 'wb').write(cdata)
+ mi.cover = cname
opf = OPFCreator(base, mi)
opf.render(f)
f.close()
@@ -1451,6 +1459,88 @@ books_series_link feeds
if not callback(count, title):
break
return failures
+
+ def find_books_in_directory(self, dirpath, single_book_per_directory):
+ dirpath = os.path.abspath(dirpath)
+ if single_book_per_directory:
+ formats = []
+ for path in os.listdir(dirpath):
+ path = os.path.abspath(os.path.join(dirpath, path))
+ if os.path.isdir(path) or not os.access(path, os.R_OK):
+ continue
+ ext = os.path.splitext(path)[1]
+ if not ext:
+ continue
+ ext = ext[1:].lower()
+ if ext not in BOOK_EXTENSIONS:
+ continue
+ formats.append(path)
+ yield formats
+ else:
+ books = {}
+ for path in os.listdir(dirpath):
+ path = os.path.abspath(os.path.join(dirpath, path))
+ if os.path.isdir(path) or not os.access(path, os.R_OK):
+ continue
+ ext = os.path.splitext(path)[1]
+ if not ext:
+ continue
+ ext = ext[1:].lower()
+ if ext not in BOOK_EXTENSIONS:
+ continue
+
+ key = os.path.splitext(path)[0]
+ if not books.has_key(key):
+ books[key] = []
+ books[key].append(path)
+
+ for formats in books.values():
+ yield formats
+
+ def import_book_directory_multiple(self, dirpath, callback=None):
+ duplicates = []
+ for formats in self.find_books_in_directory(dirpath, False):
+ mi = metadata_from_formats(formats)
+ if mi.title is None:
+ continue
+ if self.has_book(mi):
+ duplicates.append((mi, formats))
+ continue
+ self.import_book(mi, formats)
+ if callable(callback):
+ if callback(mi.title):
+ break
+ return duplicates
+
+ def import_book_directory(self, dirpath, callback=None):
+ dirpath = os.path.abspath(dirpath)
+ formats = self.find_books_in_directory(dirpath, True)
+ if not formats:
+ return
+
+ mi = metadata_from_formats(formats)
+ if mi.title is None:
+ return
+ if self.has_book(mi):
+ return [(mi, formats)]
+ self.import_book(mi, formats)
+ if callable(callback):
+ callback(mi.title)
+
+ def recursive_import(self, root, single_book_per_directory=True, callback=None):
+ root = os.path.abspath(root)
+ duplicates = []
+ for dirpath in os.walk(root):
+ res = self.import_book_directory(dirpath[0], callback=callback) if \
+ single_book_per_directory else \
+ self.import_book_directory_multiple(dirpath[0], callback=callback)
+ if res is not None:
+ duplicates.extend(res)
+ if callable(callback):
+ if callback(''):
+ break
+
+ return duplicates
diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py
index 60ae0761cf..dcbec14687 100644
--- a/src/calibre/web/feeds/recipes/__init__.py
+++ b/src/calibre/web/feeds/recipes/__init__.py
@@ -28,6 +28,7 @@ recipe_modules = ['recipe_' + r for r in (
'la_tercera', 'el_mercurio_chile', 'la_cuarta', 'lanacion_chile', 'la_segunda',
'jb_online', 'estadao', 'o_globo', 'vijesti', 'elmundo', 'the_oz',
'honoluluadvertiser', 'starbulletin', 'exiled', 'indy_star', 'dna',
+ 'pobjeda',
)]
import re, imp, inspect, time, os
diff --git a/src/calibre/web/feeds/recipes/recipe_pobjeda.py b/src/calibre/web/feeds/recipes/recipe_pobjeda.py
new file mode 100644
index 0000000000..9a4dbb0eee
--- /dev/null
+++ b/src/calibre/web/feeds/recipes/recipe_pobjeda.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+
+__license__ = 'GPL v3'
+__copyright__ = '2009, Darko Miletic '
+
+'''
+pobjeda.co.me
+'''
+
+import re
+from calibre import strftime
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class Pobjeda(BasicNewsRecipe):
+ title = 'Pobjeda Online'
+ __author__ = 'Darko Miletic'
+ description = 'News from Montenegro'
+ publisher = 'Pobjeda a.d.'
+ category = 'news, politics, Montenegro'
+ language = _('Serbian')
+ oldest_article = 2
+ max_articles_per_feed = 100
+ no_stylesheets = True
+ remove_javascript = True
+ encoding = 'utf8'
+ remove_javascript = True
+ use_embedded_content = False
+ INDEX = u'http://www.pobjeda.co.me'
+ extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{text-align: justify; font-family: serif1, serif} .article_description{font-family: serif1, serif}'
+
+ html2lrf_options = [
+ '--comment', description
+ , '--category', category
+ , '--publisher', publisher
+ ]
+
+ html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
+
+ preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
+
+ keep_only_tags = [dict(name='div', attrs={'class':'vijest'})]
+
+ remove_tags = [dict(name=['object','link'])]
+
+ feeds = [
+ (u'Politika' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=1' )
+ ,(u'Ekonomija' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=2' )
+ ,(u'Drustvo' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=3' )
+ ,(u'Crna Hronika' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=4' )
+ ,(u'Kultura' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=5' )
+ ,(u'Hronika Podgorice' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=7' )
+ ,(u'Feljton' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=8' )
+ ,(u'Crna Gora' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=9' )
+ ,(u'Svijet' , u'http://www.pobjeda.co.me/rubrika.php?rubrika=202')
+ ,(u'Ekonomija i Biznis', u'http://www.pobjeda.co.me/dodatak.php?rubrika=11' )
+ ,(u'Djeciji Svijet' , u'http://www.pobjeda.co.me/dodatak.php?rubrika=12' )
+ ,(u'Kultura i Drustvo' , u'http://www.pobjeda.co.me/dodatak.php?rubrika=13' )
+ ,(u'Agora' , u'http://www.pobjeda.co.me/dodatak.php?rubrika=133')
+ ,(u'Ekologija' , u'http://www.pobjeda.co.me/dodatak.php?rubrika=252')
+ ]
+
+ def preprocess_html(self, soup):
+ soup.html['xml:lang'] = 'sr-Latn-ME'
+ soup.html['lang'] = 'sr-Latn-ME'
+ mtag = ''
+ soup.head.insert(0,mtag)
+ for item in soup.findAll(style=True):
+ del item['style']
+ return soup
+
+ def get_cover_url(self):
+ cover_url = None
+ soup = self.index_to_soup(self.INDEX)
+ cover_item = soup.find('img',attrs={'alt':'Naslovna strana'})
+ if cover_item:
+ cover_url = self.INDEX + cover_item.parent['href']
+ return cover_url
+
+ def parse_index(self):
+ totalfeeds = []
+ lfeeds = self.get_feeds()
+ for feedobj in lfeeds:
+ feedtitle, feedurl = feedobj
+ self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl))
+ articles = []
+ soup = self.index_to_soup(feedurl)
+ for item in soup.findAll('div', attrs={'class':'vijest'}):
+ description = self.tag_to_string(item.h2)
+ atag = item.h1.find('a')
+ if atag:
+ url = self.INDEX + '/' + atag['href']
+ title = self.tag_to_string(atag)
+ date = strftime(self.timefmt)
+ articles.append({
+ 'title' :title
+ ,'date' :date
+ ,'url' :url
+ ,'description':description
+ })
+ totalfeeds.append((feedtitle, articles))
+ return totalfeeds
+