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')
74 encoding = torrent.get('encoding', None)
80 if 'path' not in file:
81 torrent_error('Unknown path')
82 if 'length' not in file:
83 torrent_error('Unknown length')
84 path = '/'.join([name] + file['path'])
85 if charset and encoding and (charset != encoding):
86 path = path.decode(encoding, 'replace').encode(charset, 'replace')
87 length = file['length']
88 paths.append((path, length))
89 else: # One-file torrent
90 if 'length' not in info:
91 torrent_error('Unknown length')
92 length = info['length']
93 if charset and encoding and (charset != encoding):
94 name = name.decode(encoding, 'replace').encode(charset, 'replace')
95 paths = [(name, length)]
98 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
99 'created by', 'announce':
101 if name == 'creation date':
102 data = decode_datetime(torrent[name])
103 elif name == 'announce-list':
104 data = decode_list(torrent[name])
107 meta.append(('.META/' + name, len(data)))
109 if 'private' in info:
110 meta.append(('.META/private', 1))
112 if 'piece length' in info:
113 meta.append(('.META/piece length', len(str(info['piece length']))))
115 for name, size in paths + meta:
116 print "-r--r--r-- 1 user group %d Jan 1 00:00 %s" % (size, name)
119 def mctorrent_copyout():
120 """Extract a file from the VFS"""
122 torrent_filename = sys.argv[3]
123 real_filename = sys.argv[4]
126 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
127 'created by', 'announce':
128 if torrent_filename == '.META/' + name:
130 if name == 'creation date':
131 data = decode_datetime(torrent[name])
132 elif name == 'announce-list':
133 data = decode_list(torrent[name])
135 data = str(torrent[name])
137 torrent_error('Unknown ' + name)
140 if torrent_filename in ('.META/private', '.META/piece length'):
141 if 'info' not in torrent:
142 torrent_error('Info absent')
143 info = torrent['info']
144 if torrent_filename == '.META/private':
145 if 'private' not in info:
146 torrent_error('Info absent')
147 if torrent_filename == '.META/piece length':
148 if 'piece length' not in info:
149 torrent_error('Info absent')
150 data = str(info[torrent_filename[len('.META/'):]])
152 if not torrent_filename.startswith('.META/'):
156 torrent_error('Unknown file name')
158 outfile = open(real_filename, 'w')
163 def mctorrent_copyin():
164 """Put a file to the VFS"""
165 sys.exit("Torrent VFS doesn't support adding files (read-only filesystem)")
168 """Remove a file from the VFS"""
169 sys.exit("Torrent VFS doesn't support removing files/directories (read-only filesystem)")
171 mctorrent_rmdir = mctorrent_rm
173 def mctorrent_mkdir():
174 """Create a directory in the VFS"""
175 sys.exit("Torrent VFS doesn't support creating directories (read-only filesystem)")
178 def torrent_error(error_str):
179 logger.critical("Error parsing the torrent metafile: %s", error_str)
182 def decode_torrent():
184 torrent_file = open(sys.argv[2], 'r')
185 data = torrent_file.read()
188 except IOError, error_str:
189 torrent_error(error_str)
192 def decode_datetime(dt):
193 from time import localtime, asctime
195 l_now = localtime(the_time)
196 return asctime(l_now)
198 def decode_list(announce):
199 return '\n'.join(l[0] for l in announce)
202 command = sys.argv[1]
203 procname = "mctorrent_" + command
206 if not g.has_key(procname):
207 logger.critical("Unknown command %s", command)
210 torrent = decode_torrent()
217 logger.exception("Error during run")