-
-
Save cskaryd/51496f5c6edecba360800c855f32668f to your computer and use it in GitHub Desktop.
Fix issues with .NET DateTime Ticks parsing. It appears that all DateTime's expect the 8th byte to be hardcoded to \x08; Making this adjustment allows the byte array to translate into an integer creating a valid .NET Ticks number that correctly converts to a DateTime value which matches the value displayed in MusicBee.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import datetime | |
global library | |
def decode_from_7bit(data): | |
""" | |
Decode 7-bit encoded int from str data | |
""" | |
result = 0 | |
for index, char in enumerate(data): | |
#byte_value = ord(char) | |
result |= (char & 0x7f) << (7 * index) | |
if char & 0x80 == 0: | |
break | |
return result | |
def read_int(bytes_): | |
return int.from_bytes(bytes_, byteorder="little", signed=True) | |
def read_uint(bytes_): | |
return int.from_bytes(bytes_, byteorder="little") | |
def read_str(file): | |
len_1 = file.read(1) | |
if read_uint(len_1) > 0x7F: | |
len_2 = file.read(1) | |
if read_uint(len_2) > 0x7F: | |
length = decode_from_7bit([read_uint(len_1), read_uint(len_2), read_uint(file.read(1))]) | |
else: | |
length = decode_from_7bit([read_uint(len_1), read_uint(len_2)]) | |
else: | |
length = read_uint(len_1) | |
if length == 0: | |
return "" | |
return file.read(length).decode("utf-8") | |
def format_date(date_bytes): | |
if date_bytes == b'\x00\x00\x00\x00\x00\x00\x00\x00': | |
return '' | |
else: | |
date_ticks = read_int(date_bytes[0:7] + b'\x08') | |
epoch_ticks = 621355968000000000 | |
return datetime.datetime.fromtimestamp((date_ticks - epoch_ticks)/10000000).strftime('%Y-%m-%d %H:%M:%S') | |
with open("F:\\MusicBee\\MusicBeeLibrary.mbl", "rb") as mbl: | |
count = read_int(mbl.read(4)) | |
if not count & 0xFF: | |
raise | |
count = count >> 8 | |
library = {"file_count": count, "files": []} | |
while True: | |
media = {"file_designation": read_uint(mbl.read(1))} | |
if media["file_designation"] == 1: | |
break | |
if 10 > media["file_designation"] > 1: | |
media["status"] = read_uint(mbl.read(1)) | |
if media["status"] > 6: | |
raise | |
media["unknown_1"] = read_uint(mbl.read(1)) | |
media["play_count"] = read_uint(mbl.read(2)) | |
media["date_last_played"] = format_date(mbl.read(8)) | |
media["skip_count"] = read_uint(mbl.read(2)) | |
media["file_path"] = read_str(mbl) | |
print(media["file_path"]) | |
if media["file_path"] == "": | |
raise | |
media["file_size"] = read_int(mbl.read(4)) | |
media["sample_rate"] = read_int(mbl.read(4)) | |
media["channel_mode"] = read_uint(mbl.read(1)) | |
media["bitrate_type"] = read_uint(mbl.read(1)) | |
media["bitrate"] = read_int(mbl.read(2)) | |
media["track_length"] = read_int(mbl.read(4)) | |
media["date_added"] = format_date(mbl.read(8)) | |
media["date_modified"] = format_date(mbl.read(8)) | |
media["artwork"] = [] | |
while True: | |
art = {"type": read_uint(mbl.read(1))} | |
if art["type"] > 253: | |
break | |
art["string_1"] = read_str(mbl) | |
art["store_mode"] = read_uint(mbl.read(1)) | |
art["string_2"] = read_str(mbl) | |
media["artwork"].append(art) | |
media["tags_type"] = read_uint(mbl.read(1)) | |
media["tags"] = {} | |
while True: | |
tag_code = read_uint(mbl.read(1)) | |
if tag_code == 0: | |
break | |
if tag_code == 255: | |
c = read_int(mbl.read(2)) | |
i = 0 | |
media["cue"] = [] | |
while i < c: | |
cue = {} | |
cue["a"] = read_uint(mbl.read(1)) | |
cue["b"] = read_uint(mbl.read(2)) | |
cue["c"] = read_int(mbl.read(8)) | |
cue["d"] = read_uint(mbl.read(2)) | |
media["cue"].append(cue) | |
i += 1 | |
break | |
media["tags"][str(tag_code)] = read_str(mbl) | |
library["files"].append(media) | |
else: | |
raise | |
print(count, len(library["files"])) | |
with open("F:\\MusicBee\\MBL.json", "w") as jfile: | |
json.dump(library, jfile, indent=2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See also a quick and dirty MBL reader written in C# based on lempano's original gist which iterates through the mbl file and displays select values including correct datetime values
https://github.com/cskaryd/musicbee-library-parser