Skip to content

Commit

Permalink
Merge pull request #70 from oskvr37/2.0
Browse files Browse the repository at this point in the history
2.0
  • Loading branch information
oskvr37 authored Jan 27, 2025
2 parents 8a10561 + 8ce2b05 commit 0889b75
Show file tree
Hide file tree
Showing 35 changed files with 1,629 additions and 1,667 deletions.
10 changes: 8 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
{
"python.analysis.typeCheckingMode": "basic"
}
"python.analysis.typeCheckingMode": "basic",
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports.ruff": "explicit",
}
},
"ruff.lineLength": 80,
}
104 changes: 48 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Tidal Downloader

TIDDL is the Python CLI application that allows downloading Tidal tracks.
Fully typed, only 2 requirements.
TIDDL is Python CLI application that allows downloading Tidal tracks.

![GitHub top language](https://img.shields.io/github/languages/top/oskvr37/tiddl?style=for-the-badge)
![PyPI - Version](https://img.shields.io/pypi/v/tiddl?style=for-the-badge)
Expand All @@ -11,6 +10,9 @@ Fully typed, only 2 requirements.
It's inspired by [Tidal-Media-Downloader](https://github.com/yaronzz/Tidal-Media-Downloader) - currently not mantained project.
This repository will contain features requests from that project and will be the enhanced version.

> [!WARNING]
> This app is for personal use only and is not affiliated with Tidal. Users must ensure their use complies with Tidal's terms of service and local copyright laws. Downloaded tracks are for personal use and may not be shared or redistributed. The developer assumes no responsibility for misuse of this app.
# Installation

Install package using `pip`
Expand All @@ -19,84 +21,74 @@ Install package using `pip`
pip install tiddl
```

After installation you can use `tiddl` to set up auth token
Run the package cli with `tiddl`

```bash
$ tiddl
> go to https://link.tidal.com/xxxxx and add device!
authenticated!
token expires in 7 days
```

Use `tiddl -h` to show help message
Usage: tiddl [OPTIONS] COMMAND [ARGS]...

# CLI
TIDDL - Download Tidal tracks ✨

After authentication - when your token is ready - you can start downloading!
Options:
-v, --verbose Show debug logs
--help Show this message and exit.

You can download `tracks` `albums` `playlists` `artists albums`
Commands:
...
```

- `tiddl -s -q high` sets high quality as default quality
- `tiddl <input>` downloads with high quality
- `tiddl <input> -q master` downloads with best possible quality
- `tiddl 284165609 -p my_folder -o "{artist} - {title}"` downloads track to `my_folder/{artist} - {title}.flac`
- `tiddl track/284165609 -p my_folder -o "{artist} - {title}" -s` same as above, but saves `my_folder` as default download path and `{artist} - {title}` as default file format
# Basic usage

### Valid input
Login with Tidal account

- 284165609 (will treat this as track id)
- https://tidal.com/browse/track/284165609
- track/284165609
- https://listen.tidal.com/album/284165608/track/284165609
- https://listen.tidal.com/album/284165608
- album/284165608
- https://listen.tidal.com/artist/7695548
- artist/7695548
- https://listen.tidal.com/playlist/803be625-97e4-4cbb-88dd-43f0b1c61ed7
- playlist/803be625-97e4-4cbb-88dd-43f0b1c61ed7
```bash
tiddl auth login
```

### File formatting
Download track / album / artist / playlist

| Key | Example | Comment |
| --------------- | ------------------------- | ------------------------------------------------------------- |
| title | Money Trees | |
| artist | Kendrick Lamar | |
| artists | Kendrick Lamar, Jay Rock | |
| album | good kid, m.A.A.d city | |
| number | 5 | number on album |
| disc_number | 1 | number of album volume |
| released | 10/22/2012 | release date |
| year | 2012 | year of release date |
| playlist | Kendrick Lamar Essentials | title of playlist will only appear when you download playlist |
| playlist_number | 15 | index of track on the playlist |
| id | 20556797 | id on Tidal |
```bash
tiddl url https://listen.tidal.com/track/103805726 download
tiddl url https://listen.tidal.com/album/103805723 download
tiddl url https://listen.tidal.com/artist/25022 download
tiddl url https://listen.tidal.com/playlist/84974059-76af-406a-aede-ece2b78fa372 download
```

# Modules
> [!TIP]
> You don't have to paste full urls, track/103805726, album/103805723 etc. will also work
You can also use TIDDL as module, it's fully typed so you will get type hints
Set download quality and output format

```python
from tiddl import TidalApi, Config
```bash
tiddl ... download -q master -o "{artist}/{title} ({album})"
```

config = Config()
This command will:
- download with highest quality
- save track with title and album name in artist folder

api = TidalApi(
config["token"],
config["user"]["user_id"],
config["user"]["country_code"]
)
> [!NOTE]
> More about file templating [on wiki](https://github.com/oskvr37/tiddl/wiki/Template-formatting).
album_id = 284165608
# Development

album = api.getAlbum(album_id)
Clone the repository

print(f"{album["title"]} has {album["numberOfTracks"]} tracks!")
```bash
git clone https://github.com/oskvr37/tiddl
```

# Testing
Install package with `--editable` flag

```bash
pip install -e .
```
python -m unittest tiddl/tests.py

Run tests

```bash
python -m unittest
```

# Resources
Expand Down
29 changes: 29 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "tiddl"
version = "2.0.0"
description = "Download Tidal tracks with CLI downloader."
readme = "README.md"
requires-python = ">=3.11"
authors = [{ name = "oskvr37" }]
classifiers = [
"Environment :: Console",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
dependencies = [
"pydantic>=2.9.2",
"requests>=2.20.0",
"click>=8.1.7",
"mutagen>=1.47.0",
"ffmpeg-python>=0.2.0",
]

[project.urls]
homepage = "https://github.com/oskvr37/tiddl"

[project.scripts]
tiddl = "tiddl.cli:cli"
3 changes: 0 additions & 3 deletions requirements.txt

This file was deleted.

15 changes: 0 additions & 15 deletions setup.py

This file was deleted.

Empty file added tests/__init__.py
Empty file.
78 changes: 78 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import unittest

from tiddl.config import Config
from tiddl.api import TidalApi


class TestApi(unittest.TestCase):
api: TidalApi

def setUp(self):
config = Config.fromFile()
auth = config.auth

token, user_id, country_code = (
auth.token,
auth.user_id,
auth.country_code
)

assert token, "No token found in config file"
assert user_id, "No user_id found in config file"
assert country_code, "No country_code found in config file"

self.api = TidalApi(token, user_id, country_code)

def test_ready(self):
session = self.api.getSession()

self.assertEqual(session.userId, int(self.api.user_id))
self.assertEqual(session.countryCode, self.api.country_code)

def test_track(self):
track = self.api.getTrack(103805726)
self.assertEqual(track.title, "Stronger")

def test_artist(self):
artist = self.api.getArtist(25022)
self.assertEqual(artist.name, "Kanye West")

def test_artist_albums(self):
self.api.getArtistAlbums(25022, filter="ALBUMS")
self.api.getArtistAlbums(25022, filter="EPSANDSINGLES")

def test_album(self):
album = self.api.getAlbum(103805723)
self.assertEqual(album.title, "Graduation")

def test_album_items(self):
album_items = self.api.getAlbumItems(103805723, limit=10)
self.assertEqual(len(album_items.items), 10)

album_items = self.api.getAlbumItems(103805723, limit=10, offset=10)
self.assertEqual(len(album_items.items), 4)

def test_playlist(self):
playlist = self.api.getPlaylist("84974059-76af-406a-aede-ece2b78fa372")
self.assertEqual(playlist.title, "Kanye West Essentials")

def test_playlist_items(self):
playlist_items = self.api.getPlaylistItems(
"84974059-76af-406a-aede-ece2b78fa372"
)
self.assertEqual(len(playlist_items.items), 25)

def test_favorites(self):
favorites = self.api.getFavorites()
self.assertGreaterEqual(len(favorites.PLAYLIST), 0)
self.assertGreaterEqual(len(favorites.ALBUM), 0)
self.assertGreaterEqual(len(favorites.VIDEO), 0)
self.assertGreaterEqual(len(favorites.TRACK), 0)
self.assertGreaterEqual(len(favorites.ARTIST), 0)

def test_search(self):
self.api.getSearch("Kanye West")


if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit 0889b75

Please sign in to comment.