|
@@ -1,6 +1,8 @@ |
|
|
#!/usr/bin/env python3 |
|
|
#!/usr/bin/env python3 |
|
|
import enum |
|
|
import enum |
|
|
|
|
|
import functools |
|
|
import hashlib |
|
|
import hashlib |
|
|
|
|
|
import operator |
|
|
import sys |
|
|
import sys |
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -85,6 +87,9 @@ def bdecode(fp, display = False, infoWrite = None): |
|
|
if c != b'd': |
|
|
if c != b'd': |
|
|
raise ValueError |
|
|
raise ValueError |
|
|
stateStack = [ParserState.NONE, ParserState.DICTIONARY] |
|
|
stateStack = [ParserState.NONE, ParserState.DICTIONARY] |
|
|
|
|
|
idxStack = [None, None] |
|
|
|
|
|
out = {} |
|
|
|
|
|
get_o = lambda: functools.reduce(operator.getitem, idxStack[2:], out) |
|
|
inInfo = False |
|
|
inInfo = False |
|
|
print_ = print if display else (lambda *args, **kwargs: None) |
|
|
print_ = print if display else (lambda *args, **kwargs: None) |
|
|
print_(f'(global): {dictEntry}') |
|
|
print_(f'(global): {dictEntry}') |
|
@@ -95,6 +100,7 @@ def bdecode(fp, display = False, infoWrite = None): |
|
|
c = fp.read(1) |
|
|
c = fp.read(1) |
|
|
if c == b'e': # End of dict |
|
|
if c == b'e': # End of dict |
|
|
stateStack.pop(-1) |
|
|
stateStack.pop(-1) |
|
|
|
|
|
idxStack.pop(-1) |
|
|
if len(stateStack) == 2 and inInfo and infoWrite: |
|
|
if len(stateStack) == 2 and inInfo and infoWrite: |
|
|
inInfo = False |
|
|
inInfo = False |
|
|
fp = fp.fp |
|
|
fp = fp.fp |
|
@@ -106,19 +112,31 @@ def bdecode(fp, display = False, infoWrite = None): |
|
|
fp = CopyingFileReader(fp, infoWrite) |
|
|
fp = CopyingFileReader(fp, infoWrite) |
|
|
v = read_or_stack_value(fp, stateStack) |
|
|
v = read_or_stack_value(fp, stateStack) |
|
|
print_(f'{indent}{key!r}: {v!r}') |
|
|
print_(f'{indent}{key!r}: {v!r}') |
|
|
|
|
|
if v is dictEntry or v is listEntry: |
|
|
|
|
|
get_o()[key] = {} if v is dictEntry else [] |
|
|
|
|
|
idxStack.append(key) |
|
|
|
|
|
else: |
|
|
|
|
|
get_o()[key] = v |
|
|
else: |
|
|
else: |
|
|
raise ValueError |
|
|
raise ValueError |
|
|
elif state == ParserState.LIST: |
|
|
elif state == ParserState.LIST: |
|
|
c = fp.read(1) |
|
|
c = fp.read(1) |
|
|
if c == b'e': |
|
|
if c == b'e': |
|
|
stateStack.pop(-1) |
|
|
stateStack.pop(-1) |
|
|
|
|
|
idxStack.pop(-1) |
|
|
continue |
|
|
continue |
|
|
else: |
|
|
else: |
|
|
v = read_or_stack_value(fp, stateStack, c) |
|
|
v = read_or_stack_value(fp, stateStack, c) |
|
|
print_(f'{indent}- {v!r}') |
|
|
print_(f'{indent}- {v!r}') |
|
|
|
|
|
o = get_o() |
|
|
|
|
|
if v is dictEntry or v is listEntry: |
|
|
|
|
|
o.append({} if v is dictEntry else []) |
|
|
|
|
|
idxStack.append(len(o) - 1) |
|
|
|
|
|
else: |
|
|
|
|
|
o.append(v) |
|
|
elif state == ParserState.NONE: |
|
|
elif state == ParserState.NONE: |
|
|
assert len(stateStack) == 1 |
|
|
assert len(stateStack) == 1 |
|
|
return |
|
|
|
|
|
|
|
|
return out |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def print_torrent(fp): |
|
|
def print_torrent(fp): |
|
@@ -131,10 +149,16 @@ def get_info_hash(fp): |
|
|
return hasher.hexdigest() |
|
|
return hasher.hexdigest() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def print_files(fp): |
|
|
|
|
|
o = bdecode(fp) |
|
|
|
|
|
for f in o['info']['files']: |
|
|
|
|
|
print('/'.join(f['path'])) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(): |
|
|
def main(): |
|
|
if len(sys.argv) < 3 or sys.argv[1] not in ('print', 'infohash'): |
|
|
|
|
|
|
|
|
if len(sys.argv) < 3 or sys.argv[1] not in ('print', 'infohash', 'files'): |
|
|
print('Usage: torrent-tiny MODE FILE [FILE...]', file = sys.stderr) |
|
|
print('Usage: torrent-tiny MODE FILE [FILE...]', file = sys.stderr) |
|
|
print('MODEs: print, infohash', file = sys.stderr) |
|
|
|
|
|
|
|
|
print('MODEs: print, infohash, files', file = sys.stderr) |
|
|
sys.exit(1) |
|
|
sys.exit(1) |
|
|
|
|
|
|
|
|
mode = sys.argv[1] |
|
|
mode = sys.argv[1] |
|
@@ -144,6 +168,8 @@ def main(): |
|
|
print_torrent(fp) |
|
|
print_torrent(fp) |
|
|
elif mode == 'infohash': |
|
|
elif mode == 'infohash': |
|
|
print(get_info_hash(fp)) |
|
|
print(get_info_hash(fp)) |
|
|
|
|
|
elif mode == 'files': |
|
|
|
|
|
print_files(fp) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
if __name__ == '__main__': |
|
|