Plugin to read metadata from RAR files

This commit is contained in:
Kovid Goyal 2009-01-23 14:08:39 -08:00
parent 3363f70602
commit 6056fa4da1
4 changed files with 208 additions and 114 deletions

View File

@ -189,6 +189,17 @@ class ZipMetadataReader(MetadataReaderPlugin):
from calibre.ebooks.metadata.zip import get_metadata from calibre.ebooks.metadata.zip import get_metadata
return get_metadata(stream) 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): class EPUBMetadataWriter(MetadataWriterPlugin):
name = 'Set EPUB metadata' name = 'Set EPUB metadata'

View 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')

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>800</width> <width>755</width>
<height>581</height> <height>557</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle" >
@ -328,8 +328,8 @@
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="page" > <widget class="QWidget" name="page" >
<layout class="QGridLayout" name="gridLayout_3" > <layout class="QVBoxLayout" name="verticalLayout_4" >
<item row="0" column="0" > <item>
<widget class="QCheckBox" name="roman_numerals" > <widget class="QCheckBox" name="roman_numerals" >
<property name="text" > <property name="text" >
<string>Use &amp;Roman numerals for series number</string> <string>Use &amp;Roman numerals for series number</string>
@ -339,12 +339,47 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0" > <item>
<widget class="QCheckBox" name="systray_icon" >
<property name="text" >
<string>Enable system &amp;tray icon (needs restart)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="systray_notifications" >
<property name="text" >
<string>Show &amp;notifications in system tray</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="separate_cover_flow" >
<property name="text" >
<string>Show cover &amp;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 &amp;news to ebook reader</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="delete_news" >
<property name="text" >
<string>&amp;Delete news from library when it is sent to reader</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" > <layout class="QHBoxLayout" name="horizontalLayout" >
<item> <item>
<widget class="QLabel" name="label_6" > <widget class="QLabel" name="label_6" >
<property name="text" > <property name="text" >
<string>&amp;Number of covers to show in browse mode (after restart):</string> <string>&amp;Number of covers to show in browse mode (needs restart):</string>
</property> </property>
<property name="buddy" > <property name="buddy" >
<cstring>cover_browse</cstring> <cstring>cover_browse</cstring>
@ -356,7 +391,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="8" column="0" > <item>
<widget class="QGroupBox" name="groupBox_2" > <widget class="QGroupBox" name="groupBox_2" >
<property name="title" > <property name="title" >
<string>Toolbar</string> <string>Toolbar</string>
@ -402,14 +437,22 @@
</widget> </widget>
</item> </item>
</layout> </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> </widget>
</item> </item>
<item row="9" column="0" > <item>
<layout class="QHBoxLayout" name="horizontalLayout_7" >
<item>
<widget class="QGroupBox" name="groupBox" > <widget class="QGroupBox" name="groupBox" >
<property name="title" > <property name="title" >
<string>Select visible &amp;columns in library view</string> <string>Select visible &amp;columns in library view</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4" > <layout class="QVBoxLayout" name="verticalLayout_7" >
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_3" > <layout class="QHBoxLayout" name="horizontalLayout_3" >
<item> <item>
@ -463,10 +506,14 @@
</item> </item>
</layout> </layout>
</item> </item>
</layout>
<zorder>columns</zorder>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="groupBox_3" > <widget class="QGroupBox" name="groupBox_3" >
<property name="title" > <property name="title" >
<string>Use internal &amp;viewer for the following formats:</string> <string>Use internal &amp;viewer for:</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4" > <layout class="QGridLayout" name="gridLayout_4" >
<item row="0" column="0" > <item row="0" column="0" >
@ -483,44 +530,19 @@
</widget> </widget>
</item> </item>
</layout> </layout>
</widget>
</item>
<item row="1" column="0" >
<widget class="QCheckBox" name="systray_icon" >
<property name="text" >
<string>Enable system &amp;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 &amp;news to ebook reader</string>
</property>
</widget>
</item>
<item row="6" column="0" >
<widget class="QCheckBox" name="delete_news" >
<property name="text" >
<string>&amp;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 &amp;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 &amp;notifications in system tray</string>
</property>
</widget>
</item> </item>
</layout> </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>
<widget class="QWidget" name="page_2" > <widget class="QWidget" name="page_2" >
<layout class="QVBoxLayout" > <layout class="QVBoxLayout" >

View File

@ -189,7 +189,30 @@ def extract(path, dir):
os.chdir(cwd) os.chdir(cwd)
_libunrar.RARCloseArchive(arc_data) _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'): if hasattr(path, 'read'):
data = path.read() data = path.read()
f = NamedTemporaryFile(suffix='.rar') 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) PFCode = _libunrar.RARProcessFileW(arc_data, RAR_EXTRACT, None, None)
if PFCode != 0: if PFCode != 0:
raise UnRARException(_interpret_process_file_error(PFCode)) 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), \ return header_data.FileNameW.replace('/', os.sep), \
open(os.path.join(dir, *header_data.FileNameW.split('/')), 'rb').read() open(os.path.join(dir, *header_data.FileNameW.split('/')), 'rb').read()
finally: finally: