131 lines
4.4 KiB
Python
131 lines
4.4 KiB
Python
import torrent_parser as tp
|
|
import threading
|
|
from math import ceil
|
|
from hashlib import sha1
|
|
from datetime import datetime
|
|
|
|
BUFFER_SIZE = 10 * 1024 * 1024
|
|
|
|
class Cron(object):
|
|
alive = False
|
|
def __init__(self, interval, func, *a, **kw):
|
|
self.interval = interval
|
|
self.func = func
|
|
self.a = a
|
|
self.kw = kw
|
|
self.ticker = threading.Event()
|
|
def start(self):
|
|
self.t = threading.Thread(target=self.run)
|
|
self.alive = True
|
|
self.t.start()
|
|
def run(self):
|
|
while self.alive and not self.ticker.wait(self.interval):
|
|
self.func(*self.a, **self.kw)
|
|
def stop(self):
|
|
self.alive = False
|
|
|
|
class Buffer(object):
|
|
def __init__(self, piece_size):
|
|
self.buf_reset_pending = False
|
|
self.buf_changed = threading.Event()
|
|
self.size = ceil(BUFFER_SIZE / piece_size)
|
|
self.buf = []
|
|
def buf_changed_event(self):
|
|
self.buf_changed.set()
|
|
self.buf_changed.clear()
|
|
def put(self, piece):
|
|
if not self.buf_reset_pending:
|
|
self.buf.append(piece)
|
|
self.buf_changed_event()
|
|
return True
|
|
else:
|
|
return False
|
|
def get(self):
|
|
if not self.buf_reset_pending:
|
|
if len(self.buf):
|
|
return self.buf.pop(0)
|
|
return None
|
|
def reset(self):
|
|
self.buf_reset_pending = True
|
|
self.buf = []
|
|
self.buf_reset_pending = False
|
|
self.buf_changed_event()
|
|
def pieces(self):
|
|
if not self.buf_reset_pending:
|
|
return list(map(lambda x: (x.index, x.hash), self.buf))
|
|
else:
|
|
return []
|
|
|
|
class Piece(object):
|
|
def __init__(self, p_hash, payload=b''):
|
|
self.hash = p_hash
|
|
self.payload = payload
|
|
self.exist = False
|
|
class PieceRange(object):
|
|
def __init__(self, first_piece, first_piece_offset, last_piece, last_piece_size):
|
|
self.first = first_piece
|
|
self.first_offset = first_piece_offset
|
|
self.last = last_piece
|
|
self.last_size = last_piece_size
|
|
class TFile(object):
|
|
def __init__(self, path, length, piece_range):
|
|
self.path = path
|
|
self.name = path[-1]
|
|
self.length = length
|
|
self.pieace_range = piece_range
|
|
class Tracker(object):
|
|
def __init__(self, url):
|
|
self.url = url
|
|
self.next_announce = datetime.now()
|
|
self.reachable = True
|
|
@property
|
|
def can_announce(self):
|
|
return self.next_announce <= datetime.now() and self.reachable
|
|
|
|
class Info(object):
|
|
def __init__(self, info, trackers):
|
|
trackers = list(set(trackers))
|
|
self.hash = sha1(tp.BEncoder(info).encode()).hexdigest()
|
|
self.files = []
|
|
self.trackers = list(map(Tracker, trackers))
|
|
self.pieces = list(map(Piece, info['pieces']))
|
|
self.piece_length = info['piece length']
|
|
def piece_range_gen(start, next_at):
|
|
first_piece = start // self.piece_length
|
|
first_piece_offset = start % self.piece_length
|
|
tail = next_at % self.piece_length
|
|
last_piece = next_at // self.piece_length - int(not bool(tail))
|
|
last_piece_size = self.piece_length if not tail else tail
|
|
return PieceRange(first_piece, first_piece_offset, last_piece, last_piece_size)
|
|
if 'files' in info:
|
|
addr_ptr = 0
|
|
for i in range(len(info['files'])):
|
|
f = info['files'][i]
|
|
next_at = addr_ptr + f['length']
|
|
pr = piece_range_gen(addr_ptr, next_at)
|
|
self.files.append(TFile(info['name'] + f['path'], f['length'], pr))
|
|
addr_ptr += f['length']
|
|
else:
|
|
pr = piece_range_gen(0, info['length'])
|
|
self.files.append(TFile([info['name']], info['length'], pr))
|
|
|
|
class Pyrrent(object):
|
|
shuttingdown = False
|
|
downloaded = 0
|
|
uploaded = 0
|
|
tracker_responce = {}
|
|
tracker_responce_ver = 0
|
|
def __init__(self, filepath):
|
|
data = tp.parse_torrent_file(filepath)
|
|
self.info = Info(data['info'], [data['announce']] + data['announce-list'] if 'announce-list' in data else [])
|
|
|
|
def tracker_anouncer(self):
|
|
self.tracker_responce_ver += 1
|
|
for i in range(len(self.info.trackers)):
|
|
tracker = self.info.trackers[i]
|
|
if tracker.can_announce:
|
|
thr = threading.Thread(target=self.announce, args=(tracker, i))
|
|
thr.start()
|
|
def announce(self, tracker, index):
|
|
pass
|