From e5d104e9f3268455e71f17ebf0369b3e8ae98b17 Mon Sep 17 00:00:00 2001 From: musicalmusicalmusical Date: Fri, 16 Jan 2026 18:46:31 +0000 Subject: [PATCH 1/3] Update __init__.py --- tidal_dl_ng/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tidal_dl_ng/__init__.py b/tidal_dl_ng/__init__.py index b7a692c..17739c7 100644 --- a/tidal_dl_ng/__init__.py +++ b/tidal_dl_ng/__init__.py @@ -1,4 +1,10 @@ #!/usr/bin/env python +# Apply mpegdash patch before any tidalapi imports to fix TIDAL manifest parsing +# See: https://github.com/FunWarry/tidal-dl-ng-For-DJ/issues/15 +from tidal_dl_ng.helper.mpegdash_patch import apply_mpegdash_patch + +apply_mpegdash_patch() + import importlib.metadata from pathlib import Path from urllib.parse import urlparse From 173b40bef20ae4d0d9b2b06b8c06701c892b298e Mon Sep 17 00:00:00 2001 From: musicalmusicalmusical Date: Fri, 16 Jan 2026 18:46:45 +0000 Subject: [PATCH 2/3] Add patch --- tidal_dl_ng/helper/mpegdash_patch.py | 88 ++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tidal_dl_ng/helper/mpegdash_patch.py diff --git a/tidal_dl_ng/helper/mpegdash_patch.py b/tidal_dl_ng/helper/mpegdash_patch.py new file mode 100644 index 0000000..3645436 --- /dev/null +++ b/tidal_dl_ng/helper/mpegdash_patch.py @@ -0,0 +1,88 @@ +""" +Monkey-patch for mpegdash to handle non-integer AdaptationSet id/group attributes. + +TIDAL recently started returning MPD manifests with string values like "main" +for the AdaptationSet `id` and `group` attributes, which mpegdash expects to be integers. +This causes a ValueError: invalid literal for int() with base 10: 'main' + +This patch modifies the parse_attr_value function to gracefully handle +non-integer values for attributes that are expected to be integers. +""" + +import logging + +logger = logging.getLogger(__name__) + +_patched = False + + +def _safe_int(value: str) -> int | None: + """Safely convert a value to int, returning None if conversion fails.""" + try: + return int(value) + except (ValueError, TypeError): + return None + + +def apply_mpegdash_patch() -> None: + """ + Apply a monkey-patch to mpegdash to handle string values in integer fields. + + This patches the parse_attr_value function in mpegdash.utils to gracefully + handle non-integer values (like "main") for attributes expected to be integers. + """ + global _patched + + if _patched: + return + + try: + from mpegdash import utils as mpegdash_utils + import re + + # Store the original function + original_parse_attr_value = mpegdash_utils.parse_attr_value + + def patched_parse_attr_value(xmlnode, attr_name, value_type): + """ + Patched version of parse_attr_value that handles non-integer values gracefully. + """ + if attr_name not in xmlnode.attributes.keys(): + return None + + attr_val = xmlnode.attributes[attr_name].nodeValue + + if isinstance(value_type, list): + attr_type = value_type[0] if len(value_type) > 0 else str + try: + return [attr_type(elem) for elem in re.split(r"[, ]", attr_val)] + except (ValueError, TypeError): + # If conversion fails, return as strings + return [str(elem) for elem in re.split(r"[, ]", attr_val)] + + # Handle integer conversion failures gracefully + if value_type == int: + result = _safe_int(attr_val) + if result is None: + logger.debug( + f"mpegdash: Could not convert '{attr_name}'='{attr_val}' to int, using None" + ) + return result + + try: + return value_type(attr_val) + except (ValueError, TypeError): + logger.debug( + f"mpegdash: Could not convert '{attr_name}'='{attr_val}' to {value_type.__name__}, using None" + ) + return None + + # Apply the patch + mpegdash_utils.parse_attr_value = patched_parse_attr_value + _patched = True + logger.debug("mpegdash patch applied successfully") + + except ImportError: + logger.warning("Could not import mpegdash, patch not applied") + except Exception as e: + logger.warning(f"Failed to apply mpegdash patch: {e}") From 0ed90ba8c2b32cff958bdb238ec154820f88ddc9 Mon Sep 17 00:00:00 2001 From: Warry Date: Fri, 23 Jan 2026 11:52:09 +0100 Subject: [PATCH 3/3] refactor: Refactor mpegdash patch logic and update import ordering --- tidal_dl_ng/__init__.py | 12 ++--- tidal_dl_ng/helper/mpegdash_patch.py | 67 +++++++++++++++------------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/tidal_dl_ng/__init__.py b/tidal_dl_ng/__init__.py index 17739c7..b7c4bc5 100644 --- a/tidal_dl_ng/__init__.py +++ b/tidal_dl_ng/__init__.py @@ -1,10 +1,4 @@ #!/usr/bin/env python -# Apply mpegdash patch before any tidalapi imports to fix TIDAL manifest parsing -# See: https://github.com/FunWarry/tidal-dl-ng-For-DJ/issues/15 -from tidal_dl_ng.helper.mpegdash_patch import apply_mpegdash_patch - -apply_mpegdash_patch() - import importlib.metadata from pathlib import Path from urllib.parse import urlparse @@ -13,8 +7,14 @@ import requests import toml from tidal_dl_ng.constants import REQUESTS_TIMEOUT_SEC + +# Apply mpegdash patch before any tidalapi imports to fix TIDAL manifest parsing +# See: https://github.com/FunWarry/tidal-dl-ng-For-DJ/issues/15 +from tidal_dl_ng.helper.mpegdash_patch import apply_mpegdash_patch from tidal_dl_ng.model.meta import ProjectInformation, ReleaseLatest +apply_mpegdash_patch() + def metadata_project() -> ProjectInformation: result: ProjectInformation diff --git a/tidal_dl_ng/helper/mpegdash_patch.py b/tidal_dl_ng/helper/mpegdash_patch.py index 3645436..4cccf0e 100644 --- a/tidal_dl_ng/helper/mpegdash_patch.py +++ b/tidal_dl_ng/helper/mpegdash_patch.py @@ -10,6 +10,7 @@ non-integer values for attributes that are expected to be integers. """ import logging +import re logger = logging.getLogger(__name__) @@ -24,6 +25,36 @@ def _safe_int(value: str) -> int | None: return None +def _convert_list(attr_val: str, value_type) -> list[str]: + attr_type = value_type[0] if value_type else str + try: + return [attr_type(elem) for elem in re.split(r"[, ]", attr_val)] + except (ValueError, TypeError): + return [str(elem) for elem in re.split(r"[, ]", attr_val)] + + +def _convert_single(attr_name: str, attr_val: str, value_type): + if value_type == int: + result = _safe_int(attr_val) + if result is None: + logger.debug( + "mpegdash: Could not convert '%s'='%s' to int, using None", + attr_name, + attr_val, + ) + return result + try: + return value_type(attr_val) + except (ValueError, TypeError): + logger.debug( + "mpegdash: Could not convert '%s'='%s' to %s, using None", + attr_name, + attr_val, + value_type.__name__, + ) + return None + + def apply_mpegdash_patch() -> None: """ Apply a monkey-patch to mpegdash to handle string values in integer fields. @@ -38,46 +69,18 @@ def apply_mpegdash_patch() -> None: try: from mpegdash import utils as mpegdash_utils - import re - - # Store the original function - original_parse_attr_value = mpegdash_utils.parse_attr_value def patched_parse_attr_value(xmlnode, attr_name, value_type): - """ - Patched version of parse_attr_value that handles non-integer values gracefully. - """ - if attr_name not in xmlnode.attributes.keys(): + if attr_name not in xmlnode.attributes: return None attr_val = xmlnode.attributes[attr_name].nodeValue if isinstance(value_type, list): - attr_type = value_type[0] if len(value_type) > 0 else str - try: - return [attr_type(elem) for elem in re.split(r"[, ]", attr_val)] - except (ValueError, TypeError): - # If conversion fails, return as strings - return [str(elem) for elem in re.split(r"[, ]", attr_val)] + return _convert_list(attr_val, value_type) - # Handle integer conversion failures gracefully - if value_type == int: - result = _safe_int(attr_val) - if result is None: - logger.debug( - f"mpegdash: Could not convert '{attr_name}'='{attr_val}' to int, using None" - ) - return result + return _convert_single(attr_name, attr_val, value_type) - try: - return value_type(attr_val) - except (ValueError, TypeError): - logger.debug( - f"mpegdash: Could not convert '{attr_name}'='{attr_val}' to {value_type.__name__}, using None" - ) - return None - - # Apply the patch mpegdash_utils.parse_attr_value = patched_parse_attr_value _patched = True logger.debug("mpegdash patch applied successfully") @@ -85,4 +88,4 @@ def apply_mpegdash_patch() -> None: except ImportError: logger.warning("Could not import mpegdash, patch not applied") except Exception as e: - logger.warning(f"Failed to apply mpegdash patch: {e}") + logger.warning("Failed to apply mpegdash patch: %s", e)