mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Plugin to read metadata from RAR files
This commit is contained in:
parent
3363f70602
commit
6056fa4da1
@ -189,6 +189,17 @@ class ZipMetadataReader(MetadataReaderPlugin):
|
||||
from calibre.ebooks.metadata.zip import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
class RARMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read RAR metadata'
|
||||
file_types = set(['rar'])
|
||||
description = _('Read metadata from ebooks in RAR archives')
|
||||
|
||||
def get_metadata(self, stream, ftype):
|
||||
from calibre.ebooks.metadata.rar import get_metadata
|
||||
return get_metadata(stream)
|
||||
|
||||
|
||||
class EPUBMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set EPUB metadata'
|
||||
|
36
src/calibre/ebooks/metadata/rar.py
Normal file
36
src/calibre/ebooks/metadata/rar.py
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Read metadata from RAR archives
|
||||
'''
|
||||
|
||||
import os
|
||||
from cStringIO import StringIO
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.libunrar import extract_member, names
|
||||
|
||||
def get_metadata(stream):
|
||||
path = getattr(stream, 'name', False)
|
||||
if not path:
|
||||
pt = PersistentTemporaryFile('_rar-meta.rar')
|
||||
pt.write(stream.read())
|
||||
pt.close()
|
||||
path = pt.name
|
||||
path = os.path.abspath(path)
|
||||
file_names = list(names(path))
|
||||
for f in file_names:
|
||||
stream_type = os.path.splitext(f)[1].lower()
|
||||
if stream_type:
|
||||
stream_type = stream_type[1:]
|
||||
if stream_type in ('lit', 'opf', 'prc', 'mobi', 'fb2', 'epub',
|
||||
'rb', 'imp', 'pdf', 'lrf'):
|
||||
data = extract_member(path, match=None, name=f)[1]
|
||||
stream = StringIO(data)
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
return get_metadata(stream, stream_type)
|
||||
raise ValueError('No ebook found in RAR archive')
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>581</height>
|
||||
<width>755</width>
|
||||
<height>557</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
@ -328,8 +328,8 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page" >
|
||||
<layout class="QGridLayout" name="gridLayout_3" >
|
||||
<item row="0" column="0" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" >
|
||||
<item>
|
||||
<widget class="QCheckBox" name="roman_numerals" >
|
||||
<property name="text" >
|
||||
<string>Use &Roman numerals for series number</string>
|
||||
@ -339,12 +339,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<item>
|
||||
<widget class="QCheckBox" name="systray_icon" >
|
||||
<property name="text" >
|
||||
<string>Enable system &tray icon (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="systray_notifications" >
|
||||
<property name="text" >
|
||||
<string>Show &notifications in system tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="separate_cover_flow" >
|
||||
<property name="text" >
|
||||
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="sync_news" >
|
||||
<property name="text" >
|
||||
<string>Automatically send downloaded &news to ebook reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="delete_news" >
|
||||
<property name="text" >
|
||||
<string>&Delete news from library when it is sent to reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6" >
|
||||
<property name="text" >
|
||||
<string>&Number of covers to show in browse mode (after restart):</string>
|
||||
<string>&Number of covers to show in browse mode (needs restart):</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>cover_browse</cstring>
|
||||
@ -356,7 +391,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0" >
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2" >
|
||||
<property name="title" >
|
||||
<string>Toolbar</string>
|
||||
@ -402,125 +437,112 @@
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>toolbar_button_size</zorder>
|
||||
<zorder>label_4</zorder>
|
||||
<zorder>show_toolbar_text</zorder>
|
||||
<zorder>columns</zorder>
|
||||
<zorder></zorder>
|
||||
<zorder>groupBox_3</zorder>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" >
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Select visible &columns in library view</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7" >
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Select visible &columns in library view</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7" >
|
||||
<item>
|
||||
<widget class="QListWidget" name="columns" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionBehavior" >
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" >
|
||||
<item>
|
||||
<widget class="QToolButton" name="column_up" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
<widget class="QListWidget" name="columns" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||
<property name="selectionBehavior" >
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="column_down" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<item>
|
||||
<widget class="QToolButton" name="column_up" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="column_down" >
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3" >
|
||||
<property name="title" >
|
||||
<string>Use internal &viewer for the following formats:</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QListWidget" name="viewer" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QCheckBox" name="systray_icon" >
|
||||
<property name="text" >
|
||||
<string>Enable system &tray icon (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" >
|
||||
<widget class="QCheckBox" name="sync_news" >
|
||||
<property name="text" >
|
||||
<string>Automatically send downloaded &news to ebook reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" >
|
||||
<widget class="QCheckBox" name="delete_news" >
|
||||
<property name="text" >
|
||||
<string>&Delete news from library when it is sent to reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" >
|
||||
<widget class="QCheckBox" name="separate_cover_flow" >
|
||||
<property name="text" >
|
||||
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QCheckBox" name="systray_notifications" >
|
||||
<property name="text" >
|
||||
<string>Show &notifications in system tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
<zorder>columns</zorder>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3" >
|
||||
<property name="title" >
|
||||
<string>Use internal &viewer for:</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QListWidget" name="viewer" >
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>roman_numerals</zorder>
|
||||
<zorder>groupBox_2</zorder>
|
||||
<zorder>groupBox</zorder>
|
||||
<zorder>systray_icon</zorder>
|
||||
<zorder>sync_news</zorder>
|
||||
<zorder>delete_news</zorder>
|
||||
<zorder>separate_cover_flow</zorder>
|
||||
<zorder>systray_notifications</zorder>
|
||||
<zorder>groupBox_3</zorder>
|
||||
<zorder></zorder>
|
||||
<zorder></zorder>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2" >
|
||||
<layout class="QVBoxLayout" >
|
||||
|
@ -188,8 +188,31 @@ def extract(path, dir):
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
_libunrar.RARCloseArchive(arc_data)
|
||||
|
||||
def extract_member(path, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$', re.I)):
|
||||
|
||||
def names(path):
|
||||
if hasattr(path, 'read'):
|
||||
data = path.read()
|
||||
f = NamedTemporaryFile(suffix='.rar')
|
||||
f.write(data)
|
||||
f.flush()
|
||||
path = f.name
|
||||
open_archive_data = RAROpenArchiveDataEx(ArcName=path, OpenMode=RAR_OM_LIST, CmtBuf=None)
|
||||
arc_data = _libunrar.RAROpenArchiveEx(byref(open_archive_data))
|
||||
try:
|
||||
if open_archive_data.OpenResult != 0:
|
||||
raise UnRARException(_interpret_open_error(open_archive_data.OpenResult, path))
|
||||
header_data = RARHeaderDataEx(CmtBuf=None)
|
||||
while True:
|
||||
if _libunrar.RARReadHeaderEx(arc_data, byref(header_data)) != 0:
|
||||
break
|
||||
PFCode = _libunrar.RARProcessFileW(arc_data, RAR_SKIP, None, None)
|
||||
if PFCode != 0:
|
||||
raise UnRARException(_interpret_process_file_error(PFCode))
|
||||
yield header_data.FileNameW
|
||||
finally:
|
||||
_libunrar.RARCloseArchive(arc_data)
|
||||
|
||||
def extract_member(path, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$', re.I), name=None):
|
||||
if hasattr(path, 'read'):
|
||||
data = path.read()
|
||||
f = NamedTemporaryFile(suffix='.rar')
|
||||
@ -210,7 +233,9 @@ def extract_member(path, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$', re.I)):
|
||||
PFCode = _libunrar.RARProcessFileW(arc_data, RAR_EXTRACT, None, None)
|
||||
if PFCode != 0:
|
||||
raise UnRARException(_interpret_process_file_error(PFCode))
|
||||
if match.search(header_data.FileNameW):
|
||||
file_name = header_data.FileNameW
|
||||
if (name is not None and file_name == name) or \
|
||||
(match is not None and match.search(file_name)):
|
||||
return header_data.FileNameW.replace('/', os.sep), \
|
||||
open(os.path.join(dir, *header_data.FileNameW.split('/')), 'rb').read()
|
||||
finally:
|
||||
|
Loading…
x
Reference in New Issue
Block a user