#!/usr/bin/env python3 """ #define FT_FILENAME 0x01 // #define FT_FILESIZE 0x02 // (or when supported) #define FT_FILESIZE_HI 0x3A // #define FT_FILETYPE 0x03 // #define FT_FILEFORMAT 0x04 // #define FT_LASTSEENCOMPLETE 0x05 // #define TAG_PART_PATH "\x06" // #define TAG_PART_HASH "\x07" #define FT_TRANSFERRED 0x08 // #define TAG_TRANSFERRED "\x08" // #define FT_GAPSTART 0x09 // #define TAG_GAPSTART "\x09" // #define FT_GAPEND 0x0A // #define TAG_GAPEND "\x0A" // #define FT_DESCRIPTION 0x0B // #define TAG_DESCRIPTION "\x0B" // #define TAG_PING "\x0C" #define TAG_FAIL "\x0D" #define TAG_PREFERENCE "\x0E" #define FT_PARTFILENAME 0x12 // //#define FT_PRIORITY 0x13 // Not used anymore #define FT_STATUS 0x14 // #define FT_SOURCES 0x15 // #define FT_PERMISSIONS 0x16 // //#define FT_ULPRIORITY 0x17 // Not used anymore #define FT_DLPRIORITY 0x18 // Was 13 #define FT_ULPRIORITY 0x19 // Was 17 #define FT_COMPRESSION 0x1A #define FT_CORRUPTED 0x1B #define FT_KADLASTPUBLISHKEY 0x20 // #define FT_KADLASTPUBLISHSRC 0x21 // #define FT_FLAGS 0x22 // #define FT_DL_ACTIVE_TIME 0x23 // #define FT_CORRUPTEDPARTS 0x24 // #define FT_DL_PREVIEW 0x25 #define FT_KADLASTPUBLISHNOTES 0x26 // #define FT_AICH_HASH 0x27 #define FT_FILEHASH 0x28 #define FT_COMPLETE_SOURCES 0x30 // nr. of sources which share a complete version of the associated file (supported by eserver 16.46+) #define FT_COLLECTIONAUTHOR 0x31 #define FT_COLLECTIONAUTHORKEY 0x32 #define FT_PUBLISHINFO 0x33 // #define FT_LASTSHARED 0x34 // #define FT_AICHHASHSET 0x35 // #define TAG_KADAICHHASHPUB "\x36" // // statistic #define FT_ATTRANSFERRED 0x50 // #define FT_ATREQUESTED 0x51 // #define FT_ATACCEPTED 0x52 // #define FT_CATEGORY 0x53 // #define FT_ATTRANSFERREDHI 0x54 // #define FT_MAXSOURCES 0x55 // #define FT_MEDIA_ARTIST 0xD0 // #define TAG_MEDIA_ARTIST "\xD0" // #define FT_MEDIA_ALBUM 0xD1 // #define TAG_MEDIA_ALBUM "\xD1" // #define FT_MEDIA_TITLE 0xD2 // #define TAG_MEDIA_TITLE "\xD2" // #define FT_MEDIA_LENGTH 0xD3 // !!! #define TAG_MEDIA_LENGTH "\xD3" // !!! #define FT_MEDIA_BITRATE 0xD4 // #define TAG_MEDIA_BITRATE "\xD4" // #define FT_MEDIA_CODEC 0xD5 // #define TAG_MEDIA_CODEC "\xD5" // #define FT_FILECOMMENT 0xF6 // #define FT_FILERATING 0xF7 // """ import sys import os import binascii from collections import namedtuple METENTRY_FIELDS = ('FT_FILENAME', 'ED2KHASH', 'NUMPARTS', 'LASTCHANGED', 'PARTHASHES', 'FT_FILESIZE', 'FT_ATTRANSFERRED', 'FT_ATREQUESTED', 'FT_ATACCEPTED', 'FT_ATTRANSFERREDHI', 'FT_PARTFILENAME', 'FT_AICHHASHSET', 'FT_AICH_HASH', 'FT_ULPRIORITY', 'FT_KADLASTPUBLISHSRC') MetEntry = namedtuple('met_entry', METENTRY_FIELDS) HEADER_KNOWN_MET_FILE = b'\x0e' HEADER_PART_MET_FILE = b'\xe0' FT_FILENAME = 0x01 FT_FILESIZE = 0x02 FT_PARTFILENAME = 0x12 FT_PERMISSIONS = 0x16 FT_ULPRIORITY = 0x19 FT_KADLASTPUBLISHKEY = 0x20 FT_KADLASTPUBLISHSRC = 0x21 FT_FLAGS = 0x22 FT_KADLASTPUBLISHNOTES = 0x26 FT_AICH_HASH = 0x27 FT_LASTSHARED = 0x34 FT_AICHHASHSET = 0x35 FT_ATTRANSFERRED = 0x50 FT_ATREQUESTED = 0x51 FT_ATACCEPTED = 0x52 FT_ATTRANSFERREDHI = 0x54 FT_MEDIA_ARTIST = 0xd0 FT_MEDIA_ALBUM = 0xd1 FT_MEDIA_TITLE = 0xd2 FT_MEDIA_LENGTH = 0xd3 FT_MEDIA_BITRATE = 0xd4 FT_MEDIA_CODEC = 0xd5 FT_STRING = ( 0x03, 0x04, 0x06, 0x0b, 0x11, 0x24, 0x28, 0xf6 ) FT_INT = ( 0x05, 0x08, 0x09, 0x0a, 0x14, 0x15, 0x18, 0x1a, 0x1b, 0x23, 0x25, 0x30, 0x33, 0x53, 0x55, 0xf7 ) def met_readstring(fd): string_len = int.from_bytes(fd.read(2), byteorder='little') return fd.read(string_len).decode('utf-8', errors='replace') def met_readinteger(fd, size=4): return int.from_bytes(fd.read(size), byteorder='little') def record_last_changed(fd): return met_readinteger(fd) def record_read_hash16(fd): aichash = fd.read(16) return binascii.hexlify(aichash).decode('utf-8') def record_tags(fd): tags = {} tags['TAGCOUNT'] = met_readinteger(fd) # print('Tags: {}'.format(tags['TAGCOUNT'])) for index in range(tags['TAGCOUNT']): filetag = fd.read(4) if filetag[:3] not in [b'\x02\x01\x00', b'\x03\x01\x00']: print('<<< Premature end of record at offset 0x{:08X}'.format(fd.tell())) return tags tagid = filetag[-1] if tagid == FT_FILENAME: tags['FT_FILENAME'] = met_readstring(fd) elif tagid == FT_FILESIZE: tags['FT_FILESIZE'] = met_readinteger(fd) elif tagid == FT_ATTRANSFERRED: tags['FT_ATTRANSFERRED'] = met_readinteger(fd) elif tagid == FT_ATREQUESTED: tags['FT_ATREQUESTED'] = met_readinteger(fd) elif tagid == FT_ATACCEPTED: tags['FT_ATACCEPTED'] = met_readinteger(fd) elif tagid == FT_ATTRANSFERREDHI: tags['FT_ATTRANSFERREDHI'] = met_readinteger(fd) elif tagid == FT_ULPRIORITY: tags['FT_ULPRIORITY'] = met_readinteger(fd) elif tagid == FT_KADLASTPUBLISHSRC: tags['FT_KADLASTPUBLISHSRC'] = met_readinteger(fd) elif tagid == FT_KADLASTPUBLISHNOTES: print('FT_KADLASTPUBLISHNOTES') met_readinteger(fd) elif tagid == FT_FLAGS: print('FT_FLAGS') met_readinteger(fd) elif tagid == FT_PERMISSIONS: print('FT_PERMISSIONS') met_readinteger(fd) elif tagid == FT_KADLASTPUBLISHKEY: print('FT_KADLASTPUBLISHKEY') met_readinteger(fd) elif tagid == FT_AICH_HASH: tags['FT_AICH_HASH'] = met_readstring(fd) elif tagid == FT_LASTSHARED: tags['FT_LASTSHARED'] = met_readinteger(fd) elif tagid == FT_AICHHASHSET: tags['FT_AICHHASHSET'] = met_readinteger(fd, 1) elif tagid == FT_PARTFILENAME: tags['FT_PARTFILENAME'] = met_readstring(fd) elif tagid == FT_MEDIA_LENGTH: print('Media Length: {}'.format(met_readinteger(fd))) elif tagid == FT_MEDIA_CODEC: print('Media Codec: {}'.format(met_readstring(fd))) elif tagid == FT_MEDIA_BITRATE: print('Media Bitrate: {}'.format(met_readinteger(fd))) elif tagid == FT_MEDIA_TITLE: print('Media Title: {}'.format(met_readstring(fd))) elif tagid == FT_MEDIA_ARTIST: print('Media Artist: {}'.format(met_readstring(fd))) elif tagid == FT_MEDIA_ALBUM: print('Media Album: {}'.format(met_readstring(fd))) elif tagid in FT_STRING: print('... Reading data with tag: {}'.format(tagid)) met_readstring(fd) elif tagid in FT_INT: print('... Reading data with tag: {}'.format(tagid)) met_readstring(fd) return tags def record_num_parts(fd): return met_readinteger(fd, 2) def load_record(fd): met_tags = {} met_tags['LASTCHANGED'] = record_last_changed(fd) met_tags['ED2KHASH'] = record_read_hash16(fd) met_tags['NUMPARTS'] = record_num_parts(fd) partshashes = [] for i in range(met_tags['NUMPARTS']): partshashes.append(record_read_hash16(fd)) met_tags['PARTHASHES'] = partshashes met_tags.update(record_tags(fd)) for field in METENTRY_FIELDS: if field not in met_tags: met_tags[field] = None # print(met_tags) metentry = MetEntry(met_tags['FT_FILENAME'], met_tags['ED2KHASH'], met_tags['NUMPARTS'], met_tags['LASTCHANGED'], met_tags['PARTHASHES'], met_tags['FT_FILESIZE'], met_tags['FT_ATTRANSFERRED'], met_tags['FT_ATREQUESTED'], met_tags['FT_ATACCEPTED'], met_tags['FT_ATTRANSFERREDHI'], met_tags['FT_PARTFILENAME'], met_tags['FT_AICHHASHSET'], met_tags['FT_AICH_HASH'], met_tags['FT_ULPRIORITY'], met_tags['FT_KADLASTPUBLISHSRC'] ) # print(metentry) return metentry def load_knownfiles(filename): with open(filename, 'rb') as fd: magic = fd.read(1) if os.path.basename(filename) == 'known.met': count = int.from_bytes(fd.read(4), byteorder='little') # print('Number of records: {}'.format(count)) if magic == HEADER_KNOWN_MET_FILE: for index in range(count): # print('>>> [{}] NEW RECORD ***'.format(index+1)) yield load_record(fd) elif magic == HEADER_PART_MET_FILE: # print('>>> [{}] NEW RECORD ***'.format(0)) yield load_record(fd)