]> git.phdru.name Git - extfs.d.git/blob - torrent
511a85d891ee67969cdf57929be7e774e5943ce9
[extfs.d.git] / torrent
1 #! /usr/bin/env python
2
3 """Torrent Virtual FileSystem for Midnight Commander
4
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).
8
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.
13
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.
19
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).
25
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.
28
29 """
30
31 __version__ = "1.0.0"
32 __revision__ = "$Id$"
33 __date__ = "$Date$"
34 __author__ = "Oleg Broytman <phd@phdru.name>"
35 __copyright__ = "Copyright (C) 2010, 2011 PhiloSoft Design"
36 __license__ = "GPL"
37
38 import locale, sys, os
39 from eff_bdecode import decode
40
41 import logging
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)
46
47 if len(sys.argv) < 3:
48     logger.critical("""\
49 Torrent Virtual FileSystem for Midnight Commander version %s
50 Author: %s
51 %s
52
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__
56 )
57     sys.exit(1)
58
59
60 locale.setlocale(locale.LC_ALL, '')
61 charset = locale.getpreferredencoding()
62
63
64 def mctorrent_list():
65     """List the entire VFS"""
66
67     if 'info' not in torrent:
68         torrent_error('Info absent')
69
70     info = torrent['info']
71     if 'name' not in info:
72         torrent_error('Unknown name')
73
74     name = info['name']
75     encoding = torrent.get('encoding', None)
76
77     if 'files' in info:
78         files = info['files']
79         paths = []
80         for file in files:
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)]
97
98     meta = []
99     for name in 'comment', 'encoding', 'creation date', 'announce-list', \
100             'created by', 'announce':
101         if name in torrent:
102             if name == 'creation date':
103                 data = decode_datetime(torrent[name])
104             elif name == 'announce-list':
105                 data = decode_list(torrent[name])
106             else:
107                 data = torrent[name]
108             meta.append(('.META/' + name, len(data)))
109
110     if 'private' in info:
111         meta.append(('.META/private', 1))
112
113     if 'piece length' in info:
114         meta.append(('.META/piece length', len(str(info['piece length']))))
115
116     for name, size in paths + meta:
117         print "-r--r--r-- 1 user group %d Jan 1 00:00 %s" % (size, name)
118
119
120 def mctorrent_copyout():
121     """Extract a file from the VFS"""
122
123     torrent_filename = sys.argv[3]
124     real_filename = sys.argv[4]
125     data = None
126
127     for name in 'comment', 'encoding', 'creation date', 'announce-list', \
128             'created by', 'announce':
129         if torrent_filename == '.META/' + name:
130             if name in torrent:
131                 if name == 'creation date':
132                     data = decode_datetime(torrent[name])
133                 elif name == 'announce-list':
134                     data = decode_list(torrent[name])
135                 else:
136                     data = str(torrent[name])
137             else:
138                 torrent_error('Unknown ' + name)
139             break
140
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/'):]])
152
153     if not torrent_filename.startswith('.META/'):
154         data = ''
155
156     if data is None:
157         torrent_error('Unknown file name')
158     else:
159         outfile = open(real_filename, 'w')
160         outfile.write(data)
161         outfile.close()
162
163
164 def mctorrent_copyin():
165     """Put a file to the VFS"""
166     sys.exit("Torrent VFS doesn't support adding files (read-only filesystem)")
167
168 def mctorrent_rm():
169     """Remove a file from the VFS"""
170     sys.exit("Torrent VFS doesn't support removing files/directories (read-only filesystem)")
171
172 mctorrent_rmdir = mctorrent_rm
173
174 def mctorrent_mkdir():
175     """Create a directory in the VFS"""
176     sys.exit("Torrent VFS doesn't support creating directories (read-only filesystem)")
177
178
179 def torrent_error(error_str):
180     logger.critical("Error parsing the torrent metafile: %s", error_str)
181     sys.exit(1)
182
183 def decode_torrent():
184     try:
185         torrent_file = open(sys.argv[2], 'r')
186         data = torrent_file.read()
187         torrent_file.close()
188         return decode(data)
189     except IOError, error_str:
190         torrent_error(error_str)
191
192
193 def decode_datetime(dt):
194     from time import localtime, asctime
195     the_time = float(dt)
196     l_now = localtime(the_time)
197     return asctime(l_now)
198
199 def decode_list(announce):
200     return '\n'.join(l[0] for l in announce)
201
202
203 command = sys.argv[1]
204 procname = "mctorrent_" + command
205
206 g = globals()
207 if not g.has_key(procname):
208     logger.critical("Unknown command %s", command)
209     sys.exit(1)
210
211 torrent = decode_torrent()
212
213 try:
214     g[procname]()
215 except SystemExit:
216     raise
217 except:
218     logger.exception("Error during run")