mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Initial implementation of add_format()
This commit is contained in:
parent
f1ce0eb75c
commit
4bba2cbf92
@ -1059,6 +1059,24 @@ class DB(object):
|
|||||||
if wam is not None:
|
if wam is not None:
|
||||||
wam.close_handles()
|
wam.close_handles()
|
||||||
|
|
||||||
|
def add_format(self, book_id, fmt, stream, title, author, path):
|
||||||
|
fname = self.construct_file_name(book_id, title, author)
|
||||||
|
path = os.path.join(self.library_path, path)
|
||||||
|
fmt = ('.' + fmt.lower()) if fmt else ''
|
||||||
|
dest = os.path.join(path, fname + fmt)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
size = 0
|
||||||
|
|
||||||
|
if (not getattr(stream, 'name', False) or not samefile(dest, stream.name)):
|
||||||
|
with lopen(dest, 'wb') as f:
|
||||||
|
shutil.copyfileobj(stream, f)
|
||||||
|
size = f.tell()
|
||||||
|
elif os.path.exists(dest):
|
||||||
|
size = os.path.getsize(dest)
|
||||||
|
|
||||||
|
return size, fname
|
||||||
|
|
||||||
def update_path(self, book_id, title, author, path_field, formats_field):
|
def update_path(self, book_id, title, author, path_field, formats_field):
|
||||||
path = self.construct_path_name(book_id, title, author)
|
path = self.construct_path_name(book_id, title, author)
|
||||||
current_path = path_field.for_book(book_id)
|
current_path = path_field.for_book(book_id)
|
||||||
|
@ -7,12 +7,13 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, traceback, random
|
import os, traceback, random, shutil
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import wraps, partial
|
from functools import wraps, partial
|
||||||
|
|
||||||
from calibre.constants import iswindows
|
from calibre.constants import iswindows
|
||||||
|
from calibre.customize.ui import run_plugins_on_import, run_plugins_on_postimport
|
||||||
from calibre.db import SPOOL_SIZE
|
from calibre.db import SPOOL_SIZE
|
||||||
from calibre.db.categories import get_categories
|
from calibre.db.categories import get_categories
|
||||||
from calibre.db.locking import create_locks
|
from calibre.db.locking import create_locks
|
||||||
@ -22,6 +23,7 @@ from calibre.db.search import Search
|
|||||||
from calibre.db.tables import VirtualTable
|
from calibre.db.tables import VirtualTable
|
||||||
from calibre.db.write import get_series_values
|
from calibre.db.write import get_series_values
|
||||||
from calibre.db.lazy import FormatMetadata, FormatsList
|
from calibre.db.lazy import FormatMetadata, FormatsList
|
||||||
|
from calibre.ebooks import check_ebook_format
|
||||||
from calibre.ebooks.metadata import string_to_authors
|
from calibre.ebooks.metadata import string_to_authors
|
||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||||
@ -51,6 +53,18 @@ def wrap_simple(lock, func):
|
|||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
def run_import_plugins(path_or_stream, fmt):
|
||||||
|
fmt = fmt.lower()
|
||||||
|
if hasattr(path_or_stream, 'seek'):
|
||||||
|
path_or_stream.seek(0)
|
||||||
|
pt = PersistentTemporaryFile('_import_plugin.'+fmt)
|
||||||
|
shutil.copyfileobj(path_or_stream, pt, 1024**2)
|
||||||
|
pt.close()
|
||||||
|
path = pt.name
|
||||||
|
else:
|
||||||
|
path = path_or_stream
|
||||||
|
return run_plugins_on_import(path, fmt)
|
||||||
|
|
||||||
|
|
||||||
class Cache(object):
|
class Cache(object):
|
||||||
|
|
||||||
@ -943,6 +957,43 @@ class Cache(object):
|
|||||||
if extra is not None or force_changes:
|
if extra is not None or force_changes:
|
||||||
protected_set_field(idx, extra)
|
protected_set_field(idx, extra)
|
||||||
|
|
||||||
|
@write_api
|
||||||
|
def add_format(self, book_id, fmt, stream_or_path, replace=True, run_hooks=True, dbapi=None):
|
||||||
|
if run_hooks:
|
||||||
|
# Run import plugins
|
||||||
|
npath = run_import_plugins(stream_or_path, fmt)
|
||||||
|
fmt = os.path.splitext(npath)[-1].lower().replace('.', '').upper()
|
||||||
|
stream_or_path = lopen(npath, 'rb')
|
||||||
|
fmt = check_ebook_format(stream_or_path, fmt)
|
||||||
|
|
||||||
|
fmt = (fmt or '').upper()
|
||||||
|
self.format_metadata_cache[book_id].pop(fmt, None)
|
||||||
|
try:
|
||||||
|
name = self.fields['formats'].format_fname(book_id, fmt)
|
||||||
|
except:
|
||||||
|
name = None
|
||||||
|
|
||||||
|
if name and not replace:
|
||||||
|
return False
|
||||||
|
|
||||||
|
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||||
|
title = self._field_for('title', book_id, default_value=_('Unknown'))
|
||||||
|
author = self._field_for('authors', book_id, default_value=(_('Unknown'),))[0]
|
||||||
|
stream = stream_or_path if hasattr(stream_or_path, 'read') else lopen(stream_or_path, 'rb')
|
||||||
|
size, fname = self.backend.add_format(book_id, fmt, stream, title, author, path)
|
||||||
|
del stream
|
||||||
|
|
||||||
|
max_size = self.fields['formats'].table.update_fmt(book_id, fmt, fname, size, self.backend)
|
||||||
|
self.fields['size'].table.update_size(book_id, max_size)
|
||||||
|
self._update_last_modified((book_id,))
|
||||||
|
|
||||||
|
if run_hooks:
|
||||||
|
# Run post import plugins
|
||||||
|
run_plugins_on_postimport(dbapi or self, book_id, fmt)
|
||||||
|
stream_or_path.close()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class SortKey(object): # {{{
|
class SortKey(object): # {{{
|
||||||
@ -959,3 +1010,4 @@ class SortKey(object): # {{{
|
|||||||
return 0
|
return 0
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from dateutil.tz import tzoffset
|
from dateutil.tz import tzoffset
|
||||||
|
|
||||||
@ -98,6 +99,9 @@ class SizeTable(OneToOneTable):
|
|||||||
'WHERE data.book=books.id) FROM books'):
|
'WHERE data.book=books.id) FROM books'):
|
||||||
self.book_col_map[row[0]] = self.unserialize(row[1])
|
self.book_col_map[row[0]] = self.unserialize(row[1])
|
||||||
|
|
||||||
|
def update_size(self, book_id, size):
|
||||||
|
self.book_col_map[book_id] = size
|
||||||
|
|
||||||
class UUIDTable(OneToOneTable):
|
class UUIDTable(OneToOneTable):
|
||||||
|
|
||||||
def read(self, db):
|
def read(self, db):
|
||||||
@ -194,8 +198,9 @@ class FormatsTable(ManyToManyTable):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def read_maps(self, db):
|
def read_maps(self, db):
|
||||||
self.fname_map = {}
|
self.fname_map = defaultdict(dict)
|
||||||
for row in db.conn.execute('SELECT book, format, name FROM data'):
|
self.size_map = defaultdict(dict)
|
||||||
|
for row in db.conn.execute('SELECT book, format, name, uncompressed_size FROM data'):
|
||||||
if row[1] is not None:
|
if row[1] is not None:
|
||||||
fmt = row[1].upper()
|
fmt = row[1].upper()
|
||||||
if fmt not in self.col_book_map:
|
if fmt not in self.col_book_map:
|
||||||
@ -204,9 +209,8 @@ class FormatsTable(ManyToManyTable):
|
|||||||
if row[0] not in self.book_col_map:
|
if row[0] not in self.book_col_map:
|
||||||
self.book_col_map[row[0]] = []
|
self.book_col_map[row[0]] = []
|
||||||
self.book_col_map[row[0]].append(fmt)
|
self.book_col_map[row[0]].append(fmt)
|
||||||
if row[0] not in self.fname_map:
|
|
||||||
self.fname_map[row[0]] = {}
|
|
||||||
self.fname_map[row[0]][fmt] = row[2]
|
self.fname_map[row[0]][fmt] = row[2]
|
||||||
|
self.size_map[row[0]][fmt] = row[3]
|
||||||
|
|
||||||
for key in tuple(self.book_col_map.iterkeys()):
|
for key in tuple(self.book_col_map.iterkeys()):
|
||||||
self.book_col_map[key] = tuple(sorted(self.book_col_map[key]))
|
self.book_col_map[key] = tuple(sorted(self.book_col_map[key]))
|
||||||
@ -216,6 +220,13 @@ class FormatsTable(ManyToManyTable):
|
|||||||
db.conn.execute('UPDATE data SET name=? WHERE book=? AND format=?',
|
db.conn.execute('UPDATE data SET name=? WHERE book=? AND format=?',
|
||||||
(fname, book_id, fmt))
|
(fname, book_id, fmt))
|
||||||
|
|
||||||
|
def update_fmt(self, book_id, fmt, fname, size, db):
|
||||||
|
self.fname_map[book_id][fmt] = fname
|
||||||
|
self.size_map[book_id][fmt] = size
|
||||||
|
db.conn.execute('INSERT OR REPLACE INTO data (book,format,uncompressed_size,name) VALUES (?,?,?,?)',
|
||||||
|
(book_id, fmt, size, fname))
|
||||||
|
return max(self.size_map[book_id].itervalues())
|
||||||
|
|
||||||
class IdentifiersTable(ManyToManyTable):
|
class IdentifiersTable(ManyToManyTable):
|
||||||
|
|
||||||
def read_id_maps(self, db):
|
def read_id_maps(self, db):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user