import html
import venigma
import os
import base64
import re
import hashlib
class AudioListItem:
def __init__(self, json_item):
self._jitem = json_item
def id(self):
return self._jitem["audio_raw_id"]
def key(self):
return self._jitem["access_key"]
def cid(self):
return f"{self.id()}_{self.key()}"
def _code(self):
return self._jitem["track_code"]
def audio_list(plain_list):
return [AudioListItem(item) for item in plain_list["payload"][1][0]]
class AudioDataItem:
def __init__(self, json_data):
self._jdata = json_data
self._manifest = None
def cid(self):
return f"{self._jdata[1]}_{self._jdata[0]}"
def index(self):
return base64.b16encode(self.hindex().encode()).decode()
def cursed_url(self):
return self._jdata[2]
def blessed_url(self, config):
return venigma.decode(config, self.cursed_url())
def root_url(self, config):
blessed_url = self.blessed_url(config)
parts = blessed_url.split("/")
return "/".join(parts[:-1]) + "/"
def title(self):
return html.unescape(self._jdata[3])
def artist(self):
return html.unescape(self._jdata[4])
def img(self):
return self._jdata[14]
def hindex(self):
return f"{self.title()} - {self.artist()}"
def findex(self, config):
return f"{config.audio_directory()}{self.cid()}.mp3"
def _hash(self, manifest_hash, mp3):
hash = hashlib.sha256(manifest_hash)
hash.update(mp3)
return hash.hexdigest()
def _hashfile(self, config):
return f"{self.manifest().directory(config)}sha256sum"
def hash(self, config):
f = open(self._hashfile(config), "w+")
fmp3 = open(self.findex(config), "rb")
manifest_hash = self.manifest().hashed(config)
h = self._hash(manifest_hash.encode(), fmp3.read())
fmp3.close()
f.write(h)
f.close()
return h
def hashed(self, config):
hashfile = self._hashfile(config)
findex = self.findex(config)
manifest_hash = self.manifest().hashed(config)
if (
not os.path.isfile(hashfile)
or not os.path.isfile(findex)
or not manifest_hash
):
return None
fmp3 = open(findex, "rb")
h = self._hash(manifest_hash.encode(), fmp3.read())
fmp3.close()
f = open(hashfile, "r")
fh = f.read()
f.close()
return h if h == fh else None
def manifest(self, manifest=None):
if manifest is None:
return self._manifest
else:
self._manifest = manifest
if manifest.data() is None:
manifest.data(self)
def audio_data(plain_data):
return [AudioDataItem(data) for data in plain_data["payload"][1][0]]
class Manifest:
def __init__(self, raw_content):
self._rcontent = raw_content
self._data = None
def load(config, data):
index = Manifest._index_for(config, data)
if os.path.isfile(index):
f = open(index, "rb")
manifest = Manifest(f.read())
f.close()
data.manifest(manifest)
return manifest
else:
return None
def content(self):
return self._rcontent
def _directory_for(config, data):
directory = config.manifests_directory()
return f"{directory}{data.index()}/"
def directory(self, config):
return Manifest._directory_for(config, self.data())
def _index_for(config, data):
return f"{Manifest._directory_for(config, data)}index.m3u8"
def index(self, config):
return Manifest._index_for(config, self.data())
def store(self, config):
directory = self.directory(config)
if not os.path.exists(directory):
os.makedirs(directory)
f = open(self.index(config), "wb+")
f.write(self.content())
f.close()
def chunks(self):
regex = r"seg\-\d+\-\w+\.ts\?siren=\d"
return re.findall(regex, self.content().decode("utf-8"))
def _hash(self, chunks):
hash = hashlib.sha1(self.content())
for chunk in chunks:
if not chunk:
continue
hash.update(chunk.data())
return hash.hexdigest()
def _hashfile(self, config):
return f"{self.directory(config)}sha1sum"
def hash(self, config, chunks):
f = open(self._hashfile(config), "w+")
h = self._hash(chunks)
f.write(h)
f.close()
return h
def hashed(self, config):
hashfile = self._hashfile(config)
chunks = [Chunk.load(config, self, chunk) for chunk in self.chunks()]
if not os.path.isfile(hashfile) or any(chunks) is None:
return None
h = self._hash(chunks)
f = open(hashfile, "r")
fh = f.read()
f.close()
return h if h == fh else None
def data(self, data=None):
if data is None:
return self._data
else:
self._data = data
if data.manifest() is None:
data.manifest(self)
class Chunk:
def __init__(self, manifest, name, raw_data):
self._name = name
self._rdata = raw_data
self._manifest = manifest
def _index_for(config, manifest, name):
return f"{manifest.directory(config)}{name}"
def load(config, manifest, name):
index = Chunk._index_for(config, manifest, name)
if os.path.isfile(index):
f = open(index, "rb")
chunk = Chunk(manifest, name, f.read())
f.close()
return chunk
else:
return None
def name(self):
return self._name
def data(self):
return self._rdata
def manifest(self):
return self._manifest
def store(self, config):
f = open(f"{Chunk._index_for(config, self.manifest(), self.name())}", "wb+")
f.write(self.data())
f.close()