forked from jimrollenhagen/pywhatauto
-
Notifications
You must be signed in to change notification settings - Fork 0
/
torrentparser.py
executable file
·223 lines (205 loc) · 8.84 KB
/
torrentparser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from __future__ import with_statement
from __future__ import division
import re, os, hashlib
def main():
dir = '/home/blubba/.config/deluge/state'
print('%-50s %-3s %10.2s %-5s' %('Infohash','M','MBs','Name'))
for file in os.listdir(dir):
root, ext = os.path.splitext(file)
if ext and ext == '.torrent':
x = torrentparser(filename=os.path.join(dir,file))
if root == x.infohash():
m = 'Y'
else:
m = 'N'
print('%-50s %-3s %10.2f %-5s' %(root,m,x.mbsize(),x.name()))
class torrentparser(object):
'''
Universal torrent writing and parsing class
'''
def __init__(self, debug = False, filename=False, content=False):
'''Takes a string with the path to the torrent file in filename,
or the content of a torrent file in content (as a string).
Runs the parses on it.
Returns a string if the file could not be opened/read,
raises SyntaxError on parsing errors.'''
self.torrentfile = None
self.debug = debug
self.dictionary = None
if filename or content:
return self.parse(filename=filename, content=content)
def parse(self, filename=False, content=False):
if filename:
try:
with open(filename, 'rb') as f:
self.torrentfile = f.read()
except IOError:
print('Cannot open file ' + filename)
raise
elif content:
self.torrentfile = content
else:
return 'No torrent data given'
self.decode()
def decode(self):
'''Decodes the raw bencoded file into self.dictionary. Raises SyntaxErrors on errors.'''
reg = re.compile('^(\d+)?(?:i(\d+)e)?(l?)(d)?(e)?', re.I + re.U) #strings, ints, lists, dicts
t = self.torrentfile
depth = [None]
l = []
i= 0
while i < len(t):
m = reg.match(t[i:])
if self.debug: print 'left: ' + repr(l)
ty = type(depth[-1])
if m:
if m.group(1): #String
string = t[i + len(m.group(1)) + 1 : i + int(m.group(1)) + len(m.group(1)) + 1]
if ty == type(list()):
if self.debug: print('Append %s to %s' %(repr(string),repr(depth[-1])))
depth[-1].append(string)
elif ty == type(dict()):
if l[-1]:
if l[-1] == 'pieces' and self.debug:
string = 'Here are all the pieces'
if self.debug: print('Map %s -> String: %s' %(l[-1],repr(string)))
depth[-1].update({l[-1] : string})
l[-1] = None
else:
if self.debug: print('Add left: String: %s' %(repr(string)))
l[-1] = string
else:
raise SyntaxError('Lonely String found: %s' %string)
i += int(m.group(1)) + len(m.group(1)) + 1
elif m.group(2): #Integer
integer = int(m.group(2))
#print('Integer: ' + str(integer))
if ty == type(list()):
depth[-1].append(integer)
elif ty == type(dict()):
if l[-1]:
if self.debug: print('Map %s -> Integer: %s' %(l[-1],repr(integer)))
depth[-1].update({l[-1] : integer})
l[-1] = None
else:
if self.debug: print('Add left: Integer: %s' %(repr(integer)))
l[-1] = str(integer)
else:
raise SyntaxError('Lonely Integer found: %s' %str(integer))
i += len(str(m.group(2))) + 2
elif m.group(3): #List
if self.debug: print('List found')
depth.append(list())
i+=1
elif m.group(4):
if self.debug: print('Dictionary found')
depth.append(dict())
l.append(None)
i+=1
elif m.group(5):
if type(depth[-2]) == type(dict()):
if ty == type(dict()):
if self.debug: print('Dictionary ended: Map %s -> %s' %(l[-2],repr(depth[-1])))
depth[-2].update({l[-2]: depth[-1]})
l[-2] = None
del l[-1]
else:
if self.debug: print('List ended: Map %s -> %s' %(l[-1],repr(depth[-1])))
depth[-2].update({l[-1]: depth[-1]})
l[-1] = None
elif type(depth[-2]) == type(list()):
if self.debug: print('List ended: Append %s to %s' %(repr(depth[-1]),repr(depth[-2])))
depth[-2].append(depth[-1])
if ty == type(dict()):
del l[-1]
else:
if self.debug: print repr(depth)
self.dictionary = depth[-1]
del depth[-1]
i+=1
else:
if i+1 == len(t):
i+=1
else:
raise SyntaxError('Torrent files is not formated, i: %d/%d remaining content: %s' %(i,len(t),repr(t[i:])))
else:
raise SyntaxError('Torrent files is not formated, i: %d/%d remaining content: %s' %(i,len(t),repr(t[i:])))
def encode(self, dictionary=None):
'''Returns the bencoded version of the dictionary'''
if not dictionary:
dictionary = self.dictionary
ben = 'd'
for key in sorted(dictionary.iterkeys(),key=str.lower):
t = type(dictionary[key])
ben += "%d:%s"%(len(key),key)
if t == type(dict()):
ben += self.encode(dictionary[key])
elif t == type(str()):
ben += '%d:%s' %(len(dictionary[key]), dictionary[key])
elif t == type(int()):
ben += 'i%de' %dictionary[key]
elif t == type(list()):
ben += self.encodelist(dictionary[key])
else:
raise SyntaxError('Mallformated dictionary as it seems.')
return ben + 'e'
def encodelist(self, list):
'''Returns the bencoded version of the list'''
ben = 'l'
for key in list:
t = type(key)
if t == type(dict()):
ben += self.encode(key)
elif t == type(str()):
ben += '%d:%s' %(len(key), key)
elif t == type(int()):
ben += 'i%de' %key
elif t == type(list()):
ben += self.encodelist(key)
else:
raise SyntaxError('Mallformated dictionary as it seems.')
return ben + 'e'
def pretty(self):
'''Prints a pretty version of the dictionary'''
import pprint
pprint.pprint(self.dictionary)
def files(self):
'''Returns a list of all files.'''
f = []
if 'files' in self.dictionary['info']:
for ff in self.dictionary['info']['files']:
path = self.dictionary['info']['name']
for fff in ff['path']:
path = os.path.join(path,fff)
f.append(path)
if self.debug: print(path)
elif 'name' in self.dictionary['info']:
f.append(self.dictionary['info']['name'])
if self.debug: print f[0]
return(f)
def infohash(self):
'''Returns the sha1hash of the infodb'''
return hashlib.sha1(self.encode(self.dictionary['info'])).hexdigest()
def mbsize(self):
'''Returns the size in MB'''
return self.rawsize() / (1024*1024)
def gbsize(self):
'''Returns the size in GB'''
return self.rawsize() / (1024*1024*1024)
def name(self):
'''Returns the name of the torrent, as specified in the info'''
if 'name' in self.dictionary['info']:
return self.dictionary['info']['name']
def rawsize(self):
'''Returns the size in bytes'''
size = 0
if 'files' in self.dictionary['info']:
for ff in self.dictionary['info']['files']:
size += ff['length']
elif 'name' in self.dictionary['info']:
size += self.dictionary['info']['length']
return size
if __name__ == "__main__":
main()