Pull from driver-dev

This commit is contained in:
Kovid Goyal 2009-05-02 11:13:22 -07:00
commit 8be2541738
19 changed files with 402 additions and 25 deletions

View File

@ -292,7 +292,8 @@ from calibre.ebooks.oeb.output import OEBOutput
from calibre.ebooks.epub.output import EPUBOutput from calibre.ebooks.epub.output import EPUBOutput
from calibre.ebooks.txt.output import TXTOutput from calibre.ebooks.txt.output import TXTOutput
from calibre.ebooks.pdf.output import PDFOutput from calibre.ebooks.pdf.output import PDFOutput
from calibre.ebooks.pdb.ereader.output import EREADEROutput from calibre.ebooks.pml.input import PMLInput
from calibre.ebooks.pml.output import PMLOutput
from calibre.customize.profiles import input_profiles, output_profiles from calibre.customize.profiles import input_profiles, output_profiles
from calibre.devices.prs500.driver import PRS500 from calibre.devices.prs500.driver import PRS500
@ -303,11 +304,14 @@ from calibre.devices.kindle.driver import KINDLE
from calibre.devices.kindle.driver import KINDLE2 from calibre.devices.kindle.driver import KINDLE2
from calibre.devices.blackberry.driver import BLACKBERRY from calibre.devices.blackberry.driver import BLACKBERRY
from calibre.devices.eb600.driver import EB600 from calibre.devices.eb600.driver import EB600
from calibre.devices.jetbook.driver import JETBOOK
plugins = [HTML2ZIP, EPUBInput, MOBIInput, PDBInput, PDFInput, HTMLInput, plugins = [HTML2ZIP, EPUBInput, MOBIInput, PDBInput, PDFInput, HTMLInput,
TXTInput, OEBOutput, TXTOutput, PDFOutput, LITInput, ComicInput, TXTInput, OEBOutput, TXTOutput, PDFOutput, LITInput, ComicInput,
FB2Input, ODTInput, RTFInput, EPUBOutput, EREADEROutput, RecipeInput] FB2Input, ODTInput, RTFInput, EPUBOutput, RecipeInput, PMLInput,
plugins += [PRS505, PRS700, CYBOOKG3, KINDLE, KINDLE2, BLACKBERRY, EB600] PMLOutput]
plugins += [PRS505, PRS700, CYBOOKG3, KINDLE, KINDLE2, BLACKBERRY, EB600, \
JETBOOK]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
x.__name__.endswith('MetadataReader')] x.__name__.endswith('MetadataReader')]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ plugins += [x for x in list(locals().values()) if isinstance(x, type) and \

View File

@ -212,6 +212,30 @@ class DevicePlugin(Plugin):
''' '''
raise NotImplementedError() raise NotImplementedError()
@classmethod
def config_widget(cls):
'''
Should return a QWidget. The QWidget contains the settings for the device interface
'''
raise NotImplementedError()
@classmethod
def save_settings(cls, settings_widget):
'''
Should save settings to disk. Takes the widget created in config_widget
and saves all settings to disk.
'''
raise NotImplementedError()
@classmethod
def settings(cls):
'''
Should return an opts object. The opts object should have one attribute
`formats` whihc is an ordered list of formats for the device.
'''
raise NotImplementedError()
class BookList(list): class BookList(list):

View File

@ -7,7 +7,7 @@ Device driver for Ectaco Jetbook firmware >= JL04_v030e
import os, re, sys, shutil import os, re, sys, shutil
from itertools import cycle from itertools import cycle
from calibre.devices.usbms.driver import USBMS, metadata_from_formats from calibre.devices.usbms.driver import USBMS
from calibre import sanitize_file_name as sanitize from calibre import sanitize_file_name as sanitize
class JETBOOK(USBMS): class JETBOOK(USBMS):
@ -99,6 +99,7 @@ class JETBOOK(USBMS):
return txt return txt
from calibre.devices.usbms.driver import metadata_from_formats
mi = metadata_from_formats([path]) mi = metadata_from_formats([path])
if (mi.title==_('Unknown') or mi.authors==[_('Unknown')]) \ if (mi.title==_('Unknown') or mi.authors==[_('Unknown')]) \

View File

@ -13,7 +13,7 @@ from calibre import __version__, iswindows, __appname__
from calibre.devices.errors import PathError from calibre.devices.errors import PathError
from calibre.utils.terminfo import TerminalController from calibre.utils.terminfo import TerminalController
from calibre.devices.errors import ArgumentError, DeviceError, DeviceLocked from calibre.devices.errors import ArgumentError, DeviceError, DeviceLocked
from calibre.devices import devices from calibre.customize.ui import device_plugins
from calibre.devices.scanner import DeviceScanner from calibre.devices.scanner import DeviceScanner
MINIMUM_COL_WIDTH = 12 #: Minimum width of columns in ls output MINIMUM_COL_WIDTH = 12 #: Minimum width of columns in ls output
@ -203,7 +203,7 @@ def main():
_wmi = wmi.WMI() _wmi = wmi.WMI()
scanner = DeviceScanner(_wmi) scanner = DeviceScanner(_wmi)
scanner.scan() scanner.scan()
for d in devices(): for d in device_plugins():
if scanner.is_device_connected(d): if scanner.is_device_connected(d):
dev = d(log_packets=options.log_packets) dev = d(log_packets=options.log_packets)

View File

@ -10,9 +10,10 @@ import os, subprocess, time, re
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import DeviceError from calibre.devices.errors import DeviceError
from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre import iswindows, islinux, isosx, __appname__ from calibre import iswindows, islinux, isosx, __appname__
class Device(DevicePlugin): class Device(DeviceConfig, DevicePlugin):
''' '''
This class provides logic common to all drivers for devices that export themselves This class provides logic common to all drivers for devices that export themselves
as USB Mass Storage devices. If you are writing such a driver, inherit from this as USB Mass Storage devices. If you are writing such a driver, inherit from this
@ -94,7 +95,7 @@ class Device(DevicePlugin):
for pid in cls.PRODUCT_ID: for pid in cls.PRODUCT_ID:
fdi_base_values = dict( fdi_base_values = dict(
app=__appname__, app=__appname__,
deviceclass=cls.__name__, deviceclass=cls.__class__.__name__,
vendor_id=hex(vid), vendor_id=hex(vid),
product_id=hex(pid), product_id=hex(pid),
main_memory=cls.MAIN_MEMORY_VOLUME_LABEL, main_memory=cls.MAIN_MEMORY_VOLUME_LABEL,

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
from calibre.utils.config import Config, ConfigProxy
class DeviceConfig(object):
HELP_MESSAGE = _('Ordered list of formats the device will accept')
@classmethod
def _config(cls):
c = Config('device_drivers_%s' % cls.__class__.__name__, _('settings for device drivers'))
c.add_opt('format_map', default=cls.FORMATS, help=cls.HELP_MESSAGE)
return c
def _configProxy(cls):
return ConfigProxy(cls._config())
@classmethod
def config_widget(cls):
from calibre.gui2.device_drivers.configwidget import ConfigWidget
cw = ConfigWidget(cls.configProxy(cls._config()), cls.FORMATS)
return cw
@classmethod
def save_settings(cls, config_widget):
cls.configProxy(cls._config())['format_map'] = config_widget.format_map()
@classmethod
def settings(cls):
return cls._config().parse()
def customization_help(cls, gui=False):
return cls.HELP_MESSAGE

View File

@ -251,19 +251,23 @@ OptionRecommendation(name='page_breaks_before',
OptionRecommendation(name='margin_top', OptionRecommendation(name='margin_top',
recommended_value=5.0, level=OptionRecommendation.LOW, recommended_value=5.0, level=OptionRecommendation.LOW,
help=_('Set the top margin in pts. Default is %default')), help=_('Set the top margin in pts. Default is %default. '
'Note: 72 pts equals 1 inch')),
OptionRecommendation(name='margin_bottom', OptionRecommendation(name='margin_bottom',
recommended_value=5.0, level=OptionRecommendation.LOW, recommended_value=5.0, level=OptionRecommendation.LOW,
help=_('Set the bottom margin in pts. Default is %default')), help=_('Set the bottom margin in pts. Default is %default. '
'Note: 72 pts equals 1 inch')),
OptionRecommendation(name='margin_left', OptionRecommendation(name='margin_left',
recommended_value=5.0, level=OptionRecommendation.LOW, recommended_value=5.0, level=OptionRecommendation.LOW,
help=_('Set the left margin in pts. Default is %default')), help=_('Set the left margin in pts. Default is %default. '
'Note: 72 pts equals 1 inch')),
OptionRecommendation(name='margin_right', OptionRecommendation(name='margin_right',
recommended_value=5.0, level=OptionRecommendation.LOW, recommended_value=5.0, level=OptionRecommendation.LOW,
help=_('Set the right margin in pts. Default is %default')), help=_('Set the right margin in pts. Default is %default. '
'Note: 72 pts equals 1 inch')),
OptionRecommendation(name='dont_justify', OptionRecommendation(name='dont_justify',
recommended_value=False, level=OptionRecommendation.LOW, recommended_value=False, level=OptionRecommendation.LOW,

View File

@ -33,7 +33,7 @@ from calibre.ebooks.lrf.html.table import Table
from calibre import filename_to_utf8, setup_cli_handlers, __appname__, \ from calibre import filename_to_utf8, setup_cli_handlers, __appname__, \
fit_image, preferred_encoding fit_image, preferred_encoding
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.devices.interface import Device from calibre.devices.interface import DevicePlugin as Device
from calibre.ebooks.lrf.html.color_map import lrs_color from calibre.ebooks.lrf.html.color_map import lrs_color
from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.chardet import xml_to_unicode

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__license__ = 'GPL 3' __license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>' __copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@ -15,7 +15,7 @@ from calibre.ebooks import DRMError
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.ebooks.pdb.formatreader import FormatReader from calibre.ebooks.pdb.formatreader import FormatReader
from calibre.ebooks.pdb.ereader import EreaderError from calibre.ebooks.pdb.ereader import EreaderError
from calibre.ebooks.pdb.ereader.pmlconverter import pml_to_html, \ from calibre.ebooks.pml.pmlconverter import pml_to_html, \
footnote_sidebar_to_html footnote_sidebar_to_html
from calibre.ebooks.mobi.palmdoc import decompress_doc from calibre.ebooks.mobi.palmdoc import decompress_doc
from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.opf2 import OPFCreator

View File

@ -11,7 +11,7 @@ import Image, cStringIO
from calibre.ebooks.oeb.base import OEB_IMAGES from calibre.ebooks.oeb.base import OEB_IMAGES
from calibre.ebooks.pdb.header import PdbHeaderBuilder from calibre.ebooks.pdb.header import PdbHeaderBuilder
from calibre.ebooks.pdb.ereader import image_name from calibre.ebooks.pdb.ereader import image_name
from calibre.ebooks.pdb.ereader.pmlconverter import html_to_pml from calibre.ebooks.pml.pmlconverter import html_to_pml
IDENTITY = 'PNPdPPrs' IDENTITY = 'PNPdPPrs'
@ -31,13 +31,14 @@ class Writer(object):
lengths = [len(i) for i in sections] lengths = [len(i) for i in sections]
pdbHeaderBuilder = PdbHeaderBuilder(IDENTITY, 'test book') pdbHeaderBuilder = PdbHeaderBuilder(IDENTITY, '')
pdbHeaderBuilder.build_header(lengths, out_stream) pdbHeaderBuilder.build_header(lengths, out_stream)
for item in sections: for item in sections:
out_stream.write(item) out_stream.write(item)
def _text(self, pages): def _text(self, pages):
# Todo: Split pages over 65505 Bytes
pml_pages = [] pml_pages = []
for page in pages: for page in pages:
@ -46,6 +47,7 @@ class Writer(object):
return pml_pages return pml_pages
def _images(self, manifest): def _images(self, manifest):
# Todo: resize images over 65505 Bytes
images = [] images = []
for item in manifest: for item in manifest:
@ -69,9 +71,19 @@ class Writer(object):
return images return images
def _metadata(self, metadata): def _metadata(self, metadata):
return 'test\x00\x00\x00\x00\x00' '''
Metadata takes the form:
title\x00
author\x00
copyright\x00
publisher\x00
isbn\x00
'''
return '\x00\x00\x00\x00\x00'
def _header_record(self, text_items, image_items): def _header_record(self, text_items, image_items):
# Todo: Find out more about header and add correct values to the file
# can be read by eReader reader software.
''' '''
text_items = the number of text pages text_items = the number of text pages
image_items = the number of images image_items = the number of images

View File

@ -63,7 +63,9 @@ class PDFOutput(OutputFormatPlugin):
def convert_text(self, oeb_book): def convert_text(self, oeb_book):
with TemporaryDirectory('_pdf_out') as oebdir: with TemporaryDirectory('_pdf_out') as oebdir:
OEBOutput(None).convert(oeb_book, oebdir, self.input_plugin, self.opts, self.log) from calibre.customize.ui import plugin_for_output_format
oeb_output = plugin_for_output_format('oeb')
oeb_output.convert(oeb, oeb_dir, self.input_plugin, self.opts, self.log)
opfpath = glob.glob(os.path.join(oebdir, '*.opf'))[0] opfpath = glob.glob(os.path.join(oebdir, '*.opf'))[0]
opf = OPF(opfpath, os.path.dirname(opfpath)) opf = OPF(opfpath, os.path.dirname(opfpath))

View File

View File

@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
import glob, os, shutil
from calibre.customize.conversion import InputFormatPlugin
from calibre.ptempfile import TemporaryDirectory
from calibre.utils.zipfile import ZipFile
from calibre.ebooks.pml.pmlconverter import pml_to_html
from calibre.ebooks.metadata.opf2 import OPFCreator
class PMLInput(InputFormatPlugin):
name = 'PML Input'
author = 'John Schember'
description = 'Convert PML to OEB'
# pmlz is a zip file containing pml files and png images.
file_types = set(['pml', 'pmlz'])
def process_pml(self, pml_path, html_path):
pclose = False
hclose = False
if not hasattr(pml_path, 'read'):
pml_stream = open(pml_path, 'rb')
pclose = True
else:
pml_stream = pml_path
if not hasattr(html_path, 'write'):
html_stream = open(html_path, 'wb')
hclose = True
else:
html_stream = html_path
ienc = pml_stream.encoding if pml_stream.encoding else 'utf-8'
if self.options.input_encoding:
ienc = self.options.input_encoding
html = pml_to_html(pml_stream.read().decode(ienc))
html_stream.write('<html><head><title /></head><body>' + html + '</body></html>')
if pclose:
pml_stream.close()
if hclose:
html_stream.close()
def convert(self, stream, options, file_ext, log,
accelerators):
self.options = options
pages, images = [], []
if file_ext == 'pmlz':
with TemporaryDirectory('_unpmlz') as tdir:
zf = ZipFile(stream)
zf.extractall(tdir)
pmls = glob.glob(os.path.join(tdir, '*.pml'))
for pml in pmls:
html_name = os.path.splitext(os.path.basename(pml))[0]+'.html'
html_path = os.path.join(os.getcwd(), html_name)
pages.append(html_name)
self.process_pml(pml, html_path)
imgs = glob.glob(os.path.join(tdir, '*.png'))
for img in imgs:
pimg_name = os.path.basename(img)
pimg_path = os.path.join(os.getcwd(), 'images', pimg_name)
images.append(pimg_name)
shutil.move(img, pimg_path)
else:
self.process_pml(stream, 'index.html')
pages.append('index.html')
images = []
# We want pages to be orded alphabetically.
pages.sort()
manifest_items = []
for item in pages+images:
manifest_items.append((item, None))
from calibre.ebooks.metadata.meta import get_metadata
mi = get_metadata(stream, 'pml')
opf = OPFCreator(os.getcwd(), mi)
opf.create_manifest(manifest_items)
opf.create_spine(pages)
with open('metadata.opf', 'wb') as opffile:
opf.render(opffile)
return os.path.join(os.getcwd(), 'metadata.opf')

View File

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
import os
import Image, cStringIO
from calibre.customize.conversion import OutputFormatPlugin
from calibre.ptempfile import TemporaryDirectory
from calibre.utils.zipfile import ZipFile
from calibre.ebooks.oeb.base import OEB_IMAGES
from calibre.ebooks.pml.pmlconverter import html_to_pml
class PMLOutput(OutputFormatPlugin):
name = 'PML Output'
author = 'John Schember'
file_type = 'pmlz'
def convert(self, oeb_book, output_path, input_plugin, opts, log):
with TemporaryDirectory('_pmlz_output') as tdir:
self.process_spine(oeb_book.spine, tdir)
self.write_images(oeb_book.manifest, tdir)
pmlz = ZipFile(output_path, 'w')
pmlz.add_dir(tdir)
def process_spine(self, spine, out_dir):
for item in spine:
html = html_to_pml(unicode(item)).encode('utf-8')
name = os.path.splitext(os.path.basename(item.href))[0] + '.pml'
path = os.path.join(out_dir, name)
with open(path, 'wb') as out:
out.write(html)
def write_images(self, manifest, out_dir):
for item in manifest:
if item.media_type in OEB_IMAGES:
im = Image.open(cStringIO.StringIO(item.data))
data = cStringIO.StringIO()
im.save(data, 'PNG')
data = data.getvalue()
name = os.path.splitext(os.path.basename(item.href))[0] + '.png'
path = os.path.join(out_dir, name)
with open(path, 'wb') as out:
out.write(data)

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import with_statement
''' '''
Convert pml markup to and from html Convert pml markup to and from html
''' '''
@ -47,6 +47,10 @@ PML_HTML_RULES = [
(re.compile(r'\\Sd="(?P<target>.+?)"(?P<text>.+?)\\Sd'), lambda match: '<a href="#sidebar-%s">%s</a>' % (match.group('target'), match.group('text'))), (re.compile(r'\\Sd="(?P<target>.+?)"(?P<text>.+?)\\Sd'), lambda match: '<a href="#sidebar-%s">%s</a>' % (match.group('target'), match.group('text'))),
(re.compile(r'\\I'), lambda match: ''), (re.compile(r'\\I'), lambda match: ''),
# Sidebar and Footnotes
(re.compile(r'<sidebar\s+id="(?P<target>.+?)">\s*(?P<text>.+?)\s*</sidebar>', re.DOTALL), lambda match: '<div id="sidebar-%s">%s</div>' % (match.group('target'), match.group('text'))),
(re.compile(r'<footnote\s+id="(?P<target>.+?)">\s*(?P<text>.+?)\s*</footnote>', re.DOTALL), lambda match: '<div id="footnote-%s">%s</div>' % (match.group('target'), match.group('text'))),
# eReader files are one paragraph per line. # eReader files are one paragraph per line.
# This forces the lines to wrap properly. # This forces the lines to wrap properly.
(re.compile('^(?P<text>.+)$', re.MULTILINE), lambda match: '<p>%s</p>' % match.group('text')), (re.compile('^(?P<text>.+)$', re.MULTILINE), lambda match: '<p>%s</p>' % match.group('text')),

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import QWidget, QListWidgetItem, Qt, QVariant, SIGNAL
from calibre.gui2.device_drivers.configwidget_ui import Ui_ConfigWidget
class ConfigWidget(QWidget, Ui_ConfigWidget):
def __init__(self, config, all_formats):
QWidget.__init__(self)
Ui_ConfigWidget.__init__(self)
self.setupUi(self)
self.config = config
format_map = config['format_map']
disabled_formats = list(set(all_formats).difference(format_map))
for format in format_map + disabled_formats:
item = QListWidgetItem(format, self.columns)
item.setData(Qt.UserRole, QVariant(format))
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
item.setCheckState(Qt.Checked if format in format_map else Qt.Unchecked)
self.connect(self.column_up, SIGNAL('clicked()'), self.up_column)
self.connect(self.column_down, SIGNAL('clicked()'), self.down_column)
def up_column(self):
idx = self.columns.currentRow()
if idx > 0:
self.columns.insertItem(idx-1, self.columns.takeItem(idx))
self.columns.setCurrentRow(idx-1)
def down_column(self):
idx = self.columns.currentRow()
if idx < self.columns.count()-1:
self.columns.insertItem(idx+1, self.columns.takeItem(idx))
self.columns.setCurrentRow(idx+1)
def format_map(self):
formats = [unicode(self.columns.item(i).data(Qt.UserRole).toString()) for i in range(self.columns.count()) if self.columns.item(i).checkState()==Qt.Checked]
return formats

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigWidget</class>
<widget class="QWidget" name="ConfigWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>442</width>
<height>332</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Select avaliable formats and their order for this device</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<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">
<item>
<widget class="QToolButton" name="column_up">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../gui2/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="../../gui2/images.qrc">
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../gui2/images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -5,8 +5,9 @@ import sys, os, shutil
from subprocess import check_call, call from subprocess import check_call, call
from calibre import __version__, __appname__ from calibre import __version__, __appname__
from calibre.customize.ui import device_plugins
DEVICES = devices() DEVICES = device_plugins()
DESTDIR = '' DESTDIR = ''
if os.environ.has_key('DESTDIR'): if os.environ.has_key('DESTDIR'):
@ -292,7 +293,7 @@ def setup_udev_rules(group_file, reload, fatal_errors):
</match> </match>
</match> </match>
</device> </device>
'''%dict(cls=cls.__name__, vendor_id=cls.VENDOR_ID, product_id=cls.PRODUCT_ID, '''%dict(cls=cls.__class__.__name__, vendor_id=cls.VENDOR_ID, product_id=cls.PRODUCT_ID,
prog=__appname__, bcd=cls.BCD)) prog=__appname__, bcd=cls.BCD))
fdi.write('\n'+cls.get_fdi()) fdi.write('\n'+cls.get_fdi())
fdi.write('\n</deviceinfo>\n') fdi.write('\n</deviceinfo>\n')