When enabled, existing path components are reused even if Tidal returns
different casing. This avoids creating separate paths on case-sensitive
filesystems that would conflict later when moved to case-insensitive systems.
For example, if "FooBar" already exists and the API returns "foobar",
downloads will continue under "FooBar".
Co-authored-by: Piotr Karbowski <git.throwaway941@simplelogin.com>
* Update Dockerfile
* Clears entire directory after install
Hidden folders such as .git, .vscode, etc were remaining in the docker build unintentionally. This update clears all files once tiddl is installed.
Resolves issue #299.
int | str is used instead of updating the type to only "str" because the API change appears to have been introduced quietly and may revert in the future. Since the exact type of these fields is not critical for the library, supporting both types provides a safer and more resilient approach.
* fix: handle null/missing fields in Video API responses
Tidal's API returns some Video objects (lyric/visualiser videos on
artist pages) with fields that don't match the current strict models:
- `imageId` can be null instead of a string
- The nested `album` object can be present but missing `id`, `title`,
and `cover`
These validation failures cause the entire `ArtistVideosItems` page
to be rejected by Pydantic before any video can be parsed, resulting
in 0 downloads when targeting an artist with `--videos`.
A second independent bug causes an `AttributeError` on every video:
the default template `{album.artist}/{album.title}/{item.title}` is
shared with videos, but many videos have no album. When `album=None`
is passed to `format_template`, Python's `str.format()` evaluates
`None.artist` and raises `AttributeError: 'NoneType' object has no
attribute 'artist'`, which is caught and printed as an error for
every single video.
Fix:
- `resources.py`: make `Video.imageId` and `Video.Album.{id,title,
cover}` optional so incomplete API responses pass validation
- `format.py`: give `AlbumTemplate` field defaults so it can be
instantiated empty; use `AlbumTemplate()` as fallback instead of
`None` when no album is present, so `{album.*}` tokens render as
empty strings rather than raising AttributeError
- `download/__init__.py`: guard `video.album.id` accesses against
`None` (now possible after the model fix) in both video code paths
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: add tests for Video model null fields and AlbumTemplate fallback
Covers the two bugs fixed in the previous commit:
- Video model accepts null/missing imageId and partial album objects
- format_template does not raise AttributeError when album is None
and the template references {album.*} tokens
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: Add `--skip-errors` flag to skip unavailable items during download
* feat: skip-error handling for unavailable items in albums, artists & mixes; fix some missing template variables
* fixes#267
* refactor variable name in _clean_segment function for clarity
* Add error handling documentation for format_template function
* Add error handling for album template formatting in download_callback
* adding flag for download lyrics
* adding sum debug info
* adding configuration part
* Update config.example.toml
* refactor: move lyrics to utility and apply code review feedback
* refactor: optimize lyrics download and apply code review feedback
- Pass album_items directly to lyrics function to avoid redundant API calls
- Remove unused Path import from api.py
- Remove duplicate debug logging already covered elsewhere
- Use last_album_items reference instead of fetching data again
- Move lyrics download logic to separate utility function in core.utils.lyrics
- Keep lyrics configuration only in docs/config.example.toml without modifying other settings
This optimization reduces API calls by reusing already fetched album items data instead of making new requests for lyrics download.