summaryrefslogtreecommitdiffstats
path: root/BitTorrent/parsedir.py
diff options
context:
space:
mode:
Diffstat (limited to 'BitTorrent/parsedir.py')
-rw-r--r--BitTorrent/parsedir.py150
1 files changed, 150 insertions, 0 deletions
diff --git a/BitTorrent/parsedir.py b/BitTorrent/parsedir.py
new file mode 100644
index 0000000..3b86a03
--- /dev/null
+++ b/BitTorrent/parsedir.py
@@ -0,0 +1,150 @@
+# The contents of this file are subject to the BitTorrent Open Source License
+# Version 1.1 (the License). You may not copy or use this file, in either
+# source code or executable form, except in compliance with the License. You
+# may obtain a copy of the License at http://www.bittorrent.com/license/.
+#
+# Software distributed under the License is distributed on an AS IS basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+
+# Written by John Hoffman and Uoti Urpala
+
+import os
+from sha import sha
+
+from BitTorrent.bencode import bencode, bdecode
+from BitTorrent.btformats import check_message
+
+NOISY = False
+
+def parsedir(directory, parsed, files, blocked, errfunc,
+ include_metainfo=True):
+ if NOISY:
+ errfunc('checking dir')
+ dirs_to_check = [directory]
+ new_files = {}
+ new_blocked = {}
+ while dirs_to_check: # first, recurse directories and gather torrents
+ directory = dirs_to_check.pop()
+ newtorrents = False
+ try:
+ dir_contents = os.listdir(directory)
+ except (IOError, OSError), e:
+ errfunc(_("Could not read directory ") + directory)
+ continue
+ for f in dir_contents:
+ if f.endswith('.torrent'):
+ newtorrents = True
+ p = os.path.join(directory, f)
+ try:
+ new_files[p] = [(os.path.getmtime(p),os.path.getsize(p)),0]
+ except (IOError, OSError), e:
+ errfunc(_("Could not stat ") + p + " : " + str(e))
+ if not newtorrents:
+ for f in dir_contents:
+ p = os.path.join(directory, f)
+ if os.path.isdir(p):
+ dirs_to_check.append(p)
+
+ new_parsed = {}
+ to_add = []
+ added = {}
+ removed = {}
+ # files[path] = [(modification_time, size), hash], hash is 0 if the file
+ # has not been successfully parsed
+ for p,v in new_files.items(): # re-add old items and check for changes
+ oldval = files.get(p)
+ if oldval is None: # new file
+ to_add.append(p)
+ continue
+ h = oldval[1]
+ if oldval[0] == v[0]: # file is unchanged from last parse
+ if h:
+ if p in blocked: # parseable + blocked means duplicate
+ to_add.append(p) # other duplicate may have gone away
+ else:
+ new_parsed[h] = parsed[h]
+ new_files[p] = oldval
+ else:
+ new_blocked[p] = None # same broken unparseable file
+ continue
+ if p not in blocked and h in parsed: # modified; remove+add
+ if NOISY:
+ errfunc(_("removing %s (will re-add)") % p)
+ removed[h] = parsed[h]
+ to_add.append(p)
+
+ to_add.sort()
+ for p in to_add: # then, parse new and changed torrents
+ new_file = new_files[p]
+ v = new_file[0]
+ if new_file[1] in new_parsed: # duplicate
+ if p not in blocked or files[p][0] != v:
+ errfunc(_("**warning** %s is a duplicate torrent for %s") %
+ (p, new_parsed[new_file[1]]['path']))
+ new_blocked[p] = None
+ continue
+
+ if NOISY:
+ errfunc('adding '+p)
+ try:
+ ff = open(p, 'rb')
+ d = bdecode(ff.read())
+ check_message(d)
+ h = sha(bencode(d['info'])).digest()
+ new_file[1] = h
+ if new_parsed.has_key(h):
+ errfunc(_("**warning** %s is a duplicate torrent for %s") %
+ (p, new_parsed[h]['path']))
+ new_blocked[p] = None
+ continue
+
+ a = {}
+ a['path'] = p
+ f = os.path.basename(p)
+ a['file'] = f
+ i = d['info']
+ l = 0
+ nf = 0
+ if i.has_key('length'):
+ l = i.get('length',0)
+ nf = 1
+ elif i.has_key('files'):
+ for li in i['files']:
+ nf += 1
+ if li.has_key('length'):
+ l += li['length']
+ a['numfiles'] = nf
+ a['length'] = l
+ a['name'] = i.get('name', f)
+ def setkey(k, d = d, a = a):
+ if d.has_key(k):
+ a[k] = d[k]
+ setkey('failure reason')
+ setkey('warning message')
+ setkey('announce-list')
+ if include_metainfo:
+ a['metainfo'] = d
+ except:
+ errfunc(_("**warning** %s has errors") % p)
+ new_blocked[p] = None
+ continue
+ try:
+ ff.close()
+ except:
+ pass
+ if NOISY:
+ errfunc(_("... successful"))
+ new_parsed[h] = a
+ added[h] = a
+
+ for p,v in files.iteritems(): # and finally, mark removed torrents
+ if p not in new_files and p not in blocked:
+ if NOISY:
+ errfunc(_("removing %s") % p)
+ removed[v[1]] = parsed[v[1]]
+
+ if NOISY:
+ errfunc(_("done checking"))
+ return (new_parsed, new_files, new_blocked, added, removed)