mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
View non LRF ebook formats in their associated viewers
This commit is contained in:
parent
e1b4efb067
commit
7a3de8a3dd
@ -18,7 +18,7 @@ __docformat__ = "epytext"
|
|||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
__appname__ = 'libprs500'
|
__appname__ = 'libprs500'
|
||||||
|
|
||||||
import sys, os, logging, mechanize, locale, cStringIO, re
|
import sys, os, logging, mechanize, locale, cStringIO, re, subprocess
|
||||||
from gettext import GNUTranslations
|
from gettext import GNUTranslations
|
||||||
from math import floor
|
from math import floor
|
||||||
from optparse import OptionParser as _OptionParser
|
from optparse import OptionParser as _OptionParser
|
||||||
@ -224,3 +224,12 @@ def detect_ncpus():
|
|||||||
return ncpus
|
return ncpus
|
||||||
#return the default value
|
#return the default value
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
def launch(path_or_url):
|
||||||
|
if islinux:
|
||||||
|
subprocess.Popen(('xdg-open', path_or_url))
|
||||||
|
elif isosx:
|
||||||
|
subprocess.Popen(('open', path_or_url))
|
||||||
|
elif iswindows:
|
||||||
|
import win32api
|
||||||
|
win32api.ShellExecute(0, 'open', path_or_url, None, os.getcwd(), 0)
|
||||||
|
@ -20,7 +20,7 @@ from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
|
|||||||
QToolButton, QDialog
|
QToolButton, QDialog
|
||||||
from PyQt4.QtSvg import QSvgRenderer
|
from PyQt4.QtSvg import QSvgRenderer
|
||||||
|
|
||||||
from libprs500 import __version__, __appname__, islinux, sanitize_file_name
|
from libprs500 import __version__, __appname__, islinux, sanitize_file_name, launch
|
||||||
from libprs500.ptempfile import PersistentTemporaryFile
|
from libprs500.ptempfile import PersistentTemporaryFile
|
||||||
from libprs500.ebooks.metadata.meta import get_metadata, get_filename_pat, set_filename_pat
|
from libprs500.ebooks.metadata.meta import get_metadata, get_filename_pat, set_filename_pat
|
||||||
from libprs500.devices.errors import FreeSpaceError
|
from libprs500.devices.errors import FreeSpaceError
|
||||||
@ -46,6 +46,7 @@ from libprs500.gui2.dialogs.lrf_single import LRFSingleDialog, LRFBulkDialog
|
|||||||
from libprs500.gui2.dialogs.config import ConfigDialog
|
from libprs500.gui2.dialogs.config import ConfigDialog
|
||||||
from libprs500.gui2.dialogs.search import SearchDialog
|
from libprs500.gui2.dialogs.search import SearchDialog
|
||||||
from libprs500.gui2.dialogs.user_profiles import UserProfiles
|
from libprs500.gui2.dialogs.user_profiles import UserProfiles
|
||||||
|
from libprs500.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||||
from libprs500.library.database import DatabaseLocked
|
from libprs500.library.database import DatabaseLocked
|
||||||
from libprs500.ebooks.metadata.meta import set_metadata
|
from libprs500.ebooks.metadata.meta import set_metadata
|
||||||
from libprs500.ebooks.metadata import MetaInformation
|
from libprs500.ebooks.metadata import MetaInformation
|
||||||
@ -137,10 +138,16 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
self.save_menu.addAction(_('Save to disk'))
|
self.save_menu.addAction(_('Save to disk'))
|
||||||
self.save_menu.addAction(_('Save to disk in a single directory'))
|
self.save_menu.addAction(_('Save to disk in a single directory'))
|
||||||
|
|
||||||
|
self.view_menu = QMenu()
|
||||||
|
self.view_menu.addAction(_('View'))
|
||||||
|
self.view_menu.addAction(_('View specific format'))
|
||||||
|
self.action_view.setMenu(self.view_menu)
|
||||||
QObject.connect(self.action_save, SIGNAL("triggered(bool)"), self.save_to_disk)
|
QObject.connect(self.action_save, SIGNAL("triggered(bool)"), self.save_to_disk)
|
||||||
QObject.connect(self.save_menu.actions()[0], SIGNAL("triggered(bool)"), self.save_to_disk)
|
QObject.connect(self.save_menu.actions()[0], SIGNAL("triggered(bool)"), self.save_to_disk)
|
||||||
QObject.connect(self.save_menu.actions()[1], SIGNAL("triggered(bool)"), self.save_to_single_dir)
|
QObject.connect(self.save_menu.actions()[1], SIGNAL("triggered(bool)"), self.save_to_single_dir)
|
||||||
QObject.connect(self.action_view, SIGNAL("triggered(bool)"), self.view_book)
|
QObject.connect(self.action_view, SIGNAL("triggered(bool)"), self.view_book)
|
||||||
|
QObject.connect(self.view_menu.actions()[0], SIGNAL("triggered(bool)"), self.view_book)
|
||||||
|
QObject.connect(self.view_menu.actions()[1], SIGNAL("triggered(bool)"), self.view_specific_format)
|
||||||
self.action_sync.setMenu(sm)
|
self.action_sync.setMenu(sm)
|
||||||
self.action_edit.setMenu(md)
|
self.action_edit.setMenu(md)
|
||||||
self.action_save.setMenu(self.save_menu)
|
self.action_save.setMenu(self.save_menu)
|
||||||
@ -164,6 +171,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
self.tool_bar.widgetForAction(self.action_convert).setPopupMode(QToolButton.MenuButtonPopup)
|
self.tool_bar.widgetForAction(self.action_convert).setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
self.tool_bar.widgetForAction(self.action_save).setPopupMode(QToolButton.MenuButtonPopup)
|
self.tool_bar.widgetForAction(self.action_save).setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
self.tool_bar.widgetForAction(self.action_add).setPopupMode(QToolButton.MenuButtonPopup)
|
self.tool_bar.widgetForAction(self.action_add).setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
|
self.tool_bar.widgetForAction(self.action_view).setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
|
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
|
||||||
|
|
||||||
QObject.connect(self.config_button, SIGNAL('clicked(bool)'), self.do_config)
|
QObject.connect(self.config_button, SIGNAL('clicked(bool)'), self.do_config)
|
||||||
@ -732,6 +740,38 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
#############################View book######################################
|
#############################View book######################################
|
||||||
|
|
||||||
|
def view_format(self, row, format):
|
||||||
|
pt = PersistentTemporaryFile('_viewer')
|
||||||
|
pt.write(self.library_view.model().db.format(row, format))
|
||||||
|
pt.close()
|
||||||
|
self.persistent_files.append(pt)
|
||||||
|
if format.upper() == 'LRF':
|
||||||
|
args = ['lrfviewer', pt.name]
|
||||||
|
self.job_manager.process_server.run('viewer%d'%self.viewer_job_id,
|
||||||
|
'lrfviewer', kwdargs=dict(args=args),
|
||||||
|
monitor=False)
|
||||||
|
self.viewer_job_id += 1
|
||||||
|
else:
|
||||||
|
launch(pt.name)
|
||||||
|
time.sleep(2) # User feedback
|
||||||
|
|
||||||
|
def view_specific_format(self, triggered):
|
||||||
|
rows = self.library_view.selectionModel().selectedRows()
|
||||||
|
if not rows or len(rows) == 0:
|
||||||
|
d = error_dialog(self, _('Cannot view'), _('No book selected'))
|
||||||
|
d.exec_()
|
||||||
|
return
|
||||||
|
|
||||||
|
row = rows[0].row()
|
||||||
|
formats = self.library_view.model().db.formats(row).upper().split(',')
|
||||||
|
d = ChooseFormatDialog(self, _('Choose the format to view'), formats)
|
||||||
|
d.exec_()
|
||||||
|
if d.result() == QDialog.Accepted:
|
||||||
|
format = d.format()
|
||||||
|
self.view_format(row, format)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
def view_book(self, triggered):
|
def view_book(self, triggered):
|
||||||
rows = self.library_view.selectionModel().selectedRows()
|
rows = self.library_view.selectionModel().selectedRows()
|
||||||
if not rows or len(rows) == 0:
|
if not rows or len(rows) == 0:
|
||||||
@ -740,25 +780,29 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
row = rows[0].row()
|
row = rows[0].row()
|
||||||
formats = self.library_view.model().db.formats(row)
|
formats = self.library_view.model().db.formats(row).upper().split(',')
|
||||||
title = self.library_view.model().db.title(row)
|
title = self.library_view.model().db.title(row)
|
||||||
id = self.library_view.model().db.id(row)
|
id = self.library_view.model().db.id(row)
|
||||||
if 'LRF' not in formats.upper():
|
format = None
|
||||||
|
if len(formats) == 1:
|
||||||
|
format = formats[0]
|
||||||
|
if 'LRF' in formats:
|
||||||
|
format = 'LRF'
|
||||||
|
if not formats:
|
||||||
d = error_dialog(self, _('Cannot view'),
|
d = error_dialog(self, _('Cannot view'),
|
||||||
_('%s is not available in LRF format. Please convert it first.')%(title,))
|
_('%s has no available formats.')%(title,))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
return
|
return
|
||||||
|
if format is None:
|
||||||
|
d = ChooseFormatDialog(self, _('Choose the format to view'), formats)
|
||||||
|
d.exec_()
|
||||||
|
if d.result() == QDialog.Accepted:
|
||||||
|
format = d.format()
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.view_format(row, format)
|
||||||
|
|
||||||
pt = PersistentTemporaryFile('_viewer')
|
|
||||||
pt.write(self.library_view.model().db.format(row, 'LRF'))
|
|
||||||
pt.close()
|
|
||||||
self.persistent_files.append(pt)
|
|
||||||
args = ['lrfviewer', pt.name]
|
|
||||||
self.job_manager.process_server.run('viewer%d'%self.viewer_job_id,
|
|
||||||
'lrfviewer', kwdargs=dict(args=args),
|
|
||||||
monitor=False)
|
|
||||||
self.viewer_job_id += 1
|
|
||||||
time.sleep(2) # User feedback
|
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
|
@ -3,6 +3,14 @@ body {
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
.documentHeading {
|
.documentHeading {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
|
@ -80,14 +80,75 @@
|
|||||||
<code>LRF, EPUB, LIT, MOBI, RTF, PDF, PRC, HTML</code>. In addition it tries to guess metadata from the filename.
|
<code>LRF, EPUB, LIT, MOBI, RTF, PDF, PRC, HTML</code>. In addition it tries to guess metadata from the filename.
|
||||||
See <a href="#config_filename_metadata">Configuration</a> section, to learn how to configure this.
|
See <a href="#config_filename_metadata">Configuration</a> section, to learn how to configure this.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
To add a new format to an existing book, use the <a href="#edit_meta_information">Edit meta information</a> action.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3 class="subsectionHeading" id="remove_books">Remove books</h3>
|
<h3 class="subsectionHeading" id="remove_books">Remove books</h3>
|
||||||
|
<p>
|
||||||
|
The <span class="action">Remove books</span> action deletes books permanently, so use it with care.
|
||||||
|
It is <i>context sensitive</i>,
|
||||||
|
i.e. it depends on which <a href="#catalogs">catalog</a> you have selected. If you have selected the
|
||||||
|
library, books will be removed from the library. If you have selected the ebook reader device, the books
|
||||||
|
will be removed from the device. To remove only a particular format for a given book use the
|
||||||
|
<a href="#edit_meta_information">Edit meta information</a> action.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3 class="subsectionHeading" id="edit_meta_information">Edit meta information</h3>
|
<h3 class="subsectionHeading" id="edit_meta_information">Edit meta information</h3>
|
||||||
|
<p>
|
||||||
|
The <span class="action">Edit meta information</span> action has two variations, accessed by the
|
||||||
|
arrow next to the button.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>Edit metadata individually</b>: This allows you to edit the metadata of books one-by-one, with the option
|
||||||
|
of fetching metadata, including covers from the internet. It also allows you to add/remove particular
|
||||||
|
ebook formats from a book. For more detail see <a href="metadata.html">Editing metadata</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Edit metadata in bulk</b>: This allows you to edit common metadata fields for large numbers of books simulataneously. It
|
||||||
|
operates on all the books you have selected in the <a href="#library">Library view</a>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h3 class="subsectionHeading" id="send_to_device">Send to device</h3>
|
<h3 class="subsectionHeading" id="send_to_device">Send to device</h3>
|
||||||
|
<p>
|
||||||
|
The <span class="action">Send to device</span> action has two variations, accessed by the
|
||||||
|
arrow next to the button.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>Send to main memory</b>: The selected books are transferred to the main memory of the ebook reader.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Send to card</b>: The selected books are transferred to the storage card on the ebook reader.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h3 class="subsectionHeading" id="save_to_disk">Save to disk</h3>
|
<h3 class="subsectionHeading" id="save_to_disk">Save to disk</h3>
|
||||||
|
<p>
|
||||||
|
The <span class="action">Save to disk</span> action has two variations, accessed by the
|
||||||
|
arrow next to the button.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li id="save_to_disk_multiple">
|
||||||
|
<b>Save to disk</b>: This will save the selected books to disk organized in directories. The directory
|
||||||
|
structure looks like:
|
||||||
|
<pre>
|
||||||
|
Author
|
||||||
|
Title
|
||||||
|
Book Files
|
||||||
|
</pre>
|
||||||
|
</li>
|
||||||
|
<li id="save_to_disk_single">
|
||||||
|
<b>Save to disk in a single directory</b>: The selected books are saved to disk in a single directory.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
All available formats as well as metadata is stored to disk for each selected book. Metadata is stored in an OPF file.
|
||||||
|
Saved books can be re-imported to the library without any loss of information by using the <a href="#add_books">Add books</a>
|
||||||
|
action.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3 class="subsectionHeading" id="fetch_news">Fetch news</h3>
|
<h3 class="subsectionHeading" id="fetch_news">Fetch news</h3>
|
||||||
|
|
||||||
|
@ -19,5 +19,14 @@
|
|||||||
<item url="common.css" />
|
<item url="common.css" />
|
||||||
<item url="gui.html" />
|
<item url="gui.html" />
|
||||||
<item url="images/" />
|
<item url="images/" />
|
||||||
|
<item url="images/actions.png" />
|
||||||
|
<item url="images/book_details.png" />
|
||||||
|
<item url="images/catalogs.png" />
|
||||||
|
<item url="images/configuration.png" />
|
||||||
|
<item url="images/jobs.png" />
|
||||||
|
<item url="images/library.png" />
|
||||||
|
<item url="images/search.png" />
|
||||||
|
<item url="libprs500.session" />
|
||||||
|
<item url="libprs500.webprj" />
|
||||||
</project>
|
</project>
|
||||||
</webproject>
|
</webproject>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user