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 For mc 4.7+ put the script in $HOME/.mc/extfs.d.
10 For older versions put it in /usr/[local/][lib|share]/mc/extfs
11 and add a line "torrent" to the /usr/[local/][lib|share]/mc/extfs/extfs.ini.
12 Make the script executable.
14 Run this "cd" command in the Midnight Commander (in the "bindings" file the
15 command is "%cd"): cd file#torrent, where "file" is the name of your torrent
16 metafile. The VFS lists all files and directories from the torrent metafile;
17 all files appear empty, of course, but the sizes are shown. Filenames are
18 reencoded from the metafile's encoding to the current locale.
20 Along with the files/directories in the torrent metafile the VFS also presents
21 meta information - in the form of files in .META directory. The size and
22 contents of these files are taken from the corresponding fields in the torrent
23 metafile. The script doesn't check if the torrent consists of a .META file or
24 directory (quite unlikely).
26 Date/time for all files is set to midnight of the 1st January of the current
27 year. The filesystem is, naturally, read-only.
34 __author__ = "Oleg Broytman <phd@phdru.name>"
35 __copyright__ = "Copyright (C) 2010, 2011 PhiloSoft Design"
38 import locale, sys, os
39 from eff_bdecode import decode
42 logger = logging.getLogger('torrent-mcextfs')
43 log_err_handler = logging.StreamHandler(sys.stderr)
44 logger.addHandler(log_err_handler)
45 logger.setLevel(logging.INFO)
49 Torrent Virtual FileSystem for Midnight Commander version %s
53 This is not a program. Put the script in /usr/[local/][lib|share]/mc/extfs.
54 For more information read the source!""",
55 __version__, __author__, __copyright__
60 locale.setlocale(locale.LC_ALL, '')
61 charset = locale.getpreferredencoding()
65 """List the entire VFS"""
67 if 'info' not in torrent:
68 torrent_error('Info absent')
70 info = torrent['info']
71 if 'name' not in info:
72 torrent_error('Unknown name')
75 encoding = torrent.get('encoding', None)
81 if 'path' not in file:
82 torrent_error('Unknown path')
83 if 'length' not in file:
84 torrent_error('Unknown length')
85 path = '/'.join([name] + file['path'])
86 if charset and encoding and (charset != encoding):
87 path = path.decode(encoding, 'replace').encode(charset, 'replace')
88 length = file['length']
89 paths.append((path, length))
90 else: # One-file torrent
91 if 'length' not in info:
92 torrent_error('Unknown length')
93 length = info['length']
94 if charset and encoding and (charset != encoding):
95 name = name.decode(encoding, 'replace').encode(charset, 'replace')
96 paths = [(name, length)]
99 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
100 'created by', 'announce':
102 if name == 'creation date':
103 data = decode_datetime(torrent[name])
104 elif name == 'announce-list':
105 data = decode_list(torrent[name])
108 meta.append(('.META/' + name, len(data)))
110 if 'private' in info:
111 meta.append(('.META/private', 1))
113 if 'piece length' in info:
114 meta.append(('.META/piece length', len(str(info['piece length']))))
116 for name, size in paths + meta:
117 print "-r--r--r-- 1 user group %d Jan 1 00:00 %s" % (size, name)
120 def mctorrent_copyout():
121 """Extract a file from the VFS"""
123 torrent_filename = sys.argv[3]
124 real_filename = sys.argv[4]
127 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
128 'created by', 'announce':
129 if torrent_filename == '.META/' + name:
131 if name == 'creation date':
132 data = decode_datetime(torrent[name])
133 elif name == 'announce-list':
134 data = decode_list(torrent[name])
136 data = str(torrent[name])
138 torrent_error('Unknown ' + name)
141 if torrent_filename in ('.META/private', '.META/piece length'):
142 if 'info' not in torrent:
143 torrent_error('Info absent')
144 info = torrent['info']
145 if torrent_filename == '.META/private':
146 if 'private' not in info:
147 torrent_error('Info absent')
148 if torrent_filename == '.META/piece length':
149 if 'piece length' not in info:
150 torrent_error('Info absent')
151 data = str(info[torrent_filename[len('.META/'):]])
153 if not torrent_filename.startswith('.META/'):
157 torrent_error('Unknown file name')
159 outfile = open(real_filename, 'w')
164 def mctorrent_copyin():
165 """Put a file to the VFS"""
166 sys.exit("Torrent VFS doesn't support adding files (read-only filesystem)")
169 """Remove a file from the VFS"""
170 sys.exit("Torrent VFS doesn't support removing files/directories (read-only filesystem)")
172 mctorrent_rmdir = mctorrent_rm
174 def mctorrent_mkdir():
175 """Create a directory in the VFS"""
176 sys.exit("Torrent VFS doesn't support creating directories (read-only filesystem)")
179 def torrent_error(error_str):
180 logger.critical("Error parsing the torrent metafile: %s", error_str)
183 def decode_torrent():
185 torrent_file = open(sys.argv[2], 'r')
186 data = torrent_file.read()
189 except IOError, error_str:
190 torrent_error(error_str)
193 def decode_datetime(dt):
194 from time import localtime, asctime
196 l_now = localtime(the_time)
197 return asctime(l_now)
199 def decode_list(announce):
200 return '\n'.join(l[0] for l in announce)
203 command = sys.argv[1]
204 procname = "mctorrent_" + command
207 if not g.has_key(procname):
208 logger.critical("Unknown command %s", command)
211 torrent = decode_torrent()
218 logger.exception("Error during run")