mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Created a rudimentary GUI that parses the media list on the device abd displays the books Switched to setuptools
310 lines
12 KiB
Python
Executable File
310 lines
12 KiB
Python
Executable File
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
|
|
## This program is free software; you can redistribute it and/or modify
|
|
## it under the terms of the GNU General Public License as published by
|
|
## the Free Software Foundation; either version 2 of the License, or
|
|
## (at your option) any later version.
|
|
##
|
|
## This program is distributed in the hope that it will be useful,
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
## GNU General Public License for more details.
|
|
##
|
|
## You should have received a copy of the GNU General Public License along
|
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
"""
|
|
Provides a command-line and optional graphical interface to the SONY Reader PRS-500.
|
|
|
|
For usage information run the script.
|
|
"""
|
|
|
|
import StringIO, sys, time, os
|
|
from optparse import OptionParser
|
|
|
|
from libprs500 import __version__ as VERSION
|
|
from libprs500.communicate import PRS500Device
|
|
from terminfo import TerminalController
|
|
from libprs500.errors import ArgumentError, DeviceError
|
|
|
|
|
|
MINIMUM_COL_WIDTH = 12 #: Minimum width of columns in ls output
|
|
|
|
def human_readable(size):
|
|
""" Convert a size in bytes into a human readle form """
|
|
if size < 1024: divisor, suffix = 1, ""
|
|
elif size < 1024*1024: divisor, suffix = 1024., "K"
|
|
elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "M"
|
|
elif size < 1024*1024*1024*1024: divisor, suffix = 1024*1024, "G"
|
|
size = str(size/divisor)
|
|
if size.find(".") > -1: size = size[:size.find(".")+2]
|
|
return size + suffix
|
|
|
|
class FileFormatter(object):
|
|
def __init__(self, file, term):
|
|
self.term = term
|
|
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
|
|
|
|
@apply
|
|
def mode_string():
|
|
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(**locals())
|
|
|
|
@apply
|
|
def name_in_color():
|
|
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(**locals())
|
|
|
|
@apply
|
|
def human_readable_size():
|
|
doc=""" File size in human readable form """
|
|
def fget(self):
|
|
human_readable(self.size)
|
|
return property(**locals())
|
|
|
|
@apply
|
|
def modification_time():
|
|
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(**locals())
|
|
|
|
@apply
|
|
def creation_time():
|
|
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(**locals())
|
|
|
|
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, term, recurse=False, color=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("/"): 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, term)
|
|
size = len(file.human_readable_size)
|
|
if size > maxlen: maxlen = size
|
|
for file in files:
|
|
file = FileFormatter(file, term)
|
|
name = file.name
|
|
lsoutput.append(name)
|
|
if color: name = file.name_in_color
|
|
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 main():
|
|
term = TerminalController()
|
|
cols = term.COLS
|
|
|
|
parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand is one of: info, books, df, ls, cp, mkdir, touch, cat or rm\n\n"+
|
|
"For help on a particular command: %prog command", version="libprs500 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 = PRS500Device(log_packets=options.log_packets)
|
|
try:
|
|
if command == "df":
|
|
data = dev.available_space()
|
|
print "Filesystem\tSize \tUsed \tAvail \tUse%"
|
|
for datum in data:
|
|
total, free, used, percent = human_readable(datum[2]), human_readable(datum[1]), human_readable(datum[2]-datum[1]), \
|
|
str(0 if datum[2]==0 else int(100*(datum[2]-datum[1])/(datum[2]*1.)))+"%"
|
|
print "%-10s\t%s\t%s\t%s\t%s"%(datum[0], total, used, free, percent)
|
|
elif command == "books":
|
|
main, card, d1, d2 = dev.books()
|
|
d1.close()
|
|
d2.close()
|
|
print "Books in main memory:"
|
|
for book in main:
|
|
print book
|
|
print
|
|
print "Books on storage card:"
|
|
for book in card:
|
|
print book
|
|
elif command == "mkdir":
|
|
parser = OptionParser(usage="usage: %prog mkdir [options] path\n\npath must begin with /,a:/ or b:/")
|
|
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\n\npath must begin with /,a:/ or b:/")
|
|
parser.add_option("--color", help="show ls output in color", dest="color", action="store_true", default=False)
|
|
parser.add_option("-l", help="In addition to the name of each file, print the file type, permissions, and timestamp (the modification time unless other times are selected). Times are local.", 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], term, color=options.color, 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\n\n"+\
|
|
"One of source or destination must be a path on the device. \n\nDevice paths have the form\n"+\
|
|
"prs500:mountpoint/my/path\n"+\
|
|
"where mountpoint is one of /, a: or b:\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)
|
|
options, args = parser.parse_args(args)
|
|
if len(args) != 2:
|
|
parser.print_help()
|
|
return 1
|
|
if args[0].startswith("prs500:"):
|
|
outfile = args[1]
|
|
path = args[0][7:]
|
|
if path.endswith("/"): path = path[:-1]
|
|
if os.path.isdir(outfile):
|
|
outfile = os.path.join(outfile, path[path.rfind("/")+1:])
|
|
try:
|
|
outfile = open(outfile, "w")
|
|
except IOError, e:
|
|
print >> sys.stderr, e
|
|
parser.print_help()
|
|
return 1
|
|
dev.get_file(path, outfile)
|
|
outfile.close()
|
|
elif args[1].startswith("prs500:"):
|
|
try:
|
|
infile = open(args[0], "r")
|
|
except IOError, e:
|
|
print >> sys.stderr, e
|
|
parser.print_help()
|
|
return 1
|
|
dev.put_file(infile, args[1][7:])
|
|
infile.close()
|
|
else:
|
|
parser.print_help()
|
|
return 1
|
|
elif command == "cat":
|
|
outfile = sys.stdout
|
|
parser = OptionParser(usage="usage: %prog cat path\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\n\npath should point to a file or empty directory on the device "+\
|
|
"and must begin with /,a:/ or b:/\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\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+
|
|
"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])
|
|
else:
|
|
parser.print_help()
|
|
if dev.handle: dev.close()
|
|
return 1
|
|
except (ArgumentError, DeviceError), e:
|
|
print >>sys.stderr, e
|
|
return 1
|
|
return 0
|