2 """Torrent Virtual FileSystem for Midnight Commander
4 The script requires Midnight Commander 3.1+
5 (http://www.midnight-commander.org/), Python 2.4+ (http://www.python.org/),
6 module eff_bdecode.py (http://effbot.org/zone/bencode.htm).
8 For mc 4.7+ put the script in $HOME/.mc/extfs.d.
9 For older versions put it in /usr/[local/][lib|share]/mc/extfs
10 and add a line "torrent" to the /usr/[local/][lib|share]/mc/extfs/extfs.ini.
11 Make the script executable.
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 script doesn't check if the torrent consists of a .META file or
23 directory (quite unlikely).
25 Date/time for all files is set to midnight of the 1st January of the current
26 year. The filesystem is, naturally, read-only.
31 __author__ = "Oleg Broytman <phd@phdru.name>"
32 __copyright__ = "Copyright (C) 2010-2012 PhiloSoft Design"
35 import locale, sys, os
36 from eff_bdecode import decode
39 logger = logging.getLogger('torrent-mcextfs')
40 log_err_handler = logging.StreamHandler(sys.stderr)
41 logger.addHandler(log_err_handler)
42 logger.setLevel(logging.INFO)
46 Torrent Virtual FileSystem for Midnight Commander version %s
50 This is not a program. Put the script in /usr/[local/][lib|share]/mc/extfs.
51 For more information read the source!""",
52 __version__, __author__, __copyright__
57 locale.setlocale(locale.LC_ALL, '')
58 charset = locale.getpreferredencoding()
62 """List the entire VFS"""
64 if 'info' not in torrent:
65 torrent_error('Info absent')
67 info = torrent['info']
68 if 'name' not in info:
69 torrent_error('Unknown name')
72 encoding = torrent.get('encoding', None)
78 if 'path' not in file:
79 torrent_error('Unknown path')
80 if 'length' not in file:
81 torrent_error('Unknown length')
82 path = '/'.join([name] + file['path'])
83 if charset and encoding and (charset != encoding):
84 path = path.decode(encoding, 'replace').encode(charset, 'replace')
85 length = file['length']
86 paths.append((path, length))
87 else: # One-file torrent
88 if 'length' not in info:
89 torrent_error('Unknown length')
90 length = info['length']
91 if charset and encoding and (charset != encoding):
92 name = name.decode(encoding, 'replace').encode(charset, 'replace')
93 paths = [(name, length)]
96 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
97 'created by', 'announce':
99 if name == 'creation date':
100 data = decode_datetime(torrent[name])
101 elif name == 'announce-list':
102 data = decode_list(torrent[name])
105 meta.append(('.META/' + name, len(data)))
107 if 'private' in info:
108 meta.append(('.META/private', 1))
110 if 'piece length' in info:
111 meta.append(('.META/piece length', len(str(info['piece length']))))
113 for name, size in paths + meta:
114 print "-r--r--r-- 1 user group %d Jan 1 00:00 %s" % (size, name)
117 def mctorrent_copyout():
118 """Extract a file from the VFS"""
120 torrent_filename = sys.argv[3]
121 real_filename = sys.argv[4]
124 for name in 'comment', 'encoding', 'creation date', 'announce-list', \
125 'created by', 'announce':
126 if torrent_filename == '.META/' + name:
128 if name == 'creation date':
129 data = decode_datetime(torrent[name])
130 elif name == 'announce-list':
131 data = decode_list(torrent[name])
133 data = str(torrent[name])
135 torrent_error('Unknown ' + name)
138 if torrent_filename in ('.META/private', '.META/piece length'):
139 if 'info' not in torrent:
140 torrent_error('Info absent')
141 info = torrent['info']
142 if torrent_filename == '.META/private':
143 if 'private' not in info:
144 torrent_error('Info absent')
145 if torrent_filename == '.META/piece length':
146 if 'piece length' not in info:
147 torrent_error('Info absent')
148 data = str(info[torrent_filename[len('.META/'):]])
150 if not torrent_filename.startswith('.META/'):
154 torrent_error('Unknown file name')
156 outfile = open(real_filename, 'w')
161 def mctorrent_copyin():
162 """Put a file to the VFS"""
163 sys.exit("Torrent VFS doesn't support adding files (read-only filesystem)")
166 """Remove a file from the VFS"""
167 sys.exit("Torrent VFS doesn't support removing files/directories (read-only filesystem)")
169 mctorrent_rmdir = mctorrent_rm
171 def mctorrent_mkdir():
172 """Create a directory in the VFS"""
173 sys.exit("Torrent VFS doesn't support creating directories (read-only filesystem)")
176 def torrent_error(error_str):
177 logger.critical("Error parsing the torrent metafile: %s", error_str)
180 def decode_torrent():
182 torrent_file = open(sys.argv[2], 'r')
183 data = torrent_file.read()
186 except IOError, error_str:
187 torrent_error(error_str)
190 def decode_datetime(dt):
191 from time import localtime, asctime
193 l_now = localtime(the_time)
194 return asctime(l_now)
196 def decode_list(announce):
197 return '\n'.join(l[0] for l in announce)
200 command = sys.argv[1]
201 procname = "mctorrent_" + command
204 if not g.has_key(procname):
205 logger.critical("Unknown command %s", command)
208 torrent = decode_torrent()
215 logger.exception("Error during run")