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'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
""" The GUI """
|
""" 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, \
|
from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSize, \
|
||||||
QByteArray, QLocale, QUrl, QTranslator, QCoreApplication, \
|
QByteArray, QLocale, QUrl, QTranslator, QCoreApplication, \
|
||||||
QModelIndex
|
QModelIndex
|
||||||
@ -14,6 +14,9 @@ from calibre import __author__, islinux, iswindows, isosx
|
|||||||
from calibre.startup import get_lang
|
from calibre.startup import get_lang
|
||||||
from calibre.utils.config import Config, ConfigProxy, dynamic
|
from calibre.utils.config import Config, ConfigProxy, dynamic
|
||||||
import calibre.resources as resources
|
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
|
NONE = QVariant() #: Null value to return from the data function of item models
|
||||||
|
|
||||||
@ -149,6 +152,40 @@ class Dispatcher(QObject):
|
|||||||
def dispatch(self, args, kwargs):
|
def dispatch(self, args, kwargs):
|
||||||
self.func(*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):
|
class TableView(QTableView):
|
||||||
def __init__(self, parent):
|
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 import dynamic
|
||||||
from calibre.gui2.dialogs.confirm_delete_ui import Ui_Dialog
|
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):
|
def _config_name(name):
|
||||||
return name + '_again'
|
return name + '_again'
|
||||||
@ -19,6 +19,7 @@ class Dialog(QDialog, Ui_Dialog):
|
|||||||
self.msg.setText(msg)
|
self.msg.setText(msg)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.connect(self.again, SIGNAL('stateChanged(int)'), self.toggle)
|
self.connect(self.again, SIGNAL('stateChanged(int)'), self.toggle)
|
||||||
|
self.buttonBox.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
|
|
||||||
def toggle(self, x):
|
def toggle(self, x):
|
||||||
|
@ -20,6 +20,7 @@ class ProgressDialog(QDialog, Ui_Dialog):
|
|||||||
self.setWindowModality(Qt.ApplicationModal)
|
self.setWindowModality(Qt.ApplicationModal)
|
||||||
self.set_min(min)
|
self.set_min(min)
|
||||||
self.set_max(max)
|
self.set_max(max)
|
||||||
|
self.bar.setValue(min)
|
||||||
self.canceled = False
|
self.canceled = False
|
||||||
|
|
||||||
self.connect(self.button_box, SIGNAL('rejected()'), self._canceled)
|
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, \
|
from calibre import __version__, __appname__, islinux, sanitize_file_name, \
|
||||||
iswindows, isosx, preferred_encoding
|
iswindows, isosx, preferred_encoding
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.ebooks.metadata.meta import get_metadata
|
|
||||||
from calibre.devices.errors import FreeSpaceError
|
from calibre.devices.errors import FreeSpaceError
|
||||||
from calibre.devices.interface import Device
|
from calibre.devices.interface import Device
|
||||||
from calibre.utils.config import prefs, dynamic
|
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, \
|
set_sidebar_directories, Dispatcher, \
|
||||||
SingleApplication, Application, available_height, \
|
SingleApplication, Application, available_height, \
|
||||||
max_available_height, config, info_dialog, \
|
max_available_height, config, info_dialog, \
|
||||||
available_width
|
available_width, GetMetadata
|
||||||
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
||||||
from calibre.gui2.dialogs.scheduler import Scheduler
|
from calibre.gui2.dialogs.scheduler import Scheduler
|
||||||
from calibre.gui2.update import CheckForUpdates
|
from calibre.gui2.update import CheckForUpdates
|
||||||
@ -78,6 +77,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.setWindowTitle(__appname__)
|
self.setWindowTitle(__appname__)
|
||||||
self.verbose = opts.verbose
|
self.verbose = opts.verbose
|
||||||
|
self.get_metadata = GetMetadata()
|
||||||
self.read_settings()
|
self.read_settings()
|
||||||
self.job_manager = JobManager()
|
self.job_manager = JobManager()
|
||||||
self.jobs_dialog = JobsDialog(self, self.job_manager)
|
self.jobs_dialog = JobsDialog(self, self.job_manager)
|
||||||
@ -608,35 +608,25 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
################################# Add books ################################
|
################################# Add books ################################
|
||||||
|
|
||||||
def add_recursive(self, single):
|
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:
|
if not root:
|
||||||
return
|
return
|
||||||
progress = ProgressDialog(_('Adding books recursively...'),
|
from calibre.gui2.add import AddRecursive
|
||||||
min=0, max=0, parent=self)
|
self._add_recursive_thread = AddRecursive(root,
|
||||||
progress.show()
|
self.library_view.model().db, self.get_metadata,
|
||||||
def callback(msg):
|
single, self)
|
||||||
if msg != '.':
|
self.connect(self._add_recursive_thread, SIGNAL('finished()'),
|
||||||
progress.set_msg((_('Added ')+msg) if msg else _('Searching...'))
|
self._recursive_files_added)
|
||||||
QApplication.processEvents()
|
self._add_recursive_thread.start()
|
||||||
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()
|
def _recursive_files_added(self):
|
||||||
self.library_view.model().research()
|
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):
|
def add_recursive_single(self, checked):
|
||||||
'''
|
'''
|
||||||
@ -686,65 +676,40 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
return
|
return
|
||||||
to_device = self.stack.currentIndex() != 0
|
to_device = self.stack.currentIndex() != 0
|
||||||
self._add_books(books, to_device)
|
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):
|
def _add_books(self, paths, to_device, on_card=None):
|
||||||
if on_card is None:
|
if on_card is None:
|
||||||
on_card = self.stack.currentIndex() == 2
|
on_card = self.stack.currentIndex() == 2
|
||||||
if not paths:
|
if not paths:
|
||||||
return
|
return
|
||||||
# Get format and metadata information
|
from calibre.gui2.add import AddFiles
|
||||||
formats, metadata, names, infos = [], [], [], []
|
self._add_files_thread = AddFiles(paths, self.default_thumbnail,
|
||||||
progress = ProgressDialog(_('Adding books...'), _('Reading metadata...'),
|
self.get_metadata,
|
||||||
min=0, max=len(paths), parent=self)
|
None if to_device else \
|
||||||
progress.show()
|
self.library_view.model().db
|
||||||
try:
|
)
|
||||||
for c, book in enumerate(paths):
|
self._add_files_thread.send_to_device = to_device
|
||||||
progress.set_value(c+1)
|
self._add_files_thread.on_card = on_card
|
||||||
if progress.canceled:
|
self._add_files_thread.create_progress_dialog(_('Adding books...'),
|
||||||
return
|
_('Reading metadata...'), self)
|
||||||
format = os.path.splitext(book)[1]
|
self.connect(self._add_files_thread, SIGNAL('finished()'),
|
||||||
format = format[1:] if format else None
|
self._files_added)
|
||||||
stream = open(book, 'rb')
|
self._add_files_thread.start()
|
||||||
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:
|
def _files_added(self):
|
||||||
progress.set_msg(_('Adding books to database...'))
|
t = self._add_files_thread
|
||||||
QApplication.processEvents()
|
self._add_files_thread = None
|
||||||
model = self.library_view.model()
|
if not t.canceled:
|
||||||
|
if t.send_to_device:
|
||||||
paths = list(paths)
|
self.upload_books(t.paths,
|
||||||
duplicates, number_added = model.add_books(paths, formats, metadata)
|
list(map(sanitize_file_name, t.names)),
|
||||||
if duplicates:
|
t.infos, on_card=t.on_card)
|
||||||
files = _('<p>Books with the same title as the following already exist in the database. Add them anyway?<ul>')
|
self.status_bar.showMessage(_('Uploading books to device.'), 2000)
|
||||||
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)
|
|
||||||
else:
|
else:
|
||||||
self.upload_books(paths, list(map(sanitize_file_name, names)), infos, on_card=on_card)
|
t.process_duplicates()
|
||||||
finally:
|
if t.number_of_books_added > 0:
|
||||||
QApplication.processEvents()
|
self.library_view.model().books_added(t.number_of_books_added)
|
||||||
progress.hide()
|
|
||||||
|
|
||||||
def upload_books(self, files, names, metadata, on_card=False, memory=None):
|
def upload_books(self, files, names, metadata, on_card=False, memory=None):
|
||||||
'''
|
'''
|
||||||
@ -801,7 +766,10 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
if not rows or len(rows) == 0:
|
if not rows or len(rows) == 0:
|
||||||
return
|
return
|
||||||
if self.stack.currentIndex() == 0:
|
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
|
return
|
||||||
view.model().delete_books(rows)
|
view.model().delete_books(rows)
|
||||||
else:
|
else:
|
||||||
@ -1410,8 +1378,15 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
def initialize_database(self):
|
def initialize_database(self):
|
||||||
self.library_path = prefs['library_path']
|
self.library_path = prefs['library_path']
|
||||||
if self.library_path is None: # Need to migrate to new database layout
|
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,
|
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:
|
if not dir:
|
||||||
dir = os.path.expanduser('~/Library')
|
dir = os.path.expanduser('~/Library')
|
||||||
self.library_path = os.path.abspath(dir)
|
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.
|
Backend that implements storage of ebooks in an sqlite database.
|
||||||
'''
|
'''
|
||||||
import sqlite3 as sqlite
|
import sqlite3 as sqlite
|
||||||
import datetime, re, os, cPickle, sre_constants
|
import datetime, re, cPickle, sre_constants
|
||||||
from zlib import compress, decompress
|
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.metadata import MetaInformation
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
|
||||||
from calibre.web.feeds.recipes import migrate_automatic_profile_to_automatic_recipe
|
from calibre.web.feeds.recipes import migrate_automatic_profile_to_automatic_recipe
|
||||||
|
|
||||||
class Concatenate(object):
|
class Concatenate(object):
|
||||||
@ -1391,116 +1388,11 @@ 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):
|
def has_id(self, id):
|
||||||
return self.conn.get('SELECT id FROM books where id=?', (id,), all=False) is not None
|
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ from itertools import repeat
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt4.QtCore import QCoreApplication, QThread, QReadWriteLock
|
from PyQt4.QtCore import QCoreApplication, QThread, QReadWriteLock
|
||||||
from PyQt4.QtGui import QApplication, QPixmap, QImage
|
from PyQt4.QtGui import QApplication, QImage
|
||||||
__app = None
|
__app = None
|
||||||
|
|
||||||
from calibre.library import title_sort
|
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.library.sqlite import connect, IntegrityError
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
from calibre.ebooks.metadata import string_to_authors, authors_to_string, MetaInformation
|
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.ebooks.metadata.opf2 import OPFCreator
|
||||||
from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_encoding
|
from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_encoding
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.customize.ui import run_plugins_on_import
|
from calibre.customize.ui import run_plugins_on_import
|
||||||
|
|
||||||
from calibre import sanitize_file_name
|
from calibre import sanitize_file_name
|
||||||
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
|
|
||||||
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
||||||
|
|
||||||
@ -627,7 +630,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
if not QCoreApplication.instance():
|
if not QCoreApplication.instance():
|
||||||
global __app
|
global __app
|
||||||
__app = QApplication([])
|
__app = QApplication([])
|
||||||
p = QPixmap()
|
p = QImage()
|
||||||
if callable(getattr(data, 'read', None)):
|
if callable(getattr(data, 'read', None)):
|
||||||
data = data.read()
|
data = data.read()
|
||||||
p.loadFromData(data)
|
p.loadFromData(data)
|
||||||
@ -1142,7 +1145,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=True):
|
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=True):
|
||||||
'''
|
'''
|
||||||
Add a book to the database. The result cache is not updated.
|
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)
|
formats, metadata, uris = iter(formats), iter(metadata), iter(uris)
|
||||||
duplicates = []
|
duplicates = []
|
||||||
@ -1180,17 +1183,17 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
self.data.refresh_ids(self.conn, ids) # Needed to update format list and size
|
self.data.refresh_ids(self.conn, ids) # Needed to update format list and size
|
||||||
if duplicates:
|
if duplicates:
|
||||||
paths = tuple(duplicate[0] for duplicate in duplicates)
|
paths = list(duplicate[0] for duplicate in duplicates)
|
||||||
formats = tuple(duplicate[1] for duplicate in duplicates)
|
formats = list(duplicate[1] for duplicate in duplicates)
|
||||||
metadata = tuple(duplicate[2] for duplicate in duplicates)
|
metadata = list(duplicate[2] for duplicate in duplicates)
|
||||||
uris = tuple(duplicate[3] for duplicate in duplicates)
|
uris = list(duplicate[3] for duplicate in duplicates)
|
||||||
return (paths, formats, metadata, uris), len(ids)
|
return (paths, formats, metadata, uris), len(ids)
|
||||||
return None, 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
|
series_index = 1 if mi.series_index is None else mi.series_index
|
||||||
if not mi.authors:
|
if not mi.authors:
|
||||||
mi.authors = ['Unknown']
|
mi.authors = [_('Unknown')]
|
||||||
aus = mi.author_sort if mi.author_sort else ', '.join(mi.authors)
|
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 (?, ?, ?, ?)',
|
obj = self.conn.execute('INSERT INTO books(title, uri, series_index, author_sort) VALUES (?, ?, ?, ?)',
|
||||||
(mi.title, None, series_index, aus))
|
(mi.title, None, series_index, aus))
|
||||||
@ -1204,7 +1207,8 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
self.add_format(id, ext, stream, index_is_id=True)
|
self.add_format(id, ext, stream, index_is_id=True)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
self.data.refresh_ids(self.conn, [id]) # Needed to update format list and size
|
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):
|
def move_library_to(self, newloc, progress=None):
|
||||||
header = _(u'<p>Copying books to %s<br><center>')%newloc
|
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')
|
f = open(os.path.join(base, sanitize_file_name(name)+'.opf'), 'wb')
|
||||||
if not mi.authors:
|
if not mi.authors:
|
||||||
mi.authors = [_('Unknown')]
|
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 = OPFCreator(base, mi)
|
||||||
opf.render(f)
|
opf.render(f)
|
||||||
f.close()
|
f.close()
|
||||||
@ -1452,5 +1460,87 @@ books_series_link feeds
|
|||||||
break
|
break
|
||||||
return failures
|
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',
|
'la_tercera', 'el_mercurio_chile', 'la_cuarta', 'lanacion_chile', 'la_segunda',
|
||||||
'jb_online', 'estadao', 'o_globo', 'vijesti', 'elmundo', 'the_oz',
|
'jb_online', 'estadao', 'o_globo', 'vijesti', 'elmundo', 'the_oz',
|
||||||
'honoluluadvertiser', 'starbulletin', 'exiled', 'indy_star', 'dna',
|
'honoluluadvertiser', 'starbulletin', 'exiled', 'indy_star', 'dna',
|
||||||
|
'pobjeda',
|
||||||
)]
|
)]
|
||||||
|
|
||||||
import re, imp, inspect, time, os
|
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