diff --git a/.gitignore b/.gitignore index 8f9852c..e13ae64 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ diff --git a/addon.xml b/addon.xml new file mode 100644 index 0000000..578547d --- /dev/null +++ b/addon.xml @@ -0,0 +1,17 @@ + + + + + + + + + all + en_GB ru_RU + Pure python torrent video stream engine for KODI + Движок потокового видео из сети torrent на чистом Python + Движок потокового видео из сети torrent на чистом Python + Pure python torrent video stream engine for KODI + roman@ukamnya.ru + https://git.ukamnya.ru/roman/script.module.pyrrent + diff --git a/lib/pyrrent.py b/lib/pyrrent.py new file mode 100644 index 0000000..eb8866b --- /dev/null +++ b/lib/pyrrent.py @@ -0,0 +1,89 @@ +import torrent_parser as tp +import threading +from math import ceil + +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_index, p_hash, payload): + self.index = p_index + self.hash = p_hash + self.payload = payload + +class Pyrrent(object): + multifile = False + def __init__(self, filepath): + self.data = tp.parse_torrent_file(filepath) + self.piece_length = self.data['info']['piece length'] + self.buffer = Buffer(self.piece_length) + self.pieces = self.data['info']['pieces'] + if 'files' in self.data['info'].keys(): + self.multifile = True + if not self.multifile: + self.file_addresses = [(0, self.piece_length)] + else: + self.file_addresses = [] + addr_ptr = 0 + for length in map(lambda x: x['length'], self.data['info']['files']): + next_at = addr_ptr + length + self.file_addresses.append((addr_ptr, next_at)) + addr_ptr += length + def piece_range(self, f_index): + start, next_at = self.file_addresses[f_index] + start_piece = start // self.piece_length + start_byte = start % self.piece_length + tail = next_at % self.piece_length + end_piece = next_at // self.piece_length - int(not bool(tail)) + end_byte = self.piece_length if not tail else tail + return [(start_piece, start_byte),(end_piece, end_byte)] +