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 paths = [(name, length)]
95 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
96 'created by', 'announce':
98 if name == 'creation date':
99 data = decode_datetime(torrent[name])
100 elif name == 'announce-list':
101 data = decode_list(torrent[name])
104 meta.append(('.META/' + name, len(data)))
106 if 'private' in info:
107 meta.append(('.META/private', 1))
109 if 'piece length' in info:
110 meta.append(('.META/piece length', len(str(info['piece length']))))
112 for name, size in paths + meta:
113 print "-r--r--r-- 1 user group %d Jan 1 00:00 %s" % (size, name)
116 def mctorrent_copyout():
117 """Extract a file from the VFS"""
119 torrent_filename = sys.argv[3]
120 real_filename = sys.argv[4]
123 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
124 'created by', 'announce':
125 if torrent_filename == '.META/' + name:
127 if name == 'creation date':
128 data = decode_datetime(torrent[name])
129 elif name == 'announce-list':
130 data = decode_list(torrent[name])
132 data = str(torrent[name])
134 torrent_error('Unknown ' + name)
137 if torrent_filename in ('.META/private', '.META/piece length'):
138 if 'info' not in torrent:
139 torrent_error('Info absent')
140 info = torrent['info']
141 if torrent_filename == '.META/private':
142 if 'private' not in info:
143 torrent_error('Info absent')
144 if torrent_filename == '.META/piece length':
145 if 'piece length' not in info:
146 torrent_error('Info absent')
147 data = str(info[torrent_filename[len('.META/'):]])
149 if not torrent_filename.startswith('.META/'):
153 torrent_error('Unknown file name')
155 outfile = open(real_filename, 'w')
160 def mctorrent_copyin():
161 """Put a file to the VFS"""
162 sys.exit("Torrent VFS doesn't support adding files")
165 """Remove a file from the VFS"""
166 sys.exit("Torrent VFS doesn't support removing files/directories")
168 mctorrent_rmdir = mctorrent_rm
170 def mctorrent_mkdir():
171 """Create a directory in the VFS"""
172 sys.exit("Torrent VFS doesn't support creating directories")
175 def torrent_error(error_str):
176 logger.critical("Error parsing the torrent metafile: %s", error_str)
179 def decode_torrent():
181 torrent_file = open(sys.argv[2], 'r')
182 data = torrent_file.read()
185 except IOError, error_str:
186 torrent_error(error_str)
189 def decode_datetime(dt):
190 from time import localtime, asctime
192 l_now = localtime(the_time)
193 return asctime(l_now)
195 def decode_list(announce):
196 return '\n'.join(l[0] for l in announce)
199 command = sys.argv[1]
200 procname = "mctorrent_" + command
203 if not g.has_key(procname):
204 logger.critical("Unknown command %s", command)
207 torrent = decode_torrent()
214 logger.exception("Error during run")