mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
New recipe for Pobjeda by Darko Miletic
This commit is contained in:
parent
dcd258b52b
commit
bc4b6fdf48
@ -1,7 +1,7 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
""" 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):
|
||||
|
224
src/calibre/gui2/add.py
Normal file
224
src/calibre/gui2/add.py
Normal file
@ -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 = _('<p>Books with the same title as the following already '
|
||||
'exist in the database. Add them anyway?<ul>')
|
||||
for mi in self.duplicates[2]:
|
||||
files += '<li>'+mi.title+'</li>\n'
|
||||
d = WarningDialog (_('Duplicates found!'),
|
||||
_('Duplicates found!'),
|
||||
files+'</ul></p>', 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 = _('<p>Books with the same title as the following already '
|
||||
'exist in the database. Add them anyway?<ul>')
|
||||
for mi in self.duplicates:
|
||||
files += '<li>'+mi[0].title+'</li>\n'
|
||||
d = WarningDialog (_('Duplicates found!'),
|
||||
_('Duplicates found!'),
|
||||
files+'</ul></p>', 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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
BIN
src/calibre/gui2/images/news/pobjeda.png
Normal file
BIN
src/calibre/gui2/images/news/pobjeda.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 285 B |
@ -13,7 +13,6 @@ from PyQt4.QtSvg import QSvgRenderer
|
||||
from calibre import __version__, __appname__, islinux, sanitize_file_name, \
|
||||
iswindows, isosx, preferred_encoding
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
from calibre.devices.errors import FreeSpaceError
|
||||
from calibre.devices.interface import Device
|
||||
from calibre.utils.config import prefs, dynamic
|
||||
@ -23,7 +22,7 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
|
||||
set_sidebar_directories, Dispatcher, \
|
||||
SingleApplication, Application, available_height, \
|
||||
max_available_height, config, info_dialog, \
|
||||
available_width
|
||||
available_width, GetMetadata
|
||||
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
||||
from calibre.gui2.dialogs.scheduler import Scheduler
|
||||
from calibre.gui2.update import CheckForUpdates
|
||||
@ -78,6 +77,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle(__appname__)
|
||||
self.verbose = opts.verbose
|
||||
self.get_metadata = GetMetadata()
|
||||
self.read_settings()
|
||||
self.job_manager = JobManager()
|
||||
self.jobs_dialog = JobsDialog(self, self.job_manager)
|
||||
@ -608,36 +608,26 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
################################# Add books ################################
|
||||
|
||||
def add_recursive(self, single):
|
||||
root = choose_dir(self, 'recursive book import root dir dialog', 'Select root folder')
|
||||
root = choose_dir(self, 'recursive book import root dir dialog',
|
||||
'Select root folder')
|
||||
if not root:
|
||||
return
|
||||
progress = ProgressDialog(_('Adding books recursively...'),
|
||||
min=0, max=0, parent=self)
|
||||
progress.show()
|
||||
def callback(msg):
|
||||
if msg != '.':
|
||||
progress.set_msg((_('Added ')+msg) if msg else _('Searching...'))
|
||||
QApplication.processEvents()
|
||||
QApplication.sendPostedEvents()
|
||||
QApplication.flush()
|
||||
return progress.canceled
|
||||
try:
|
||||
duplicates = self.library_view.model().db.recursive_import(root, single, callback=callback)
|
||||
finally:
|
||||
progress.hide()
|
||||
if duplicates:
|
||||
files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>')
|
||||
for mi, formats in duplicates:
|
||||
files += '<li>'+mi.title+'</li>\n'
|
||||
d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'),
|
||||
files+'</ul></p>', 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 = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>')
|
||||
for mi in duplicates[2]:
|
||||
files += '<li>'+mi.title+'</li>\n'
|
||||
d = WarningDialog(_('Duplicates found!'), _('Duplicates found!'), files+'</ul></p>', 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('<p>'+_('The selected books will be <b>permanently deleted</b> and the files removed from your computer. Are you sure?')+'</p>', 'library_delete_books', self):
|
||||
if not confirm('<p>'+_('The selected books will be '
|
||||
'<b>permanently deleted</b> and the files '
|
||||
'removed from your computer. Are you sure?')
|
||||
+'</p>', '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)
|
||||
|
@ -4,13 +4,10 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
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):
|
||||
|
@ -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'<p>Copying books to %s<br><center>')%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
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
102
src/calibre/web/feeds/recipes/recipe_pobjeda.py
Normal file
102
src/calibre/web/feeds/recipes/recipe_pobjeda.py
Normal file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
|
||||
'''
|
||||
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 = '<meta http-equiv="Content-Language" content="sr-Latn-ME"/>'
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user