~ubuntu-branches/debian/sid/calibre/sid

« back to all changes in this revision

Viewing changes to src/calibre/linux.py

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2014-05-14 18:17:50 UTC
  • mfrom: (1.5.10)
  • Revision ID: package-import@ubuntu.com-20140514181750-xyrxqa47dbw0qfhu
Tags: 1.36.0+dfsg-1
* New upstream release:
  - Fixes editing of metadata (Closes: #741638)

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
 
4
4
''' Post installation script for linux '''
5
5
 
6
 
import sys, os, cPickle, textwrap, stat
 
6
import sys, os, cPickle, textwrap, stat, errno
7
7
from subprocess import check_call
8
8
from functools import partial
9
9
 
75
75
                except:
76
76
                    pass
77
77
            elif os.path.exists(path):
78
 
                with open(path, 'r+b') as f:
79
 
                    if f.read() != val:
80
 
                        f.seek(0)
81
 
                        f.truncate()
82
 
                        f.write(val)
 
78
                try:
 
79
                    with open(path, 'r+b') as f:
 
80
                        if f.read() != val:
 
81
                            f.seek(0)
 
82
                            f.truncate()
 
83
                            f.write(val)
 
84
                except EnvironmentError as e:
 
85
                    if e.errno != errno.EACCES:
 
86
                        raise
83
87
 
84
88
# Uninstall script {{{
85
89
UNINSTALL = '''\
86
90
#!{python}
 
91
from __future__ import print_function, unicode_literals
87
92
euid = {euid}
88
93
 
89
94
import os, subprocess, shutil
90
95
 
 
96
try:
 
97
    raw_input
 
98
except NameError:
 
99
    raw_input = input
 
100
 
91
101
if os.geteuid() != euid:
92
 
    print 'WARNING: uninstaller must be run as', euid, 'to remove all files'
93
 
 
94
 
for x in {manifest!r}:
95
 
    if not os.path.exists(x): continue
96
 
    print 'Removing', x
 
102
    print ('The installer was last run as user id:', euid, 'To remove all files you must run the uninstaller as the same user')
 
103
    if raw_input('Proceed anyway? [y/n]:').lower() != 'y':
 
104
        raise SystemExit(1)
 
105
 
 
106
frozen_path = {frozen_path!r}
 
107
if not frozen_path or not os.path.exists(os.path.join(frozen_path, 'resources', 'calibre-mimetypes.xml')):
 
108
    frozen_path = None
 
109
 
 
110
for f in {mime_resources!r}:
 
111
    cmd = ['xdg-mime', 'uninstall', f]
 
112
    print ('Removing mime resource:', os.path.basename(f))
 
113
    ret = subprocess.call(cmd, shell=False)
 
114
    if ret != 0:
 
115
        print ('WARNING: Failed to remove mime resource', f)
 
116
 
 
117
for x in tuple({manifest!r}) + tuple({appdata_resources!r}) + (os.path.abspath(__file__), __file__, frozen_path):
 
118
    if not x or not os.path.exists(x):
 
119
        continue
 
120
    print ('Removing', x)
97
121
    try:
98
122
        if os.path.isdir(x):
99
123
            shutil.rmtree(x)
100
124
        else:
101
125
            os.unlink(x)
102
126
    except Exception as e:
103
 
        print 'Failed to delete', x
104
 
        print '\t', e
 
127
        print ('Failed to delete', x)
 
128
        print ('\t', e)
105
129
 
106
130
icr = {icon_resources!r}
107
 
for context, name, size in icr:
 
131
mimetype_icons = []
 
132
 
 
133
def remove_icon(context, name, size, update=False):
108
134
    cmd = ['xdg-icon-resource', 'uninstall', '--context', context, '--size', size, name]
109
 
    if (context, name) != icr[-1]:
 
135
    if not update:
110
136
        cmd.insert(2, '--noupdate')
111
 
    ret = subprocess.call(cmd)
 
137
    print ('Removing icon:', name, 'from context:', context, 'at size:', size)
 
138
    ret = subprocess.call(cmd, shell=False)
112
139
    if ret != 0:
113
 
        print 'WARNING: Failed to remove icon', name
 
140
        print ('WARNING: Failed to remove icon', name)
 
141
 
 
142
for i, (context, name, size) in enumerate(icr):
 
143
    if context == 'mimetypes':
 
144
        mimetype_icons.append((name, size))
 
145
        continue
 
146
    remove_icon(context, name, size, update=i == len(icr) - 1)
114
147
 
115
148
mr = {menu_resources!r}
116
149
for f in mr:
117
150
    cmd = ['xdg-desktop-menu', 'uninstall', f]
118
 
    ret = subprocess.call(cmd)
 
151
    print ('Removing desktop file:', f)
 
152
    ret = subprocess.call(cmd, shell=False)
119
153
    if ret != 0:
120
 
        print 'WARNING: Failed to remove menu item', f
121
 
 
122
 
os.remove(os.path.abspath(__file__))
 
154
        print ('WARNING: Failed to remove menu item', f)
 
155
 
 
156
print ()
 
157
 
 
158
if mimetype_icons and raw_input('Remove the ebook format icons? [y/n]:').lower() in ['', 'y']:
 
159
    for i, (name, size) in enumerate(mimetype_icons):
 
160
        remove_icon('mimetypes', name, size, update=i == len(mimetype_icons) - 1)
123
161
'''
124
162
 
125
163
# }}}
409
447
        print '\n'+'_'*20, 'WARNING','_'*20
410
448
        prints(*args, **kwargs)
411
449
        print '_'*50
 
450
        print ('\n')
412
451
        self.warnings.append((args, kwargs))
413
452
        sys.stdout.flush()
414
453
 
430
469
                os.path.join(self.opts.staging_root, 'etc')
431
470
 
432
471
        scripts = cPickle.loads(P('scripts.pickle', data=True))
 
472
        self.manifest = manifest or []
433
473
        if getattr(sys, 'frozen_path', False):
434
 
            self.info('Creating symlinks...')
435
 
            for exe in scripts.keys():
436
 
                dest = os.path.join(self.opts.staging_bindir, exe)
437
 
                if os.path.lexists(dest):
438
 
                    os.unlink(dest)
439
 
                tgt = os.path.join(getattr(sys, 'frozen_path'), exe)
440
 
                self.info('\tSymlinking %s to %s'%(tgt, dest))
441
 
                os.symlink(tgt, dest)
 
474
            if os.access(self.opts.staging_bindir, os.W_OK):
 
475
                self.info('Creating symlinks...')
 
476
                for exe in scripts.keys():
 
477
                    dest = os.path.join(self.opts.staging_bindir, exe)
 
478
                    if os.path.lexists(dest):
 
479
                        os.unlink(dest)
 
480
                    tgt = os.path.join(getattr(sys, 'frozen_path'), exe)
 
481
                    self.info('\tSymlinking %s to %s'%(tgt, dest))
 
482
                    os.symlink(tgt, dest)
 
483
                    self.manifest.append(dest)
 
484
            else:
 
485
                self.warning(textwrap.fill(
 
486
                    'No permission to write to %s, not creating program launch symlinks,'
 
487
                    ' you should ensure that %s is in your PATH or create the symlinks yourself' % (
 
488
                        self.opts.staging_bindir, getattr(sys, 'frozen_path', 'the calibre installation directory'))))
442
489
 
443
 
        if manifest is None:
444
 
            manifest = [os.path.abspath(os.path.join(opts.staging_bindir, x)) for x in
445
 
                scripts.keys()]
446
 
        self.manifest = manifest
447
490
        self.icon_resources = []
448
491
        self.menu_resources = []
449
492
        self.mime_resources = []
 
493
        self.appdata_resources = []
450
494
        if islinux or isbsd:
451
495
            self.setup_completion()
452
496
        if islinux or isbsd:
465
509
                    os.rmdir(config_dir)
466
510
 
467
511
        if warn is None and self.warnings:
468
 
            self.info('There were %d warnings'%len(self.warnings))
 
512
            self.info('\n\nThere were %d warnings\n'%len(self.warnings))
469
513
            for args, kwargs in self.warnings:
470
514
                self.info('*', *args, **kwargs)
471
515
                print
472
516
 
473
517
    def create_uninstaller(self):
474
 
        dest = os.path.join(self.opts.staging_bindir, 'calibre-uninstall')
475
 
        raw = UNINSTALL.format(python=os.path.abspath(sys.executable), euid=os.geteuid(),
476
 
                manifest=self.manifest, icon_resources=self.icon_resources,
477
 
                menu_resources=self.menu_resources)
 
518
        base = self.opts.staging_bindir
 
519
        if not os.access(base, os.W_OK) and getattr(sys, 'frozen_path', False):
 
520
            base = sys.frozen_path
 
521
        dest = os.path.join(base, 'calibre-uninstall')
 
522
        self.info('Creating un-installer:', dest)
 
523
        raw = UNINSTALL.format(
 
524
            python='/usr/bin/python', euid=os.geteuid(),
 
525
            manifest=self.manifest, icon_resources=self.icon_resources,
 
526
            mime_resources=self.mime_resources, menu_resources=self.menu_resources,
 
527
            appdata_resources=self.appdata_resources, frozen_path=getattr(sys, 'frozen_path', None))
478
528
        try:
479
529
            with open(dest, 'wb') as f:
480
530
                f.write(raw)
499
549
            from calibre.utils.smtp import option_parser as smtp_op
500
550
            from calibre.library.server.main import option_parser as serv_op
501
551
            from calibre.ebooks.oeb.polish.main import option_parser as polish_op, SUPPORTED
 
552
            from calibre.ebooks.oeb.polish.import_book import IMPORTABLE
502
553
            from calibre.debug import option_parser as debug_op
503
554
            from calibre.ebooks import BOOK_EXTENSIONS
504
555
            from calibre.customize.ui import available_input_formats
505
556
            input_formats = sorted(all_input_formats())
506
 
            tweak_formats = sorted(x.lower() for x in SUPPORTED)
 
557
            tweak_formats = sorted(x.lower() for x in SUPPORTED|IMPORTABLE)
507
558
            zsh = ZshCompleter(self.opts)
508
559
            bc = os.path.join(os.path.dirname(self.opts.staging_sharedir),
509
560
                'bash-completion')
516
567
                    f = os.path.join(self.opts.staging_etc, 'bash_completion.d/calibre')
517
568
            if not os.path.exists(os.path.dirname(f)):
518
569
                os.makedirs(os.path.dirname(f))
 
570
            bash_comp_dest, zsh_comp_dest = f, None
519
571
            if zsh.dest:
520
572
                self.info('Installing zsh completion to:', zsh.dest)
521
573
                self.manifest.append(zsh.dest)
522
 
            self.manifest.append(f)
 
574
                zsh_comp_dest = zsh.dest
523
575
            complete = 'calibre-complete'
524
576
            if getattr(sys, 'frozen_path', None):
525
577
                complete = os.path.join(getattr(sys, 'frozen_path'), complete)
631
683
                complete -o nospace -C %s ebook-convert
632
684
                ''')%complete)
633
685
            zsh.write()
 
686
            self.manifest.extend((bash_comp_dest, zsh_comp_dest))
634
687
        except TypeError as err:
635
688
            if 'resolve_entities' in str(err):
636
689
                print 'You need python-lxml >= 2.0.5 for calibre'
637
690
                sys.exit(1)
638
691
            raise
 
692
        except EnvironmentError as e:
 
693
            if e.errno == errno.EACCES:
 
694
                self.warning('Failed to setup completion, permission denied')
639
695
        except:
640
696
            if self.opts.fatal_errors:
641
697
                raise
677
733
                self.icon_resources.append(('mimetypes', 'application-x-mobi8-ebook', '128'))
678
734
                render_img('lt.png', 'calibre-gui.png', width=256, height=256)
679
735
                cc('xdg-icon-resource install --noupdate --size 256 calibre-gui.png calibre-gui', shell=True)
680
 
                self.icon_resources.append(('apps', 'calibre-gui', '128'))
681
 
                render_img('viewer.png', 'calibre-viewer.png')
682
 
                cc('xdg-icon-resource install --size 128 calibre-viewer.png calibre-viewer', shell=True)
683
 
                self.icon_resources.append(('apps', 'calibre-viewer', '128'))
684
 
                render_img('tweak.png', 'calibre-ebook-edit.png')
685
 
                cc('xdg-icon-resource install --size 128 calibre-ebook-edit.png calibre-ebook-edit', shell=True)
686
 
                self.icon_resources.append(('apps', 'calibre-ebook-edit', '128'))
 
736
                self.icon_resources.append(('apps', 'calibre-gui', '256'))
 
737
                render_img('viewer.png', 'calibre-viewer.png', width=256, height=256)
 
738
                cc('xdg-icon-resource install --size 256 calibre-viewer.png calibre-viewer', shell=True)
 
739
                self.icon_resources.append(('apps', 'calibre-viewer', '256'))
 
740
                render_img('tweak.png', 'calibre-ebook-edit.png', width=256, height=256)
 
741
                cc('xdg-icon-resource install --size 256 calibre-ebook-edit.png calibre-ebook-edit', shell=True)
 
742
                self.icon_resources.append(('apps', 'calibre-ebook-edit', '256'))
687
743
 
688
744
                mimetypes = set([])
689
745
                for x in all_input_formats():
717
773
                    try:
718
774
                        os.mkdir(appdata)
719
775
                    except:
720
 
                        prints('Failed to create %s not installing appdata files' % appdata)
 
776
                        self.warning('Failed to create %s not installing appdata files' % appdata)
721
777
                if os.path.exists(appdata) and not os.access(appdata, os.W_OK):
722
 
                    prints('Do not have write permissions for %s not installing appdata files' % appdata)
 
778
                    self.warning('Do not have write permissions for %s not installing appdata files' % appdata)
723
779
                else:
724
780
                    from calibre.utils.localization import get_all_translators
725
781
                    translators = dict(get_all_translators())
731
787
                    self.menu_resources.append(x)
732
788
                    ak = x.partition('.')[0]
733
789
                    if ak in APPDATA and os.access(appdata, os.W_OK):
734
 
                        write_appdata(ak, APPDATA[ak], appdata, translators)
 
790
                        self.appdata_resources.append(write_appdata(ak, APPDATA[ak], appdata, translators))
735
791
                cc(['xdg-desktop-menu', 'forceupdate'])
736
 
                f = open('calibre-mimetypes.xml', 'wb')
737
 
                f.write(MIME)
738
 
                f.close()
739
 
                self.mime_resources.append('calibre-mimetypes.xml')
740
 
                cc('xdg-mime install ./calibre-mimetypes.xml', shell=True)
 
792
                MIME = P('calibre-mimetypes.xml')
 
793
                self.mime_resources.append(MIME)
 
794
                cc(['xdg-mime', 'install', MIME])
741
795
        except Exception:
742
796
            if self.opts.fatal_errors:
743
797
                raise
875
929
GenericName=Viewer for E-books
876
930
Comment=Viewer for E-books in all the major formats
877
931
TryExec=ebook-viewer
878
 
Exec=ebook-viewer %f
 
932
Exec=ebook-viewer --detach %f
879
933
Icon=calibre-viewer
880
934
Categories=Graphics;Viewer;
881
935
'''
888
942
GenericName=Edit E-books
889
943
Comment=Edit e-books in various formats
890
944
TryExec=ebook-edit
891
 
Exec=ebook-edit %f
 
945
Exec=ebook-edit --detach %f
892
946
Icon=calibre-ebook-edit
893
947
Categories=Office;
894
948
'''
901
955
GenericName=E-book library management
902
956
Comment=E-book library management: Convert, view, share, catalogue all your e-books
903
957
TryExec=calibre
904
 
Exec=calibre %F
 
958
Exec=calibre --detach %F
905
959
Icon=calibre-gui
906
960
Categories=Office;
907
961
'''
972
1026
    return fpath
973
1027
 
974
1028
 
975
 
MIME = '''\
976
 
<?xml version="1.0"?>
977
 
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
978
 
    <mime-type type="application/x-sony-bbeb">
979
 
        <comment>SONY E-book compiled format</comment>
980
 
        <glob pattern="*.lrf"/>
981
 
    </mime-type>
982
 
    <mime-type type="application/epub+zip">
983
 
        <comment>EPUB ebook format</comment>
984
 
        <glob pattern="*.epub"/>
985
 
    </mime-type>
986
 
    <mime-type type="text/lrs">
987
 
        <comment>SONY E-book source format</comment>
988
 
        <glob pattern="*.lrs"/>
989
 
    </mime-type>
990
 
    <mime-type type="application/x-mobipocket-ebook">
991
 
        <comment>Amazon Mobipocket e-book format</comment>
992
 
        <sub-class-of type="application/x-palm-database"/>
993
 
        <glob pattern="*.azw"/>
994
 
    </mime-type>
995
 
    <mime-type type="application/x-topaz-ebook">
996
 
        <comment>Amazon Topaz ebook format</comment>
997
 
        <glob pattern="*.tpz"/>
998
 
        <glob pattern="*.azw1"/>
999
 
    </mime-type>
1000
 
    <mime-type type="application/x-kindle-application">
1001
 
        <comment>Amazon Kindle Application (Kindlet)</comment>
1002
 
        <sub-class-of type="application/x-java-archive"/>
1003
 
        <glob pattern="*.azw2"/>
1004
 
    </mime-type>
1005
 
    <mime-type type="application/x-mobipocket-subscription">
1006
 
        <comment>Amazon Mobipocket ebook newspaper format</comment>
1007
 
        <sub-class-of type="application/x-mobipocket-ebook"/>
1008
 
        <!-- Technically, this depends on the cdeType (NWPR or MAGZ), but since EXTH headers have a variable length, it's tricky to probe via magic... -->
1009
 
        <alias type="application/x-mobipocket-subscription-magazine"/>
1010
 
        <glob pattern="*.pobi"/>
1011
 
    </mime-type>
1012
 
    <mime-type type="application/x-mobi8-ebook">
1013
 
        <comment>Amazon KF8 ebook format</comment>
1014
 
        <sub-class-of type="application/x-palm-database"/>
1015
 
        <glob pattern="*.azw3"/>
1016
 
    </mime-type>
1017
 
</mime-info>
1018
 
'''
1019
 
 
1020
1029
def render_img(image, dest, width=128, height=128):
1021
1030
    from PyQt4.Qt import QImage, Qt
1022
1031
    img = QImage(I(image)).scaled(width, height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)