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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
__license__   = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
"""
Provides a command-line interface to ebook devices.

For usage information run the script.
"""

import StringIO, sys, time, os
from optparse import OptionParser

from calibre import __version__, __appname__, human_readable, fsync
from calibre.devices.errors import ArgumentError, DeviceError, DeviceLocked
from calibre.customize.ui import device_plugins
from calibre.devices.scanner import DeviceScanner
from calibre.utils.config import device_prefs

MINIMUM_COL_WIDTH = 12  # : Minimum width of columns in ls output

class FileFormatter(object):

    def __init__(self, file):
        self.is_dir      = file.is_dir
        self.is_readonly = file.is_readonly
        self.size        = file.size
        self.ctime       = file.ctime
        self.wtime       = file.wtime
        self.name        = file.name
        self.path        = file.path

    @dynamic_property
    def mode_string(self):
        doc=""" The mode string for this file. There are only two modes read-only and read-write """
        def fget(self):
            mode, x = "-", "-"
            if self.is_dir:
                mode, x = "d", "x"
            if self.is_readonly:
                mode += "r-"+x+"r-"+x+"r-"+x
            else:
                mode += "rw"+x+"rw"+x+"rw"+x
            return mode
        return property(doc=doc, fget=fget)

    @dynamic_property
    def isdir_name(self):
        doc='''Return self.name + '/' if self is a directory'''
        def fget(self):
            name = self.name
            if self.is_dir:
                name += '/'
            return name
        return property(doc=doc, fget=fget)

    @dynamic_property
    def name_in_color(self):
        doc=""" The name in ANSI text. Directories are blue, ebooks are green """
        def fget(self):
            cname = self.name
            blue, green, normal = "", "", ""
            if self.term:
                blue, green, normal = self.term.BLUE, self.term.GREEN, self.term.NORMAL
            if self.is_dir:
                cname = blue + self.name + normal
            else:
                ext = self.name[self.name.rfind("."):]
                if ext in (".pdf", ".rtf", ".lrf", ".lrx", ".txt"):
                    cname = green + self.name + normal
            return cname
        return property(doc=doc, fget=fget)

    @dynamic_property
    def human_readable_size(self):
        doc=""" File size in human readable form """
        def fget(self):
            return human_readable(self.size)
        return property(doc=doc, fget=fget)

    @dynamic_property
    def modification_time(self):
        doc=""" Last modified time in the Linux ls -l format """
        def fget(self):
            return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime))
        return property(doc=doc, fget=fget)

    @dynamic_property
    def creation_time(self):
        doc=""" Last modified time in the Linux ls -l format """
        def fget(self):
            return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.ctime))
        return property(doc=doc, fget=fget)

def info(dev):
    info = dev.get_device_information()
    print "Device name:     ", info[0]
    print "Device version:  ", info[1]
    print "Software version:", info[2]
    print "Mime type:       ", info[3]

def ls(dev, path, recurse=False, human_readable_size=False, ll=False, cols=0):
    def col_split(l, cols):  # split list l into columns
        rows = len(l) / cols
        if len(l) % cols:
            rows += 1
        m = []
        for i in range(rows):
            m.append(l[i::rows])
        return m

    def row_widths(table):  # Calculate widths for each column in the row-wise table
        tcols = len(table[0])
        rowwidths = [0 for i in range(tcols)]
        for row in table:
            c = 0
            for item in row:
                rowwidths[c] = len(item) if len(item) > rowwidths[c] else rowwidths[c]
                c += 1
        return rowwidths

    output = StringIO.StringIO()
    if path.endswith("/") and len(path) > 1:
        path = path[:-1]
    dirs = dev.list(path, recurse)
    for dir in dirs:
        if recurse:
            print >>output, dir[0] + ":"
        lsoutput, lscoloutput = [], []
        files = dir[1]
        maxlen = 0
        if ll:  # Calculate column width for size column
            for file in files:
                size = len(str(file.size))
                if human_readable_size:
                    file = FileFormatter(file)
                    size = len(file.human_readable_size)
                if size > maxlen:
                    maxlen = size
        for file in files:
            file = FileFormatter(file)
            name = file.name if ll else file.isdir_name
            lsoutput.append(name)
            lscoloutput.append(name)
            if ll:
                size = str(file.size)
                if human_readable_size:
                    size = file.human_readable_size
                print >>output, file.mode_string, ("%"+str(maxlen)+"s")%size, file.modification_time, name
        if not ll and len(lsoutput) > 0:
            trytable = []
            for colwidth in range(MINIMUM_COL_WIDTH, cols):
                trycols = int(cols/colwidth)
                trytable = col_split(lsoutput, trycols)
                works = True
                for row in trytable:
                    row_break = False
                    for item in row:
                        if len(item) > colwidth - 1:
                            works, row_break = False, True
                            break
                    if row_break:
                        break
                if works:
                    break
            rowwidths = row_widths(trytable)
            trytablecol = col_split(lscoloutput, len(trytable[0]))
            for r in range(len(trytable)):
                for c in range(len(trytable[r])):
                    padding = rowwidths[c] - len(trytable[r][c])
                    print >>output, trytablecol[r][c], "".ljust(padding),
                print >>output
        print >>output
    listing = output.getvalue().rstrip()+ "\n"
    output.close()
    return listing

