Skip to content

Conversation

@blacklight
Copy link
Collaborator

This is a more robust fix for #382.

What I didn't notice is that the case I covered was the one where mediaMetadata was actually populated but it was empty, but apparently there can also be paths where the key is completely missing so we still end up dereferencing a null.

ERROR    2025-12-02 01:09:49,035 [613:ThreadPoolExecutor-6_1] tidalapi.workers
  Failed to run >(limit=50, offset=2900, args=[None, None])
ERROR    2025-12-02 01:09:49,036 [613:ThreadPoolExecutor-6_1] tidalapi.workers
  'NoneType' object has no attribute 'get'
Traceback (most recent call last):
  File "/home/blacklight/.local/lib/python3.13/site-packages/tidalapi/workers.py", line 15, in func_wrapper
    items = func(limit, offset, *extra_args)
  File "/home/blacklight/.local/lib/python3.13/site-packages/tidalapi/playlist.py", line 230, in tracks
    self.request.map_json(
    ~~~~~~~~~~~~~~~~~~~~~^
        json_obj=request.json(), parse=self.session.parse_track
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/blacklight/.local/lib/python3.13/site-packages/tidalapi/request.py", line 247, in map_json
    return list(map(parse, items))
  File "/home/blacklight/.local/lib/python3.13/site-packages/tidalapi/session.py", line 281, in parse_track
    return self.track().parse_track(obj, album)
           ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
  File "/home/blacklight/.local/lib/python3.13/site-packages/tidalapi/media.py", line 307, in parse_track
    self.media_metadata_tags = json_obj.get("mediaMetadata", {}).get("tags", {})
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get'

This should fix it in all the cases.

@blacklight
Copy link
Collaborator Author

Btw since I also managed to replicate the error I also added a dump of json_obj to see on which track it fails. Example:

{
  "id": 55059589,
  "title": "Unavailable",
  "duration": 0,
  "replayGain": 0.0,
  "peak": 0.0,
  "allowStreaming": false,
  "streamReady": false,
  "payToStream": false,
  "adSupportedStreamReady": false,
  "djReady": false,
  "stemReady": false,
  "streamStartDate": null,
  "premiumStreamingOnly": false,
  "trackNumber": 0,
  "volumeNumber": 0,
  "version": null,
  "popularity": 0,
  "copyright": null,
  "bpm": null,
  "key": null,
  "keyScale": null,
  "description": null,
  "url": "http://www.tidal.com/track/55059589",
  "isrc": null,
  "editable": false,
  "explicit": false,
  "audioQuality": "LOW",
  "audioModes": [],
  "mediaMetadata": null,
  "upload": false,
  "accessType": null,
  "spotlighted": false,
  "artist": {
    "id": 1,
    "name": "Unknown Artist",
    "handle": null,
    "type": "MAIN",
    "picture": null
  }
}

The track has been removed in this case, but there's nothing to tell us that it was removed except for Unknown Artist - Unavailable and a duration of 0 (and, of course, the missing metadata).

This combination of flags can probably be interpreted as a way to tell if a track is available or not and filter these out in case.

Even though IMHO I'd still show unavailable tracks in playlists, or at least provide it as an option.

The reason is that many times I want to know if a track that I added to a playlist has been removed, so I can provision it through other means.

@tehkillerbee
Copy link
Collaborator

tehkillerbee commented Dec 2, 2025

Similar to your work, I did a bunch of tests yesterday.

I suggest we use one or more of the fields below to determine if the track is available (for streaming)

  "accessType": null, # should be 'PUBLIC' when streaming is available so perhaps the best bet.
  ...
  "allowStreaming": false,
  "streamReady": false,
  "payToStream": false,
  "adSupportedStreamReady": false,
  "djReady": false,
  "stemReady": false,

The media class already has the "available" field that should be possible to use directly. IIRC, it is directly set from the value of allowStreaming. If a track(video) is not available, it probably does not make sense to parse anything but the bare minimum (id, title artist, url etc).

I do have a WIP on a local branch, but ended taking a detour adding missing fields to the Media and Track classes so did not finish it. Will see if I can wrap it up tonight :)

@tehkillerbee
Copy link
Collaborator

I think we should not attempt parsing these fields, if the track is not available for streaming. Instead we should only parse available fields as done here: https://github.com/EbbLabs/python-tidal/tree/feature/fix-unavailable-tracks

I suggest closing this branch, as the fix is not relevant if we skip parsing the fields altogether.

@blacklight blacklight closed this Dec 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants