Files
Deezer-Downloader-Nextcloud/tests/test_deezer_downloader.py
2025-11-06 22:42:49 +01:00

368 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import unittest
import pytest
from pathlib import Path
from mutagen.mp3 import MP3
from mutagen.flac import FLAC
from mutagen import MutagenError
from deezer_downloader.configuration import load_config
if "DEEZER_DOWNLOADER_CONFIG_FILE" in os.environ:
config_file = Path(os.environ["DEEZER_DOWNLOADER_CONFIG_FILE"])
else:
config_file = (Path(__file__).parents[1] / Path("deezer_downloader") / Path("cli") / Path("deezer-downloader.ini.template")).resolve()
load_config(config_file)
from deezer_downloader.configuration import config
from deezer_downloader.deezer import init_deezer_session, TYPE_TRACK, TYPE_ALBUM
from deezer_downloader.deezer import deezer_search, get_song_infos_from_deezer_website, parse_deezer_playlist, download_song, get_deezer_favorites
from deezer_downloader.deezer import Deezer404Exception, DeezerApiException
from deezer_downloader.spotify import get_songs_from_spotify_website, SpotifyWebsiteParserException, parse_uri, SpotifyInvalidUrlException
from deezer_downloader.youtubedl import youtubedl_download, YoutubeDLFailedException, DownloadedFileNotFoundException
known_song_keys = ["SNG_ID", "DURATION", "MD5_ORIGIN", "SNG_TITLE", "TRACK_NUMBER",
"ALB_PICTURE", "MEDIA_VERSION", "ART_NAME", "ALB_TITLE"]
init_deezer_session(config['proxy']['server'],
config["deezer"]["quality"])
class TestDeezerMethods(unittest.TestCase):
# BEGIN: TEST deezer_search
def test_deezer_search_song_valid(self):
songs = deezer_search("Großstadtgeflüster diadem", TYPE_TRACK)
self.assertIsInstance(songs, list)
s = songs[0]
self.assertSetEqual(set(s.keys()), {'preview_url', 'artist', 'id', 'id_type', 'album_id', 'title', 'img_url', 'album'})
self.assertTrue(s['id'], '730393272')
self.assertTrue(s['title'], 'Diadem')
self.assertTrue(s['artist'], 'Grossstadtgeflüster')
self.assertTrue(s['album'], 'Tips & Tricks')
self.assertTrue(s['preview_url'], 'https://cdns-preview-6.dzcdn.net/stream/c-6abdd540dd7e7f02d2c4d21537709c23-3.mp3')
self.assertTrue(s['album_id'], '107261872')
self.assertTrue(s['id_type'], 'track')
def test_deezer_search_album_valid(self):
albums = deezer_search("Coldplay", TYPE_ALBUM)
self.assertIsInstance(albums, list)
for album in albums:
self.assertSetEqual(set(album.keys()), {'id', 'id_type', 'album', 'album_id', 'img_url', 'artist', 'title', 'preview_url'})
found_album_names = [x['album'] for x in albums]
known_album_names = ['Parachutes', 'X&Y', 'A Head Full of Dreams']
for known_album_name in known_album_names:
self.assertIn(known_album_name, found_album_names)
def test_deezer_search_invalid_search_typ(self):
songs = deezer_search("Coldplay", "this is a wrong search type")
self.assertIsInstance(songs, list)
self.assertListEqual(songs, [])
def test_deezer_search_song_invalid_no_song_found(self):
songs = deezer_search("8f49834zf934fdshfkhejw", TYPE_TRACK)
self.assertIsInstance(songs, list)
self.assertListEqual(songs, [])
def test_deezer_search_album_invalid_no_song_found(self):
songs = deezer_search("8f49834zf934fdshfkhejw", TYPE_ALBUM)
self.assertIsInstance(songs, list)
self.assertListEqual(songs, [])
# END: TEST deezer_search
# BEGIN: TEST get_song_infos_from_deezer_website
def test_get_track_infos_from_website(self):
song = get_song_infos_from_deezer_website(TYPE_TRACK, "69962764")
self.assertIsInstance(song, dict)
song_keys = list(song.keys())
for key in known_song_keys:
self.assertIn(key, song_keys)
self.assertEqual(song["SNG_ID"], "69962764")
self.assertEqual(song["ART_NAME"], "The Clash")
self.assertEqual(song["SNG_TITLE"], "Should I Stay or Should I Go")
# MD5_ORIGIN changes over time
#self.assertEqual(song["MD5_ORIGIN"], "df51967a8b9b88d079fb0d9f4a0c1c38")
self.assertEqual(len(song["MD5_ORIGIN"]), 32)
def test_get_album_infos_from_website(self):
songs = get_song_infos_from_deezer_website(TYPE_ALBUM, "1434890")
self.assertIsInstance(songs, list)
self.assertEqual(len(songs), 15)
for song in songs:
song_keys = list(song.keys())
for key in known_song_keys:
self.assertIn(key, song_keys)
self.assertEqual(songs[0]["SNG_ID"], "15523769")
self.assertEqual(songs[0]["ART_NAME"], "System of A Down")
self.assertEqual(songs[0]["SNG_TITLE"], "Prison Song")
self.assertEqual(len(songs[14]["MD5_ORIGIN"]), 32)
self.assertEqual(songs[1]["SNG_ID"], "15523770")
self.assertEqual(songs[1]["ART_NAME"], "System of A Down")
self.assertEqual(songs[1]["SNG_TITLE"], "Needles")
self.assertEqual(songs[2]["SNG_ID"], "15523772")
self.assertEqual(songs[2]["ART_NAME"], "System of A Down")
self.assertEqual(songs[2]["SNG_TITLE"], "Deer Dance")
self.assertEqual(songs[3]["SNG_ID"], "15523775")
self.assertEqual(songs[3]["ART_NAME"], "System of A Down")
self.assertEqual(songs[3]["SNG_TITLE"], "Jet Pilot")
self.assertEqual(songs[4]["SNG_ID"], "15523778")
self.assertEqual(songs[4]["ART_NAME"], "System of A Down")
self.assertEqual(songs[4]["SNG_TITLE"], "X")
self.assertEqual(songs[5]["SNG_ID"], "15523781")
self.assertEqual(songs[5]["ART_NAME"], "System of A Down")
self.assertEqual(songs[5]["SNG_TITLE"], "Chop Suey!")
self.assertEqual(songs[6]["SNG_ID"], "15523784")
self.assertEqual(songs[6]["ART_NAME"], "System of A Down")
self.assertEqual(songs[6]["SNG_TITLE"], "Bounce")
self.assertEqual(songs[7]["SNG_ID"], "15523788")
self.assertEqual(songs[7]["ART_NAME"], "System of A Down")
self.assertEqual(songs[7]["SNG_TITLE"], "Forest")
self.assertEqual(songs[8]["SNG_ID"], "15523790")
self.assertEqual(songs[8]["ART_NAME"], "System of A Down")
self.assertEqual(songs[8]["SNG_TITLE"], "ATWA")
self.assertEqual(songs[9]["SNG_ID"], "15523791")
self.assertEqual(songs[9]["ART_NAME"], "System of A Down")
self.assertEqual(songs[9]["SNG_TITLE"], "Science")
self.assertEqual(songs[10]["SNG_ID"], "15523792")
self.assertEqual(songs[10]["ART_NAME"], "System of A Down")
self.assertEqual(songs[10]["SNG_TITLE"], "Shimmy")
self.assertEqual(songs[11]["SNG_ID"], "15523793")
self.assertEqual(songs[11]["ART_NAME"], "System of A Down")
self.assertEqual(songs[11]["SNG_TITLE"], "Toxicity")
self.assertEqual(songs[12]["SNG_ID"], "15523796")
self.assertEqual(songs[12]["ART_NAME"], "System of A Down")
self.assertEqual(songs[12]["SNG_TITLE"], "Psycho")
self.assertEqual(songs[13]["SNG_ID"], "15523799")
self.assertEqual(songs[13]["ART_NAME"], "System of A Down")
self.assertEqual(songs[13]["SNG_TITLE"], "Aerials")
self.assertEqual(songs[14]["SNG_ID"], "15523803")
self.assertEqual(songs[14]["ART_NAME"], "System of A Down")
self.assertEqual(songs[14]["SNG_TITLE"], "Arto")
def test_get_invalid_track_infos_from_website(self):
with self.assertRaises(Deezer404Exception):
get_song_infos_from_deezer_website(TYPE_TRACK, "thisdoesnotexist")
def test_get_invalid_album_infos_from_website(self):
with self.assertRaises(Deezer404Exception):
get_song_infos_from_deezer_website(TYPE_ALBUM, "thisdoesnotexist")
# END: TEST get_song_infos_from_deezer_website
# BEGIN: parse_deezer_playlist
def _call_parse_valid_deezer_playlist(self, playlist):
playlist_name, songs = parse_deezer_playlist(playlist)
self.assertEqual(playlist_name, "test-playlist")
self.assertIsInstance(songs, list)
self.assertEqual(len(songs), 2)
for song in songs:
song_keys = list(song.keys())
for key in known_song_keys:
self.assertIn(key, song_keys)
self.assertEqual(songs[0]["SNG_ID"], "113951680")
self.assertEqual(songs[0]["ART_NAME"], 'Fredrika Stahl')
self.assertEqual(songs[0]["SNG_TITLE"], 'Make a Change')
# MD5_ORIGIN is only there if we are logged in
self.assertEqual(songs[0]["MD5_ORIGIN"], '57250623592ef44c8caeead79917f7e5')
def test_parse_valid_deezer_playlist_with_url(self):
playlist_url = "https://www.deezer.com/de/playlist/7639370122"
self._call_parse_valid_deezer_playlist(playlist_url)
def test_parse_valid_deezer_playlist_with_id(self):
playlist_id = "7639370122"
self._call_parse_valid_deezer_playlist(playlist_id)
def test_parse_invalid_deezer_playlist_with_id(self):
invalid_playlist_id = "999999999999999999999999999"
with self.assertRaises(DeezerApiException):
playlist_name, songs = parse_deezer_playlist(invalid_playlist_id)
def test_parse_invalid_input_for_deezer_playlist_with_id(self):
invalid_playlist_id = "!\"§$%&/((;-';k(()=+ü\\?"
with self.assertRaises(DeezerApiException):
playlist_name, songs = parse_deezer_playlist(invalid_playlist_id)
def test_parse_invalid_deezer_playlist_with_url(self):
invalid_playlist_url = "https://www.heise.de"
with self.assertRaises(DeezerApiException):
playlist_name, songs = parse_deezer_playlist(invalid_playlist_url)
# END: parse_deezer_playlist
# BEGIN: get_deezer_favorites
def test_get_deezer_favorites_userid_not_numeric(self):
user_id = "123notnumeric"
with self.assertRaises(Exception):
get_deezer_favorites(user_id)
def test_get_deezer_favorites_userid_api_error(self):
user_id = "0"
with self.assertRaises(Exception):
get_deezer_favorites(user_id)
def test_get_deezer_favorites_userid_valid(self):
user_id = "2517244282" # own of test (works)
songs = get_deezer_favorites(user_id)
self.assertIsInstance(songs, list)
for song in songs:
self.assertIsInstance(song, int)
# END: get_deezer_favorites
# BEGIN: download_song
@pytest.mark.skipif(config["deezer"].get("quality", "mp3") == "flac", reason="skiping deezer mp3 download. Quality set to flac")
def test_download_song_valid_mp3(self):
def check_mp3_metadata_deezer_song(test_file: Path):
self.assertTrue(test_song.exists())
try:
audio = MP3(test_file)
except MutagenError as e:
pytest.fail(f"File is not in mp3 format: {e}")
self.assertEqual(audio['TPE1'].text[0], 'Faber')
self.assertEqual(audio['TALB'].text[0], 'Alles Gute')
self.assertEqual(audio['TIT2'].text[0], 'Tausendfrankenlang')
self.assertEqual(audio['TRCK'].text[0], '4')
self.assertEqual(audio['TDRC'].text[0].year, 2015)
self.assertEqual(audio['TPOS'].text[0], '1')
self.assertTrue('APIC:Cover' in audio)
test_song = Path("/tmp/song-548935.mp3")
test_song.unlink(missing_ok=True)
song_infos = deezer_search("faber tausendfrankenlang", TYPE_TRACK)[0]
song = get_song_infos_from_deezer_website(TYPE_TRACK, song_infos['id'])
download_song(song, test_song.as_posix())
check_mp3_metadata_deezer_song(test_song)
test_song.unlink()
@pytest.mark.skipif(config["deezer"].get("quality", "mp3") == "mp3", reason="skiping deezer flac download. Quality set to mp3")
def test_download_song_valid_flac(self):
def check_flac_metadata_deezer_song(test_file: Path):
try:
audio = FLAC(test_file)
except MutagenError as e:
pytest.fail(f"File is not in mp3 format: {e}")
assert audio["artist"] == ["Faber",]
assert audio["title"] == ['Tausendfrankenlang',]
assert audio["album"] == ['Alles Gute',]
assert audio["tracknumber"] == ['4',]
assert audio["discnumber"] == ["1",]
assert len(audio.pictures) == 1
assert audio.pictures[0].data[:5] == b'\xff\xd8\xff\xe0\x00'
test_song = Path("/tmp/song-548935.flac")
test_song.unlink(missing_ok=True)
song_infos = deezer_search("faber tausendfrankenlang", TYPE_TRACK)[0]
song = get_song_infos_from_deezer_website(TYPE_TRACK, song_infos['id'])
download_song(song, test_song.as_posix())
check_flac_metadata_deezer_song(test_song)
test_song.unlink()
def test_download_song_invalid_song_type(self):
with self.assertRaises(AssertionError):
download_song("this sould be a dict", "/not/relevant/outputfile.mp3")
# END: download_song
class TestSpotifyMethods(unittest.TestCase):
# BEGIN parse_uri
def test_parse_url_spotify(self):
res = parse_uri("spotify:album:Hksdhfaif23ffushef9823")
self.assertEqual(res['type'], "album")
self.assertEqual(res['id'], "Hksdhfaif23ffushef9823")
res = parse_uri("spotify:playlist:Hksdhfaif23ffushef9823")
self.assertEqual(res['type'], "playlist")
self.assertEqual(res['id'], "Hksdhfaif23ffushef9823")
res = parse_uri("spotify:track:Hksdhfaif23ffushef9823")
self.assertEqual(res['type'], "track")
self.assertEqual(res['id'], "Hksdhfaif23ffushef9823")
def test_parse_url_open_domain(self):
res = parse_uri("https://open.spotify.com/track/Hksdhfaif23ffushef9823")
self.assertEqual(res['type'], "track")
self.assertEqual(res['id'], "Hksdhfaif23ffushef9823")
def test_parse_url_play_domain(self):
res = parse_uri("https://play.spotify.com/track/Hksdhfaif23ffushef9823")
self.assertEqual(res['type'], "track")
self.assertEqual(res['id'], "Hksdhfaif23ffushef9823")
def test_parse_url_embed_domain(self):
res = parse_uri("https://embed.spotify.com/?uri=spotify:track:Hksdhfaif23ffushef9823")
self.assertEqual(res['type'], "track")
self.assertEqual(res['id'], "Hksdhfaif23ffushef9823")
# END parse_uri
# BEGIN test invalid
def test_spotify_parser_invalid_playlist_id(self):
playlist_id = "thisdoesnotexist"
with self.assertRaises(SpotifyWebsiteParserException):
get_songs_from_spotify_website(playlist_id, None)
def test_spotify_parser_invalid_playlist_url(self):
playlist_url = "https://www.heise.de"
with self.assertRaises(SpotifyInvalidUrlException):
get_songs_from_spotify_website(playlist_url, None)
# END test invalid
# BEGIN valid stuff
def check_parse_spotify_playlist_website(self, playlist):
songs = get_songs_from_spotify_website(playlist, None)
self.assertIn("Cyndi Lauper Time After Time", songs)
def test_spotify_parser_valid_playlist_embed_url(self):
playlist_url = "https://open.spotify.com/embed/playlist/0wl9Q3oedquNlBAJ4MGZtS"
self.check_parse_spotify_playlist_website(playlist_url)
def test_spotify_parser_valid_playlist_url(self):
playlist_url = "https://open.spotify.com/playlist/0wl9Q3oedquNlBAJ4MGZtS"
self.check_parse_spotify_playlist_website(playlist_url)
def test_spotify_parser_valid_playlist_id(self):
playlist_id = "0wl9Q3oedquNlBAJ4MGZtS"
self.check_parse_spotify_playlist_website(playlist_id)
# END valid stuff
class TestYoutubeMethods(unittest.TestCase):
is_github_ci = len(os.environ.get("GITHUB_ACTION", "")) > 0
@pytest.mark.xfail(is_github_ci, reason="Fails with 'Sign in to confirm youre not a bot. This helps protect our community. Learn more'", raises=YoutubeDLFailedException)
def test_youtube_dl_valid_url(self):
out_file = Path("/tmp/Pharrell Williams - Happy (Video).mp3")
out_file.unlink(missing_ok=True)
url = "https://www.youtube.com/watch?v=ZbZSe6N_BXs"
destination_file = youtubedl_download(url, "/tmp")
self.assertEqual(out_file.as_posix(), destination_file)
self.assertTrue(out_file.exists())
try:
audio = MP3(out_file)
self.assertEqual(audio['TPE1'].text[0], 'Pharrell Williams')
self.assertEqual(audio['TIT2'].text[0], 'Pharrell Williams - Happy (Video)')
except MutagenError as e:
pytest.fail(f"File is not in mp3 format: {e}")
out_file.unlink()
def test_youtube_dl_invalid_url(self):
url = "https://www.heise.de"
with self.assertRaises(YoutubeDLFailedException):
youtubedl_download(url, "/tmp")
@pytest.mark.xfail(is_github_ci, reason="Fails with 'Sign in to confirm youre not a bot. This helps protect our community. Learn more'", raises=YoutubeDLFailedException)
def test_youtube_dl_command_execution(self):
url = "https://www.youtube.com/watch?v=ZbZSe6N_BXs&$(touch /tmp/pwned.txt)"
try:
youtubedl_download(url, "/tmp")
except DownloadedFileNotFoundException:
pytest.xfail("Fails if the file already exists from the previous test. TODO")
pwn_succeeded = os.path.exists("/tmp/pwned.txt")
self.assertEqual(pwn_succeeded, False)
if __name__ == '__main__':
unittest.main()