def shutdown_plugins():
    for d in device_plugins():
        try:
            d.shutdown()
        except:
            pass

def main():
    from calibre.utils.terminal import geometry
    cols = geometry()[0]

    parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand "+
            "is one of: info, books, df, ls, cp, mkdir, touch, cat, rm, eject, test_file\n\n"+
    "For help on a particular command: %prog command", version=__appname__+" version: " + __version__)
    parser.add_option("--log-packets", help="print out packet stream to stdout. "+
                    "The numbers in the left column are byte offsets that allow the packet size to be read off easily.",
    dest="log_packets", action="store_true", default=False)
    parser.remove_option("-h")
    parser.disable_interspersed_args()  # Allow unrecognized options
    options, args = parser.parse_args()

    if len(args) < 1:
        parser.print_help()
        return 1

    command = args[0]
    args = args[1:]
    dev = None
    scanner = DeviceScanner()
    scanner.scan()
    connected_devices = []

    for d in device_plugins():
        try:
            d.startup()
        except:
            print ('Startup failed for device plugin: %s'%d)
        if d.MANAGES_DEVICE_PRESENCE:
            cd = d.detect_managed_devices(scanner.devices)
            if cd is not None:
                connected_devices.append((cd, d))
                dev = d
                break
            continue
        ok, det = scanner.is_device_connected(d)
        if ok:
            dev = d
            dev.reset(log_packets=options.log_packets, detected_device=det)
            connected_devices.append((det, dev))

    if dev is None:
        print >>sys.stderr, 'Unable to find a connected ebook reader.'
        shutdown_plugins()
        return 1

    for det, d in connected_devices:
        try:
            d.open(det, None)
        except:
            continue
        else:
            dev = d
            d.specialize_global_preferences(device_prefs)
            break

    try:
        if command == "df":
            total = dev.total_space(end_session=False)
            free = dev.free_space()
            where = ("Memory", "Card A", "Card B")
            print "Filesystem\tSize \tUsed \tAvail \tUse%"
            for i in range(3):
                print "%-10s\t%s\t%s\t%s\t%s"%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]),
                                                                            str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+"%")
        elif command == 'eject':
            dev.eject()
        elif command == "books":
            print "Books in main memory:"
            for book in dev.books():
                print book
            print "\nBooks on storage carda:"
            for book in dev.books(oncard='carda'):
                print book
            print "\nBooks on storage cardb:"
            for book in dev.books(oncard='cardb'):
                print book
        elif command == "mkdir":
            parser = OptionParser(usage="usage: %prog mkdir [options] path\nCreate a directory on the device\n\npath must begin with / or card:/")
            if len(args) != 1:
                parser.print_help()
                sys.exit(1)
            dev.mkdir(args[0])
        elif command == "ls":
            parser = OptionParser(usage="usage: %prog ls [options] path\nList files on the device\n\npath must begin with / or card:/")
            parser.add_option(
                "-l", help="In addition to the name of each file, print the file type, permissions, and  timestamp  (the  modification time, in the local timezone). Times are local.",  # noqa
                              dest="ll", action="store_true", default=False)
            parser.add_option("-R", help="Recursively list subdirectories encountered. /dev and /proc are omitted",
                              dest="recurse", action="store_true", default=False)
            parser.remove_option("-h")
            parser.add_option("-h", "--human-readable", help="show sizes in human readable format", dest="hrs", action="store_true", default=False)
            options, args = parser.parse_args(args)
            if len(args) != 1:
                parser.print_help()
                return 1
            print ls(dev, args[0], recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols),
        elif command == "info":
            info(dev)
        elif command == "cp":
            usage="usage: %prog cp [options] source destination\nCopy files to/from the device\n\n"+\
            "One of source or destination must be a path on the device. \n\nDevice paths have the form\n"+\
            "dev:mountpoint/my/path\n"+\
            "where mountpoint is one of / or carda: or cardb:/\n\n"+\
            "source must point to a file for which you have read permissions\n"+\
            "destination must point to a file or directory for which you have write permissions"
            parser = OptionParser(usage=usage)
            parser.add_option('-f', '--force', dest='force', action='store_true', default=False,
                              help='Overwrite the destination file if it exists already.')
            options, args = parser.parse_args(args)
            if len(args) != 2:
                parser.print_help()
                return 1
            if args[0].startswith("dev:"):
                outfile = args[1]
                path = args[0][4:]
                if path.endswith("/"):
                    path = path[:-1]
                if os.path.isdir(outfile):
                    outfile = os.path.join(outfile, path[path.rfind("/")+1:])
                try:
                    outfile = open(outfile, "wb")
                except IOError as e:
                    print >> sys.stderr, e
                    parser.print_help()
                    return 1
                dev.get_file(path, outfile)
                fsync(outfile)
                outfile.close()
            elif args[1].startswith("dev:"):
                try:
                    infile = open(args[0], "rb")
                except IOError as e:
                    print >> sys.stderr, e
                    parser.print_help()
                    return 1
                dev.put_file(infile, args[1][4:], replace_file=options.force)
                infile.close()
            else:
                parser.print_help()
                return 1
        elif command == "cat":
            outfile = sys.stdout
            parser = OptionParser(
                usage="usage: %prog cat path\nShow file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/")
            options, args = parser.parse_args(args)
            if len(args) != 1:
                parser.print_help()
                return 1
            if args[0].endswith("/"):
                path = args[0][:-1]
            else:
                path = args[0]
            outfile = sys.stdout
            dev.get_file(path, outfile)
        elif command == "rm":
            parser = OptionParser(usage="usage: %prog rm path\nDelete files from the device\n\npath should point to a file or empty directory on the device "+
                                  "and must begin with / or card:/\n\n"+
                                  "rm will DELETE the file. Be very CAREFUL")
            options, args = parser.parse_args(args)
            if len(args) != 1:
                parser.print_help()
                return 1
            dev.rm(args[0])
        elif command == "touch":
            parser = OptionParser(usage="usage: %prog touch path\nCreate an empty file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+  # noqa
            "Unfortunately, I cant figure out how to update file times on the device, so if path already exists, touch does nothing")
            options, args = parser.parse_args(args)
            if len(args) != 1:
                parser.print_help()
                return 1
            dev.touch(args[0])
        elif command == 'test_file':
            parser = OptionParser(usage=("usage: %prog test_file path\n"
                'Open device, copy file specified by path to device and '
                'then eject device.'))
            options, args = parser.parse_args(args)
            if len(args) != 1:
                parser.print_help()
                return 1
            path = args[0]
            from calibre.ebooks.metadata.meta import get_metadata
            mi = get_metadata(open(path, 'rb'), path.rpartition('.')[-1].lower())
            print dev.upload_books([args[0]], [os.path.basename(args[0])],
                    end_session=False, metadata=[mi])
            dev.eject()
        else:
            parser.print_help()
            if getattr(dev, 'handle', False):
                dev.close()
            return 1
    except DeviceLocked:
        print >> sys.stderr, "The device is locked. Use the --unlock option"
    except (ArgumentError, DeviceError) as e:
        print >>sys.stderr, e
        return 1
    finally:
        shutdown_plugins()

    return 0

if __name__ == '__main__':
    main()