mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk and fix case change in edit metadata single.
This commit is contained in:
commit
ec776e3052
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>'
|
__copyright__ = u'2010-2011, Tomasz Dlugosz <tomek3d@gmail.com>'
|
||||||
'''
|
'''
|
||||||
frazpc.pl
|
frazpc.pl
|
||||||
'''
|
'''
|
||||||
@ -19,17 +19,20 @@ class FrazPC(BasicNewsRecipe):
|
|||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
|
||||||
feeds = [(u'Aktualno\u015bci', u'http://www.frazpc.pl/feed'), (u'Recenzje', u'http://www.frazpc.pl/kat/recenzje-2/feed') ]
|
feeds = [
|
||||||
|
(u'Aktualno\u015bci', u'http://www.frazpc.pl/feed/aktualnosci'),
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':'FRAZ_CONTENT'})]
|
(u'Artyku\u0142y', u'http://www.frazpc.pl/feed/artykuly')
|
||||||
|
|
||||||
remove_tags = [dict(name='p', attrs={'class':'gray tagsP fs11'})]
|
|
||||||
|
|
||||||
preprocess_regexps = [
|
|
||||||
(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
|
||||||
[(r'<div id="post-[0-9]*"', lambda match: '<div id="FRAZ_CONTENT"'),
|
|
||||||
(r'href="/f/news/', lambda match: 'href="http://www.frazpc.pl/f/news/'),
|
|
||||||
(r' <a href="http://www.frazpc.pl/[^>]*?">(Skomentuj|Komentarz(e)?\([0-9]*\))</a> \|', lambda match: '')]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'class':'article'})]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'class':'title-wrapper'}),
|
||||||
|
dict(name='p', attrs={'class':'tags'}),
|
||||||
|
dict(name='p', attrs={'class':'article-links'}),
|
||||||
|
dict(name='div', attrs={'class':'comments_box'})
|
||||||
|
]
|
||||||
|
|
||||||
|
preprocess_regexps = [(re.compile(r'\| <a href="#comments">Komentarze \([0-9]*\)</a>'), lambda match: '')]
|
||||||
|
|
||||||
remove_attributes = [ 'width', 'height' ]
|
remove_attributes = [ 'width', 'height' ]
|
||||||
|
@ -607,6 +607,7 @@ class StoreBase(Plugin): # {{{
|
|||||||
supported_platforms = ['windows', 'osx', 'linux']
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
author = 'John Schember'
|
author = 'John Schember'
|
||||||
type = _('Store')
|
type = _('Store')
|
||||||
|
minimum_calibre_version = (0, 7, 59)
|
||||||
|
|
||||||
actual_plugin = None
|
actual_plugin = None
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ __docformat__ = 'restructuredtext en'
|
|||||||
Device driver for BOEYE serial readers
|
Device driver for BOEYE serial readers
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import re
|
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
class BOEYE_BEX(USBMS):
|
class BOEYE_BEX(USBMS):
|
||||||
|
@ -298,11 +298,12 @@ class AuthorSortEdit(EnLineEdit):
|
|||||||
self.current_val = self.db.author_sort_from_authors(authors)
|
self.current_val = self.db.author_sort_from_authors(authors)
|
||||||
|
|
||||||
def initialize(self, db, id_):
|
def initialize(self, db, id_):
|
||||||
self.current_val = db.author_sort(id_, index_is_id=True)
|
self.current_val = self.original_val = db.author_sort(id_, index_is_id=True)
|
||||||
|
|
||||||
def commit(self, db, id_):
|
def commit(self, db, id_):
|
||||||
aus = self.current_val
|
aus = self.current_val
|
||||||
db.set_author_sort(id_, aus, notify=False, commit=False)
|
if aus != self.original_val:
|
||||||
|
db.set_author_sort(id_, aus, notify=False, commit=False)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
@ -13,7 +13,7 @@ from functools import partial
|
|||||||
from PyQt4.Qt import (Qt, QVBoxLayout, QHBoxLayout, QWidget, QPushButton,
|
from PyQt4.Qt import (Qt, QVBoxLayout, QHBoxLayout, QWidget, QPushButton,
|
||||||
QGridLayout, pyqtSignal, QDialogButtonBox, QScrollArea, QFont,
|
QGridLayout, pyqtSignal, QDialogButtonBox, QScrollArea, QFont,
|
||||||
QTabWidget, QIcon, QToolButton, QSplitter, QGroupBox, QSpacerItem,
|
QTabWidget, QIcon, QToolButton, QSplitter, QGroupBox, QSpacerItem,
|
||||||
QSizePolicy, QPalette, QFrame, QSize, QKeySequence, QMenu, QLabel)
|
QSizePolicy, QPalette, QFrame, QSize, QKeySequence, QMenu)
|
||||||
|
|
||||||
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
||||||
from calibre.gui2 import ResizableDialog, error_dialog, gprefs, pixmap_to_data
|
from calibre.gui2 import ResizableDialog, error_dialog, gprefs, pixmap_to_data
|
||||||
|
@ -47,7 +47,7 @@ class StorePlugin(object): # {{{
|
|||||||
|
|
||||||
def __init__(self, gui, name):
|
def __init__(self, gui, name):
|
||||||
from calibre.gui2 import JSONConfig
|
from calibre.gui2 import JSONConfig
|
||||||
|
|
||||||
self.gui = gui
|
self.gui = gui
|
||||||
self.name = name
|
self.name = name
|
||||||
self.base_plugin = None
|
self.base_plugin = None
|
||||||
@ -79,14 +79,14 @@ class StorePlugin(object): # {{{
|
|||||||
return items as a generator.
|
return items as a generator.
|
||||||
|
|
||||||
Don't be lazy with the search! Load as much data as possible in the
|
Don't be lazy with the search! Load as much data as possible in the
|
||||||
:class:`calibre.gui2.store.search_result.SearchResult` object.
|
:class:`calibre.gui2.store.search_result.SearchResult` object.
|
||||||
However, if data (such as cover_url)
|
However, if data (such as cover_url)
|
||||||
isn't available because the store does not display cover images then it's okay to
|
isn't available because the store does not display cover images then it's okay to
|
||||||
ignore it.
|
ignore it.
|
||||||
|
|
||||||
At the very least a :class:`calibre.gui2.store.search_result.SearchResult`
|
At the very least a :class:`calibre.gui2.store.search_result.SearchResult`
|
||||||
returned by this function must have the title, author and id.
|
returned by this function must have the title, author and id.
|
||||||
|
|
||||||
If you have to parse multiple pages to get all of the data then implement
|
If you have to parse multiple pages to get all of the data then implement
|
||||||
:meth:`get_deatils` for retrieving additional information.
|
:meth:`get_deatils` for retrieving additional information.
|
||||||
|
|
||||||
@ -105,24 +105,24 @@ class StorePlugin(object): # {{{
|
|||||||
item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store.
|
item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store.
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def get_details(self, search_result, timeout=60):
|
def get_details(self, search_result, timeout=60):
|
||||||
'''
|
'''
|
||||||
Delayed search for information about specific search items.
|
Delayed search for information about specific search items.
|
||||||
|
|
||||||
Typically, this will be used when certain information such as
|
Typically, this will be used when certain information such as
|
||||||
formats, drm status, cover url are not part of the main search
|
formats, drm status, cover url are not part of the main search
|
||||||
results and the information is on another web page.
|
results and the information is on another web page.
|
||||||
|
|
||||||
Using this function allows for the main information (title, author)
|
Using this function allows for the main information (title, author)
|
||||||
to be displayed in the search results while other information can
|
to be displayed in the search results while other information can
|
||||||
take extra time to load. Splitting retrieving data that takes longer
|
take extra time to load. Splitting retrieving data that takes longer
|
||||||
to load into a separate function will give the illusion of the search
|
to load into a separate function will give the illusion of the search
|
||||||
being faster.
|
being faster.
|
||||||
|
|
||||||
:param search_result: A search result that need details set.
|
:param search_result: A search result that need details set.
|
||||||
:param timeout: The maximum amount of time in seconds to spend downloading details.
|
:param timeout: The maximum amount of time in seconds to spend downloading details.
|
||||||
|
|
||||||
:return: True if the search_result was modified otherwise False
|
:return: True if the search_result was modified otherwise False
|
||||||
'''
|
'''
|
||||||
return False
|
return False
|
||||||
@ -133,30 +133,30 @@ class StorePlugin(object): # {{{
|
|||||||
is called to update the caches. It is recommended to call this function
|
is called to update the caches. It is recommended to call this function
|
||||||
from :meth:`open`. Especially if :meth:`open` does anything other than
|
from :meth:`open`. Especially if :meth:`open` does anything other than
|
||||||
open a web page.
|
open a web page.
|
||||||
|
|
||||||
This function can be called at any time. It is up to the plugin to determine
|
This function can be called at any time. It is up to the plugin to determine
|
||||||
if the cache really does need updating. Unless :param:`force` is True, then
|
if the cache really does need updating. Unless :param:`force` is True, then
|
||||||
the plugin must update the cache. The only time force should be True is if
|
the plugin must update the cache. The only time force should be True is if
|
||||||
this function is called by the plugin's configuration dialog.
|
this function is called by the plugin's configuration dialog.
|
||||||
|
|
||||||
if :param:`suppress_progress` is False it is safe to assume that this function
|
if :param:`suppress_progress` is False it is safe to assume that this function
|
||||||
is being called from the main GUI thread so it is safe and recommended to use
|
is being called from the main GUI thread so it is safe and recommended to use
|
||||||
a QProgressDialog to display what is happening and allow the user to cancel
|
a QProgressDialog to display what is happening and allow the user to cancel
|
||||||
the operation. if :param:`suppress_progress` is True then run the update
|
the operation. if :param:`suppress_progress` is True then run the update
|
||||||
silently. In this case there is no guarantee what thread is calling this
|
silently. In this case there is no guarantee what thread is calling this
|
||||||
function so no Qt related functionality that requires being run in the main
|
function so no Qt related functionality that requires being run in the main
|
||||||
GUI thread should be run. E.G. Open a QProgressDialog.
|
GUI thread should be run. E.G. Open a QProgressDialog.
|
||||||
|
|
||||||
:param parent: The parent object to be used by an GUI dialogs.
|
:param parent: The parent object to be used by an GUI dialogs.
|
||||||
|
|
||||||
:param timeout: The maximum amount of time that should be spent in
|
:param timeout: The maximum amount of time that should be spent in
|
||||||
any given network connection.
|
any given network connection.
|
||||||
|
|
||||||
:param force: Force updating the cache even if the plugin has determined
|
:param force: Force updating the cache even if the plugin has determined
|
||||||
it is not necessary.
|
it is not necessary.
|
||||||
|
|
||||||
:param suppress_progress: Should a progress indicator be shown.
|
:param suppress_progress: Should a progress indicator be shown.
|
||||||
|
|
||||||
:return: True if the cache was updated, False otherwise.
|
:return: True if the cache was updated, False otherwise.
|
||||||
'''
|
'''
|
||||||
return False
|
return False
|
||||||
|
@ -33,7 +33,7 @@ from calibre import isbytestring
|
|||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename
|
||||||
from calibre.utils.date import utcnow, now as nowf, utcfromtimestamp
|
from calibre.utils.date import utcnow, now as nowf, utcfromtimestamp
|
||||||
from calibre.utils.config import prefs, tweaks, from_json, to_json
|
from calibre.utils.config import prefs, tweaks, from_json, to_json
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key, strcmp
|
||||||
from calibre.utils.search_query_parser import saved_searches, set_saved_searches
|
from calibre.utils.search_query_parser import saved_searches, set_saved_searches
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
|
from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
|
||||||
from calibre.utils.magick.draw import save_cover_data_to
|
from calibre.utils.magick.draw import save_cover_data_to
|
||||||
@ -1920,6 +1920,18 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
result.append(r)
|
result.append(r)
|
||||||
return ' & '.join(result).replace('|', ',')
|
return ' & '.join(result).replace('|', ',')
|
||||||
|
|
||||||
|
def _update_author_in_cache(self, id_, ss, final_authors):
|
||||||
|
self.conn.execute('UPDATE books SET author_sort=? WHERE id=?', (ss, id_))
|
||||||
|
self.data.set(id_, self.FIELD_MAP['authors'],
|
||||||
|
','.join([a.replace(',', '|') for a in final_authors]),
|
||||||
|
row_is_id=True)
|
||||||
|
self.data.set(id_, self.FIELD_MAP['author_sort'], ss, row_is_id=True)
|
||||||
|
|
||||||
|
aum = self.authors_with_sort_strings(id_, index_is_id=True)
|
||||||
|
self.data.set(id_, self.FIELD_MAP['au_map'],
|
||||||
|
':#:'.join([':::'.join((au.replace(',', '|'), aus)) for (au, aus) in aum]),
|
||||||
|
row_is_id=True)
|
||||||
|
|
||||||
def _set_authors(self, id, authors, allow_case_change=False):
|
def _set_authors(self, id, authors, allow_case_change=False):
|
||||||
if not authors:
|
if not authors:
|
||||||
authors = [_('Unknown')]
|
authors = [_('Unknown')]
|
||||||
@ -1933,14 +1945,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
a = a.strip().replace(',', '|')
|
a = a.strip().replace(',', '|')
|
||||||
if not isinstance(a, unicode):
|
if not isinstance(a, unicode):
|
||||||
a = a.decode(preferred_encoding, 'replace')
|
a = a.decode(preferred_encoding, 'replace')
|
||||||
aus = self.conn.get('SELECT id, name FROM authors WHERE name=?', (a,))
|
aus = self.conn.get('SELECT id, name, sort FROM authors WHERE name=?', (a,))
|
||||||
if aus:
|
if aus:
|
||||||
aid, name = aus[0]
|
aid, name, sort = aus[0]
|
||||||
# Handle change of case
|
# Handle change of case
|
||||||
if name != a:
|
if name != a:
|
||||||
if allow_case_change:
|
if allow_case_change:
|
||||||
self.conn.execute('''UPDATE authors
|
ns = author_to_author_sort(a.replace('|', ','))
|
||||||
SET name=? WHERE id=?''', (a, aid))
|
if strcmp(sort, ns) == 0:
|
||||||
|
sort = ns
|
||||||
|
self.conn.execute('''UPDATE authors SET name=?, sort=?
|
||||||
|
WHERE id=?''', (a, sort, aid))
|
||||||
case_change = True
|
case_change = True
|
||||||
else:
|
else:
|
||||||
a = name
|
a = name
|
||||||
@ -1957,17 +1972,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
bks = self.conn.get('''SELECT book FROM books_authors_link
|
bks = self.conn.get('''SELECT book FROM books_authors_link
|
||||||
WHERE author=?''', (aid,))
|
WHERE author=?''', (aid,))
|
||||||
books_to_refresh |= set([bk[0] for bk in bks])
|
books_to_refresh |= set([bk[0] for bk in bks])
|
||||||
|
for bk in books_to_refresh:
|
||||||
|
ss = self.author_sort_from_book(id, index_is_id=True)
|
||||||
|
aus = self.author_sort(bk, index_is_id=True)
|
||||||
|
if strcmp(aus, ss) == 0:
|
||||||
|
self._update_author_in_cache(bk, ss, final_authors)
|
||||||
|
# This can repeat what was done above in rare cases. Let it.
|
||||||
ss = self.author_sort_from_book(id, index_is_id=True)
|
ss = self.author_sort_from_book(id, index_is_id=True)
|
||||||
self.conn.execute('UPDATE books SET author_sort=? WHERE id=?',
|
self._update_author_in_cache(id, ss, final_authors)
|
||||||
(ss, id))
|
|
||||||
self.data.set(id, self.FIELD_MAP['authors'],
|
|
||||||
','.join([a.replace(',', '|') for a in final_authors]),
|
|
||||||
row_is_id=True)
|
|
||||||
self.data.set(id, self.FIELD_MAP['author_sort'], ss, row_is_id=True)
|
|
||||||
aum = self.authors_with_sort_strings(id, index_is_id=True)
|
|
||||||
self.data.set(id, self.FIELD_MAP['au_map'],
|
|
||||||
':#:'.join([':::'.join((au.replace(',', '|'), aus)) for (au, aus) in aum]),
|
|
||||||
row_is_id=True)
|
|
||||||
return books_to_refresh
|
return books_to_refresh
|
||||||
|
|
||||||
def set_authors(self, id, authors, notify=True, commit=True,
|
def set_authors(self, id, authors, notify=True, commit=True,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user