From 54ef06f84c76f22a86983f2bc64acc78ceb4468e Mon Sep 17 00:00:00 2001 From: Dniel97 <6324072+Dniel97@users.noreply.github.com> Date: Thu, 3 Feb 2022 15:50:16 +0100 Subject: [PATCH] Improved MQA detection - Downloads the first 163824 bytes and check for MQA - Now properly downloads the file as any other track - Already existing track check working --- README.md | 3 +- interface.py | 47 +++++++++++++++---------- mqa_identifier_python/mqa_identifier.py | 12 ++++--- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index ff7c045..f7288ed 100644 --- a/README.md +++ b/README.md @@ -151,8 +151,7 @@ file in order to get properly detected. [@purpl3F0x](https://github.com/purpl3F0x) and [mqaid](https://github.com/redsudo/mqaid) by [@redsudo](https://github.com/redsudo).** -**NOTE: `fix_mqa` is experimental! May not detect already existing tracks, -slower as normal download and could be not working at all** +**NOTE: `fix_mqa` is enabled which is experimental! May be slower as normal download and could not be working at all** ## Contact diff --git a/interface.py b/interface.py index c63a183..bdb9877 100644 --- a/interface.py +++ b/interface.py @@ -12,7 +12,7 @@ import ffmpeg from tqdm import tqdm from utils.models import * -from utils.utils import sanitise_name, silentremove, download_to_temp, create_temp_filename +from utils.utils import sanitise_name, silentremove, download_to_temp, create_temp_filename, create_requests_session from .mqa_identifier_python.mqa_identifier import MqaIdentifier from .tidal_api import TidalTvSession, TidalApi, SessionStorage, TidalMobileSession, SessionType @@ -357,21 +357,16 @@ class ModuleInterface: else: # check if MQA if track_codec is CodecEnum.MQA and self.settings['fix_mqa']: - self.print(f'"fix_mqa" is enabled which is experimental! May not detect already existing tracks, ' - f'slower as normal download and could be not working at all', drop_level=1) - self.print(f'=== Downloading MQA {track_name} ({track_id}) ===', drop_level=1) - indent_level = self.oprinter.indent_number - self.oprinter.multiplier - # download the file to analyze it - temp_file_path = download_to_temp(manifest['urls'][0], enable_progress_bar=True, - indent_level=indent_level) - download_args = {'temp_file_path': temp_file_path} + self.print(f'"fix_mqa" is enabled which is experimental! May be slower as normal download and could ' + f'not be working at all', drop_level=1) + # download the first chunk of the flac file to analyze it + temp_file_path = self.download_temp_header(manifest['urls'][0]) # detect MQA file mqa_file = MqaIdentifier(temp_file_path) - self.print(f'=== MQA {track_id} downloaded ===', drop_level=1) - else: - download_args = {'file_url': manifest['urls'][0]} + # add the file to download_args + download_args = {'file_url': manifest['urls'][0]} bit_depth = 24 if track_codec in [CodecEnum.EAC3, CodecEnum.MHA1] else 16 sample_rate = 48 if track_codec in [CodecEnum.EAC3, CodecEnum.MHA1, CodecEnum.AC4] else 44.1 @@ -405,6 +400,24 @@ class ModuleInterface: return track_info + @staticmethod + def download_temp_header(file_url: str, chunk_size: int = 16384) -> str: + # create flac temp_location + temp_location = create_temp_filename() + '.flac' + + # create session and download the file to the temp_location + r_session = create_requests_session() + + r = r_session.get(file_url, stream=True, verify=False) + with open(temp_location, 'wb') as f: + # only download the first chunk_size bytes + for chunk in r.iter_content(chunk_size=chunk_size): + if chunk: # filter out keep-alive new chunks + f.write(chunk) + break + + return temp_location + @staticmethod def parse_mpd(xml: bytes) -> list: xml = xml.decode('UTF-8') @@ -466,17 +479,13 @@ class ModuleInterface: return tracks - def get_track_download(self, file_url: str = None, temp_file_path: str = None, audio_track: AudioTrack = None) \ + def get_track_download(self, file_url: str = None, audio_track: AudioTrack = None) \ -> TrackDownloadInfo: - # only file_url, temp_file_path or audio_track at a time, with file_url > temp_file_path + # only file_url or audio_track at a time - # MHA1 or EC-3 + # MHA1, EC-3 or MQA if file_url: return TrackDownloadInfo(download_type=DownloadEnum.URL, file_url=file_url) - - # MQA file with enabled "fix_mqa" - if temp_file_path: - return TrackDownloadInfo(download_type=DownloadEnum.TEMP_FILE_PATH, temp_file_path=temp_file_path) # MPEG-DASH # use the total_file size for a better progress bar? Is it even possible to calculate the total size from MPD? diff --git a/mqa_identifier_python/mqa_identifier.py b/mqa_identifier_python/mqa_identifier.py index a59f9d4..6c2c1ae 100644 --- a/mqa_identifier_python/mqa_identifier.py +++ b/mqa_identifier_python/mqa_identifier.py @@ -58,7 +58,7 @@ MAGIC = 51007556744 # int.from_bytes(bytes.fromhex('0be0498c88'), 'big') jesus class MqaIdentifier: - def __init__(self, flac_file_path: Path): + def __init__(self, flac_file_path: str or Path): self.is_mqa = False self.is_mqa_studio = False self.original_sample_rate = None @@ -76,7 +76,7 @@ class MqaIdentifier: return int(sample_rate) return sample_rate - def _decode_flac_samples(self, flac_file_path) -> list: + def _decode_flac_samples(self, flac_file_path: str or Path) -> list: """ Decodes a 16/24bit flac file to a samples list @@ -89,7 +89,11 @@ class MqaIdentifier: if magic == b'fLaC': with flac.BitInputStream(f) as bf: f = io.BytesIO() - flac.decode_file(bf, f, seconds=1) + # ignore EOFError + try: + flac.decode_file(bf, f, seconds=1) + except EOFError: + pass f.seek(0) with wave.open(f) as wf: @@ -108,7 +112,7 @@ class MqaIdentifier: return list(iter_data(wf.readframes(framerate))) - def detect(self, flac_file_path) -> bool: + def detect(self, flac_file_path: str or Path) -> bool: """ Detects if the FLAC file is a MQA file and also detects if it's MQA Studio (blue) and the originalSampleRate