3 """Torrent Virtual FileSystem for Midnight Commander
5 The script requires Midnight Commander 3.1+
6 (http://www.midnight-commander.org/), Python 2.4+ (http://www.python.org/),
7 module eff_bdecode.py (http://effbot.org/zone/bencode.htm).
9 Put the script in the /usr/[local/][lib|share]/mc/extfs/, and add a line
10 "torrent" to the /usr/[local/][lib|share]/mc/extfs/extfs.ini. Make the script
13 Run this "cd" command in the Midnight Commander (in the "bindings" file the
14 command is "%cd"): cd file#torrent, where "file" is the name of your torrent
15 metafile. The VFS lists all files and directories from the torrent metafile;
16 all files appear empty, of course, but the sizes are shown. Filenames are
17 reencoded from the metafile's encoding to the current locale.
19 Along with the files/directories in the torrent metafile the VFS also presents
20 meta information - in the form of files in .META directory. The size and
21 contents of these files are taken from the corresponding fields in the torrent
22 metafile. The VFS doesn't check if the torrent contains .META file or directory
25 Date/time for all files is set at midnight of the 1st January of the current
26 year. The filesystem is, naturally, read-only.
33 __author__ = "Oleg Broytman <phd@phd.pp.ru>"
34 __copyright__ = "Copyright (C) 2010 PhiloSoft Design"
37 import locale, sys, os
38 from eff_bdecode import decode
41 logger = logging.getLogger('torrent-mcextfs')
42 log_err_handler = logging.StreamHandler(sys.stderr)
43 logger.addHandler(log_err_handler)
44 logger.setLevel(logging.INFO)
48 Torrent Virtual FileSystem for Midnight Commander version %s
52 This is not a program. Put the script in /usr/[local/][lib|share]/mc/extfs.
53 For more information read the source!""",
54 __version__, __author__, __copyright__
59 locale.setlocale(locale.LC_ALL, '')
60 charset = locale.getpreferredencoding()
64 """List the entire VFS"""
66 if 'info' not in torrent:
67 torrent_error('Info absent')
69 info = torrent['info']
70 if 'name' not in info:
71 torrent_error('Unknown name')
77 encoding = torrent.get('encoding', None)
79 if 'path' not in file:
80 torrent_error('Unknown path')
81 if 'length' not in file:
82 torrent_error('Unknown length')
83 path = '/'.join([name] + file['path'])
84 if charset and encoding and (charset != encoding):
85 path = path.decode(encoding, 'replace').encode(charset, 'replace')
86 length = file['length']
87 paths.append((path, length))
88 else: # One-file torrent
89 if 'length' not in info:
90 torrent_error('Unknown length')
91 length = info['length']
92 if charset and encoding and (charset != encoding):
93 name = name.decode(encoding, 'replace').encode(charset, 'replace')
94 paths = [(name, length)]
97 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
98 'created by', 'announce':
100 if name == 'creation date':
101 data = decode_datetime(torrent[name])
102 elif name == 'announce-list':
103 data = decode_list(torrent[name])
106 meta.append(('.META/' + name, len(data)))
108 if 'private' in info:
109 meta.append(('.META/private', 1))
111 if 'piece length' in info:
112 meta.append(('.META/piece length', len(str(info['piece length']))))
114 for name, size in paths + meta:
115 print "-r--r--r-- 1 user group %d Jan 1 00:00 %s" % (size, name)
118 def mctorrent_copyout():
119 """Extract a file from the VFS"""
121 torrent_filename = sys.argv[3]
122 real_filename = sys.argv[4]
125 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
126 'created by', 'announce':
127 if torrent_filename == '.META/' + name:
129 if name == 'creation date':
130 data = decode_datetime(torrent[name])
131 elif name == 'announce-list':
132 data = decode_list(torrent[name])
134 data = str(torrent[name])
136 torrent_error('Unknown ' + name)
139 if torrent_filename in ('.META/private', '.META/piece length'):
140 if 'info' not in torrent:
141 torrent_error('Info absent')
142 info = torrent['info']
143 if torrent_filename == '.META/private':
144 if 'private' not in info:
145 torrent_error('Info absent')
146 if torrent_filename == '.META/piece length':
147 if 'piece length' not in info:
148 torrent_error('Info absent')
149 data = str(info[torrent_filename[len('.META/'):]])
151 if not torrent_filename.startswith('.META/'):
155 torrent_error('Unknown file name')
157 outfile = open(real_filename, 'w')
162 def mctorrent_copyin():
163 """Put a file to the VFS"""
164 sys.exit("Torrent VFS doesn't support adding files")
167 """Remove a file from the VFS"""
168 sys.exit("Torrent VFS doesn't support removing files/directories")
170 mctorrent_rmdir = mctorrent_rm
172 def mctorrent_mkdir():
173 """Create a directory in the VFS"""
174 sys.exit("Torrent VFS doesn't support creating directories")
177 def torrent_error(error_str):
178 logger.critical("Error parsing the torrent metafile: %s", error_str)
181 def decode_torrent():
183 torrent_file = open(sys.argv[2], 'r')
184 data = torrent_file.read()
187 except IOError, error_str:
188 torrent_error(error_str)
191 def decode_datetime(dt):
192 from time import localtime, asctime
194 l_now = localtime(the_time)
195 return asctime(l_now)
197 def decode_list(announce):
198 return '\n'.join(l[0] for l in announce)
201 command = sys.argv[1]
202 procname = "mctorrent_" + command
205 if not g.has_key(procname):
206 logger.critical("Unknown command %s", command)
209 torrent = decode_torrent()
216 logger.exception("Error during run")