# Written by Arno Bakker, Diego Rabioli
# see LICENSE.txt for license information
#
# TODO:
# - Switch to SIMPLE+METADATA query
#
# - adjust SIMPLE+METADATA such that it returns P2PURLs if possible.
# - DO NOT SAVE P2PURLs as .torrent, put in 'torrent_file_name' field in DB.
#
# - Implement continuous dump of results to JS. I.e. push sorting and rendering to browser.
# * One option is RFC5023: Atom Pub Proto, $10.1 "Collecting Partial Lists" I.e.
# return a partial list and add a >sys.stderr,"searchmap: Parsed",o
qdict = cgi.parse_qs(o.query)
print >>sys.stderr,"searchmap: qdict",qdict
searchstr = qdict['q'][0]
searchstr = searchstr.strip()
collection = qdict['collection'][0]
metafeedurl = qdict['metafeed'][0]
print >>sys.stderr,"searchmap: searchstr",`searchstr`
# Garbage collect:
self.id2hits.garbage_collect_timestamp_smaller(time.time() - HITS_TIMEOUT)
if collection == "metafeed":
if not self.check_reload_metafeed(metafeedurl):
return {'statuscode':504, 'statusmsg':'504 MetaFeed server did not respond'}
return self.process_search_metafeed(searchstr)
else:
return self.process_search_p2p(searchstr)
def process_search_metafeed(self,searchstr):
""" Search for hits in the ATOM feeds we got from the meta feed """
allhits = []
for feedurl in self.metafp.get_feedurls():
feedp = FeedParser(feedurl)
try:
feedp.parse()
except:
# TODO: return 504 gateway error if none of the feeds return anything
print_exc()
hits = feedp.search(searchstr)
allhits.extend(hits)
for hitentry in allhits:
titleelement = hitentry.find('{http://www.w3.org/2005/Atom}title')
print >>sys.stderr,"bg: search: meta: Got hit",titleelement.text
id = str(random.random())[2:]
atomurlpathprefix = URLPATH_HITS_PREFIX+'/'+str(id)
atomxml = feedhits2atomxml(allhits,searchstr,atomurlpathprefix)
atomstream = StringIO(atomxml)
atomstreaminfo = { 'statuscode':200,'mimetype': 'application/atom+xml', 'stream': atomstream, 'length': len(atomxml)}
return atomstreaminfo
def process_search_p2p(self,searchstr):
""" Search for hits in local database and perform remote query.
EXPERIMENTAL: needs peers with SIMPLE+METADATA query support.
"""
# Initially, searchstr = keywords
keywords = searchstr.split()
id = str(random.random())[2:]
self.id2hits.add_query(id,searchstr,time.time())
# Parallel: initiate remote query
q = 'SIMPLE '+searchstr
print >>sys.stderr,"bg: search: p2p: Remote query for",q
got_remote_hits_lambda = lambda permid,query,remotehits:self.sesscb_got_remote_hits(id,permid,query,remotehits)
self.st = time.time()
self.session.query_connected_peers(q,got_remote_hits_lambda,max_peers_to_query=20)
# Query local DB while waiting
torrent_db = self.session.open_dbhandler(NTFY_TORRENTS)
localdbhits = torrent_db.searchNames(keywords)
print >>sys.stderr,"bg: Local hits",len(localdbhits)
self.session.close_dbhandler(torrent_db)
# Convert list to dict keyed by infohash
localhits = localdbhits2hits(localdbhits)
self.id2hits.add_hits(id,localhits)
# TODO ISSUE: incremental display of results to user? How to implement this?
atomurlpathprefix = URLPATH_HITS_PREFIX+'/'+str(id)
nextlinkpath = atomurlpathprefix
atomhits = hits2atomhits(localhits,atomurlpathprefix)
atomxml = atomhits2atomxml(atomhits,searchstr,atomurlpathprefix,nextlinkpath=nextlinkpath)
atomstream = StringIO(atomxml)
atomstreaminfo = { 'statuscode':200,'mimetype': 'application/atom+xml', 'stream': atomstream, 'length': len(atomxml)}
return atomstreaminfo
def sesscb_got_remote_hits(self,id,permid,query,remotehits):
# Called by SessionCallback thread
try:
et = time.time()
diff = et - self.st
print >>sys.stderr,"bg: sesscb_got_remote_hits",len(remotehits),"after",diff
hits = remotehits2hits(remotehits)
print >>sys.stderr,"bg: sesscb_got_remote_hits: common",len(hits)
# TEST
"""
newhits = {}
for infohash in hits.keys()[0:1]:
hit = hits[infohash]
newhits[infohash] = hit
"""
newhits = hits
self.id2hits.add_hits(id,newhits)
atomurlpathprefix = URLPATH_HITS_PREFIX+'/'+str(id)
# Inform Plugin of feed.
#self.ic.start_searchresults(atomurlpathprefix)
except:
print_exc()
def check_reload_metafeed(self,metafeedurl):
if self.metafeedurl is None or self.metafeedurl != metafeedurl:
self.metafp = MetaFeedParser(metafeedurl)
try:
self.metafp.parse() # TODO: offload to separate thread?
print >>sys.stderr,"bg: search: meta: Found feeds",self.metafp.get_feedurls()
self.metafeedurl = metafeedurl
except:
print_exc()
return False
return True
def localdbhits2hits(localdbhits):
hits = {}
for dbhit in localdbhits:
localhit = {}
localhit['hittype'] = "localdb"
localhit.update(dbhit)
infohash = dbhit['infohash'] # convenient to also have in record
hits[infohash] = localhit
return hits
def remotehits2hits(remotehits):
hits = {}
for infohash,hit in remotehits.iteritems():
#print >>sys.stderr,"remotehit2hits: keys",hit.keys()
remotehit = {}
remotehit['hittype'] = "remote"
#remotehit['query_permid'] = permid # Bit of duplication, ignore
remotehit['infohash'] = infohash # convenient to also have in record
remotehit.update(hit)
# HACK: Create fake torrent file
if not 'metadata' in hit:
metatype = TSTREAM_MIME_TYPE
metadata = hack_make_default_merkletorrent(hit['content_name'])
remotehit['metatype'] = metatype
remotehit['metadata'] = metadata
hits[infohash] = remotehit
return hits
class Query2HitsMap:
""" Stores localdb and remotehits in common hits format, i.e., each
hit has a 'hittype' attribute that tells which type it is (localdb or remote).
This Query2HitsMap is passed to the Hits2AnyPathMapper, which is connected
to the internal HTTP server.
The HTTP server will then forward all "/hits" GET requests to this mapper.
The mapper then dynamically generates the required contents from the stored
hits, e.g. an ATOM feed, MPEG7 description, .torrent file and thumbnail
images from the torrent.
"""
def __init__(self):
self.lock = RLock()
self.d = {}
def add_query(self,id,searchstr,timestamp):
if DEBUG:
print >>sys.stderr,"q2h: lock1",id
self.lock.acquire()
try:
qrec = self.d.get(id,{})
qrec['searchstr'] = searchstr
qrec['timestamp'] = timestamp
qrec['hitlist'] = {}
self.d[id] = qrec
finally:
if DEBUG:
print >>sys.stderr,"q2h: unlock1"
self.lock.release()
def add_hits(self,id,hits):
if DEBUG:
print >>sys.stderr,"q2h: lock2",id,len(hits)
self.lock.acquire()
try:
qrec = self.d[id]
qrec['hitlist'].update(hits)
finally:
if DEBUG:
print >>sys.stderr,"q2h: unlock2"
self.lock.release()
def get_hits(self,id):
if DEBUG:
print >>sys.stderr,"q2h: lock3",id
self.lock.acquire()
try:
qrec = self.d[id]
return copy.copy(qrec['hitlist']) # return shallow copy
finally:
if DEBUG:
print >>sys.stderr,"q2h: unlock3"
self.lock.release()
def get_searchstr(self,id):
if DEBUG:
print >>sys.stderr,"q2h: lock4"
self.lock.acquire()
try:
qrec = self.d[id]
return qrec['searchstr']
finally:
if DEBUG:
print >>sys.stderr,"q2h: unlock4"
self.lock.release()
def garbage_collect_timestamp_smaller(self,timethres):
self.lock.acquire()
try:
idlist = []
for id,qrec in self.d.iteritems():
if qrec['timestamp'] < timethres:
idlist.append(id)
for id in idlist:
del self.d[id]
finally:
self.lock.release()
class Hits2AnyPathMapper(AbstractPathMapper):
""" See Query2Hits description """
def __init__(self,session,id2hits):
self.session = session
self.id2hits = id2hits
def get(self,urlpath):
"""
Possible paths:
/hits/id -> ATOM feed
/hits/id/infohash.xml -> MPEG 7
/hits/id/infohash.tstream -> Torrent file
/hits/id/infohash.tstream/thumbnail -> Thumbnail
"""
if DEBUG:
print >>sys.stderr,"hitsmap: Got",urlpath
if not urlpath.startswith(URLPATH_HITS_PREFIX):
return streaminfo404()
paths = urlpath.split('/')
if len(paths) < 3:
return streaminfo404()
id = paths[2]
if len(paths) == 3:
# ATOM feed
searchstr = self.id2hits.get_searchstr(id)
hits = self.id2hits.get_hits(id)
if DEBUG:
print >>sys.stderr,"hitsmap: Found",len(hits),"hits"
atomhits = hits2atomhits(hits,urlpath)
if DEBUG:
print >>sys.stderr,"hitsmap: Found",len(atomhits),"atomhits"
atomxml = atomhits2atomxml(atomhits,searchstr,urlpath)
#if DEBUG:
# print >>sys.stderr,"hitsmap: atomstring is",`atomxml`
atomstream = StringIO(atomxml)
atomstreaminfo = { 'statuscode':200,'mimetype': 'application/atom+xml', 'stream': atomstream, 'length': len(atomxml)}
return atomstreaminfo
elif len(paths) >= 4:
# Either NS Metadata, Torrent file, or thumbnail
urlinfohash = paths[3]
print >>sys.stderr,"hitsmap: path3 is",urlinfohash
if urlinfohash.endswith(URLPATH_TORRENT_POSTFIX):
# Torrent file, or thumbnail
coded = urlinfohash[:-len(URLPATH_TORRENT_POSTFIX)]
infohash = urlpath2infohash(coded)
else:
# NS Metadata / MPEG7
coded = urlinfohash[:-len(URLPATH_NSMETA_POSTFIX)]
infohash = urlpath2infohash(coded)
# Check if hit:
hits = self.id2hits.get_hits(id)
print >>sys.stderr,"hitsmap: meta: Found",len(hits),"hits"
hit = hits.get(infohash,None)
if hit is not None:
if len(paths) == 5:
# Thumbnail
return self.get_thumbstreaminfo(infohash,hit)
elif urlinfohash.endswith(URLPATH_TORRENT_POSTFIX):
# Torrent file
return self.get_torrentstreaminfo(infohash,hit)
else:
# NS Metadata / MPEG7
hiturlpathprefix = URLPATH_HITS_PREFIX+'/'+id
return self.get_nsmetastreaminfo(infohash,hit,hiturlpathprefix,urlpath)
return streaminfo404()
def get_torrentstreaminfo(self,infohash,hit):
if DEBUG:
print >>sys.stderr,"hitmap: get_torrentstreaminfo",infohash2urlpath(infohash)
torrent_db = self.session.open_dbhandler(NTFY_TORRENTS)
try:
if hit['hittype'] == "localdb":
dbhit = torrent_db.getTorrent(infohash,include_mypref=False)
colltorrdir = self.session.get_torrent_collecting_dir()
filepath = os.path.join(colltorrdir,dbhit['torrent_file_name'])
# Return stream that contains torrent file
stream = open(filepath,"rb")
length = os.path.getsize(filepath)
torrentstreaminfo = {'statuscode':200,'mimetype':TSTREAM_MIME_TYPE,'stream':stream,'length':length}
return torrentstreaminfo
else:
if hit['metatype'] == URL_MIME_TYPE:
# Shouldn't happen, P2PURL should be embedded in atom
return streaminfo404()
else:
stream = StringIO(hit['metadata'])
length = len(hit['metadata'])
torrentstreaminfo = {'statuscode':200,'mimetype':TSTREAM_MIME_TYPE,'stream':stream,'length':length}
return torrentstreaminfo
finally:
self.session.close_dbhandler(torrent_db)
def get_thumbstreaminfo(self,infohash,hit):
if DEBUG:
print >>sys.stderr,"hitmap: get_thumbstreaminfo",infohash2urlpath(infohash)
torrent_db = self.session.open_dbhandler(NTFY_TORRENTS)
try:
if hit['hittype'] == "localdb":
dbhit = torrent_db.getTorrent(infohash,include_mypref=False)
colltorrdir = self.session.get_torrent_collecting_dir()
filepath = os.path.join(colltorrdir,dbhit['torrent_file_name'])
tdef = TorrentDef.load(filepath)
(thumbtype,thumbdata) = tdef.get_thumbnail()
return self.create_thumbstreaminfo(thumbtype,thumbdata)
else:
if hit['metatype'] == URL_MIME_TYPE:
# Shouldn't happen, not thumb in P2PURL
return streaminfo404()
else:
if DEBUG:
print >>sys.stderr,"hitmap: get_thumbstreaminfo: looking for thumb in remote hit"
metainfo = bdecode(hit['metadata'])
tdef = TorrentDef.load_from_dict(metainfo)
(thumbtype,thumbdata) = tdef.get_thumbnail()
return self.create_thumbstreaminfo(thumbtype,thumbdata)
finally:
self.session.close_dbhandler(torrent_db)
def create_thumbstreaminfo(self,thumbtype,thumbdata):
if thumbtype is None:
return streaminfo404()
else:
# Return stream that contains thumb
stream = StringIO(thumbdata)
length = len(thumbdata)
thumbstreaminfo = {'statuscode':200,'mimetype':thumbtype,'stream':stream,'length':length}
return thumbstreaminfo
def get_nsmetastreaminfo(self,infohash,hit,hiturlpathprefix,hitpath):
colltorrdir = self.session.get_torrent_collecting_dir()
nsmetahit = hit2nsmetahit(hit,hiturlpathprefix,colltorrdir)
if DEBUG:
print >>sys.stderr,"hitmap: get_nsmetastreaminfo: nsmetahit is",`nsmetahit`
nsmetarepr = nsmetahit2nsmetarepr(nsmetahit,hitpath)
nsmetastream = StringIO(nsmetarepr)
nsmetastreaminfo = { 'statuscode':200,'mimetype': 'text/xml', 'stream': nsmetastream, 'length': len(nsmetarepr)}
return nsmetastreaminfo
def infohash2urlpath(infohash):
if len(infohash) != 20:
raise ValueError("infohash len 20 !=" + str(len(infohash)))
hex = binascii.hexlify(infohash)
if len(hex) != 40:
raise ValueError("hex len 40 !=" + str(len(hex)))
return hex
def urlpath2infohash(hex):
if len(hex) != 40:
raise ValueError("hex len 40 !=" + str(len(hex)) + " " + hex)
infohash = binascii.unhexlify(hex)
if len(infohash) != 20:
raise ValueError("infohash len 20 !=" + str(len(infohash)))
return infohash
def hits2atomhits(hits,urlpathprefix):
atomhits = {}
for infohash,hit in hits.iteritems():
if hit['hittype'] == "localdb":
atomhit = localdbhit2atomhit(hit,urlpathprefix)
atomhits[infohash] = atomhit
else:
atomhit = remotehit2atomhit(hit,urlpathprefix)
atomhits[infohash] = atomhit
return atomhits
def localdbhit2atomhit(dbhit,urlpathprefix):
atomhit = {}
atomhit['title'] = htmlfilter(unicode2iri(dbhit['name']))
atomhit['summary'] = htmlfilter(unicode2iri(dbhit['comment']))
if dbhit['thumbnail']:
urlpath = urlpathprefix+'/'+infohash2urlpath(dbhit['infohash'])+URLPATH_TORRENT_POSTFIX+URLPATH_THUMBNAIL_POSTFIX
atomhit['p2pnext:image'] = urlpath
return atomhit
def remotehit2atomhit(remotehit,urlpathprefix):
# TODO: make RemoteQuery return full DB schema of TorrentDB
#print >>sys.stderr,"remotehit2atomhit: keys",remotehit.keys()
atomhit = {}
atomhit['title'] = htmlfilter(remotehit['content_name'])
atomhit['summary'] = "Seeders: "+str(remotehit['seeder'])+" Leechers: "+str(remotehit['leecher'])
if remotehit['metatype'] != URL_MIME_TYPE:
# TODO: thumbnail, see if we can detect presence (see DB schema remark).
# Now we assume it's always there if not P2PURL
urlpath = urlpathprefix+'/'+infohash2urlpath(remotehit['infohash'])+URLPATH_TORRENT_POSTFIX+URLPATH_THUMBNAIL_POSTFIX
atomhit['p2pnext:image'] = urlpath
return atomhit
def htmlfilter(s):
""" Escape characters to which HTML parser is sensitive """
if s is None:
return ""
news = s
news = news.replace('&','&')
news = news.replace('<','<')
news = news.replace('>','>')
return news
def atomhits2atomxml(atomhits,searchstr,urlpathprefix,nextlinkpath=None):
# TODO: use ElementTree parser here too, see AtomFeedParser:feedhits2atomxml
atom = ''
atom += '\n'
atom += '\n'
atom += ' Hits for '+searchstr+'\n'
atom += ' \n'
if nextlinkpath:
atom += ' \n'
atom += ' \n'
atom += ' NSSA\n'
atom += ' \n'
atom += ' urn:nssa\n'
atom += ' '+now2formatRFC3339()+'\n'
#atom += '\n' # TODO
for infohash,hit in atomhits.iteritems():
urlinfohash = infohash2urlpath(infohash)
hitpath = urlpathprefix+'/'+urlinfohash+URLPATH_NSMETA_POSTFIX
atom += ' \n'
atom += ' '+hit['title']+'\n'
atom += ' \n'
atom += ' urn:nssa-'+urlinfohash+'\n'
atom += ' '+now2formatRFC3339()+'\n'
if hit['summary'] is not None:
atom += ' '+hit['summary']+'\n'
if 'p2pnext:image' in hit:
atom += ' \n'
atom += ' \n'
atom += '\n'
return atom
def now2formatRFC3339():
formatstr = "%Y-%m-%dT%H:%M:%S"
s = time.strftime(formatstr, time.gmtime())
s += 'Z'
return s
def hit2nsmetahit(hit,hiturlprefix,colltorrdir):
""" Convert common hit to the fields required for the MPEG7 NS metadata """
print >>sys.stderr,"his2nsmetahit:"
# Read info from torrent files / P2PURLs
if hit['hittype'] == "localdb":
name = hit['name']
if hit['torrent_file_name'].startswith(P2PURL_SCHEME):
# Local DB hit that is P2PURL
torrenturl = hit['torrent_file_name']
titleimgurl = None
tdef = TorrentDef.load_from_url(torrenturl)
else:
# Local DB hit that is torrent file
torrenturlpath = '/'+infohash2urlpath(hit['infohash'])+URLPATH_TORRENT_POSTFIX
torrenturl = hiturlprefix + torrenturlpath
filepath = os.path.join(colltorrdir,hit['torrent_file_name'])
tdef = TorrentDef.load(filepath)
(thumbtype,thumbdata) = tdef.get_thumbnail()
if thumbtype is None:
titleimgurl = None
else:
titleimgurl = torrenturl+URLPATH_THUMBNAIL_POSTFIX
else:
# Remote hit
name = hit['content_name']
if hit['metatype'] == URL_MIME_TYPE:
torrenturl = hit['torrent_file_name']
titleimgurl = None
tdef = TorrentDef.load_from_url(torrenturl)
else:
torrenturlpath = '/'+infohash2urlpath(hit['infohash'])+URLPATH_TORRENT_POSTFIX
torrenturl = hiturlprefix + torrenturlpath
metainfo = bdecode(hit['metadata'])
tdef = TorrentDef.load_from_dict(metainfo)
(thumbtype,thumbdata) = tdef.get_thumbnail()
if thumbtype is None:
titleimgurl = None
else:
titleimgurl = torrenturl+URLPATH_THUMBNAIL_POSTFIX
# Extract info required for NS metadata MPEG7 representation.
nsmetahit = {}
nsmetahit['title'] = unicode2iri(name)
nsmetahit['titleimgurl'] = titleimgurl
comment = tdef.get_comment()
if comment is None:
nsmetahit['abstract'] = None
else:
nsmetahit['abstract'] = unicode2iri(comment)
nsmetahit['producer'] = 'Insert Name Here'
creator = tdef.get_created_by()
if creator is None:
creator = 'Insert Name Here Too'
nsmetahit['disseminator'] = creator
nsmetahit['copyrightstr'] = 'Copyright '+creator
nsmetahit['torrent_url'] = torrenturl
# TODO: multifile torrents, LIVE
nsmetahit['duration'] = bitratelength2nsmeta_duration(tdef.get_bitrate(),tdef.get_length())
return nsmetahit
def unicode2iri(uni):
# Roughly after http://www.ietf.org/rfc/rfc3987.txt Sec 3.1 procedure.
# TODO: do precisely after.
s = uni.encode('UTF-8')
return urllib.quote(s)
def bitratelength2nsmeta_duration(bitrate,length):
# Format example: PT0H15M0S
if bitrate is None:
return 'PT01H00M0S' # 1 hour
secs = float(length)/float(bitrate)
hours = float(int(secs / 3600.0))
secs = secs - hours*3600.0
mins = float(int(secs / 60.0))
secs = secs - mins*60.0
return 'PT%02.0fH%02.0fM%02.0fS' % (hours,mins,secs)
def nsmetahit2nsmetarepr(hit,hitpath):
title = hit['title']
titleimgurl = hit['titleimgurl']
abstract = hit['abstract']
producer = hit['producer']
disseminator = hit['disseminator']
copyrightstr = hit['copyrightstr']
torrenturl = hit['torrent_url']
duration = hit['duration'] # Format example: PT0H15M0S
livetimepoint = now2formatRFC3339() # Format example: '2009-10-05T00:40:00+01:00' # TODO VOD
s = ''
s += '\n'
s += '\n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' '+title+'\n'
s += ' \n'
if titleimgurl:
s += ' \n'
s += ' '+titleimgurl+'\n'
s += ' \n'
s += ' \n'
if abstract:
s += ' \n'
s += ' '+abstract+'\n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' '+producer+'\n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' '+disseminator+'\n'
s += ' \n'
s += ' \n'
s += ' '+copyrightstr+'\n'
s += ' \n'
s += ' \n'
s += ' false\n'
s += ' false\n'
s += ' false\n'
s += ' \n'
s += ' \n'
s += ' '+torrenturl+'\n'
s += ' \n'
s += ' offset(0, 1000)\n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' '+livetimepoint+'\n'
s += ' \n'
s += ' \n'
s += ' \n'
s += ' \n'
s += '\n'
return s
def hack_make_default_merkletorrent(title):
metainfo = {}
metainfo['announce'] = 'http://localhost:0/announce'
metainfo['creation date'] = int(time.time())
info = {}
info['name'] = title
info['length'] = 2 ** 30
info['piece length'] = 2 ** 16
info['root hash'] = '*' * 20
metainfo['info'] = info
mdict = {}
mdict['Publisher'] = 'Tribler'
mdict['Description'] = ''
mdict['Progressive'] = 1
mdict['Speed Bps'] = str(2 ** 16)
mdict['Title'] = metainfo['info']['name']
mdict['Creation Date'] = long(time.time())
# Azureus client source code doesn't tell what this is, so just put in random value from real torrent
mdict['Content Hash'] = 'PT3GQCPW4NPT6WRKKT25IQD4MU5HM4UY'
mdict['Revision Date'] = long(time.time())
cdict = {}
cdict['Content'] = mdict
metainfo['azureus_properties'] = cdict
return bencode(metainfo)
"""
class Infohash2TorrentPathMapper(AbstractPathMapper):
Mapper to map in the collection of known torrents files (=collected + started
+ own) into the HTTP address space of the local HTTP server. In particular,
it maps a "/infohash/aabbccdd...zz.tstream" path to a streaminfo dict.
Also supported are "/infohash/aabbccdd...zz.tstream/thumbnail" queries, which
try to read the thumbnail from the torrent.
def __init__(self,urlpathprefix,session):
self.urlpathprefix = urlpathprefix
self.session = session
self.torrent_db = self.session.open_dbhandler(NTFY_TORRENTS)
def get(self,urlpath):
if not urlpath.startswith(self.urlpathprefix):
return None
try:
wantthumb = False
if urlpath.endswith(URLPATH_THUMBNAIL_POSTFIX):
wantthumb = True
infohashquote = urlpath[len(self.urlpathprefix):-len(URLPATH_TORRENT_POSTFIX+URLPATH_THUMBNAIL_POSTFIX)]
else:
infohashquote = urlpath[len(self.urlpathprefix):-len(URLPATH_TORRENT_POSTFIX)]
infohash = urlpath2infohash(infohash)
dbhit = self.torrent_db.getTorrent(infohash,include_mypref=False)
colltorrdir = self.session.get_torrent_collecting_dir()
filepath = os.path.join(colltorrdir,dbhit['torrent_file_name'])
if not wantthumb:
# Return stream that contains torrent file
stream = open(filepath,"rb")
length = os.path.getsize(filepath)
streaminfo = {'statuscode':200,'mimetype':TSTREAM_MIME_TYPE,'stream':stream,'length':length}
else:
# Return stream that contains thumbnail
tdef = TorrentDef.load(filepath)
(thumbtype,thumbdata) = tdef.get_thumbnail()
if thumbtype is None:
return None
else:
stream = StringIO(thumbdata)
streaminfo = {'statuscode':200,'mimetype':thumbtype,'stream':stream,'length':len(thumbdata)}
return streaminfo
except:
print_exc()
return None
"""