mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			245 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import subprocess
 | 
						|
import os
 | 
						|
 | 
						|
from spotdl import const
 | 
						|
from spotdl import internals
 | 
						|
from spotdl import spotify_tools
 | 
						|
from spotdl import youtube_tools
 | 
						|
from spotdl import convert
 | 
						|
from spotdl import metadata
 | 
						|
from spotdl import downloader
 | 
						|
 | 
						|
import pytest
 | 
						|
import loader
 | 
						|
 | 
						|
loader.load_defaults()
 | 
						|
 | 
						|
SPOTIFY_TRACK_URL = "https://open.spotify.com/track/3SipFlNddvL0XNZRLXvdZD"
 | 
						|
EXPECTED_YOUTUBE_TITLE = "Janji - Heroes Tonight (feat. Johnning) [NCS Release]"
 | 
						|
EXPECTED_SPOTIFY_TITLE = "Janji - Heroes Tonight"
 | 
						|
EXPECTED_YOUTUBE_URL = "http://youtube.com/watch?v=3nQNiWdeH2Q"
 | 
						|
# GIST_URL is the monkeypatched version of: https://www.youtube.com/results?search_query=janji+-+heroes
 | 
						|
# so that we get same results even if YouTube changes the list/order of videos on their page.
 | 
						|
GIST_URL = "https://gist.githubusercontent.com/ritiek/e731338e9810e31c2f00f13c249a45f5/raw/c11a27f3b5d11a8d082976f1cdd237bd605ec2c2/search_results.html"
 | 
						|
 | 
						|
 | 
						|
def pytest_namespace():
 | 
						|
    # XXX: We override the value of `content_fixture` later in the tests.
 | 
						|
    # We do not use an acutal @pytest.fixture because it does not accept
 | 
						|
    # the monkeypatch parameter and we need to monkeypatch the network
 | 
						|
    # request before creating the Pafy object.
 | 
						|
    return {"content_fixture": None}
 | 
						|
 | 
						|
 | 
						|
@pytest.fixture(scope="module")
 | 
						|
def metadata_fixture():
 | 
						|
    meta_tags = spotify_tools.generate_metadata(SPOTIFY_TRACK_URL)
 | 
						|
    return meta_tags
 | 
						|
 | 
						|
 | 
						|
def test_metadata(metadata_fixture):
 | 
						|
    expect_number = 24
 | 
						|
    assert len(metadata_fixture) == expect_number
 | 
						|
 | 
						|
 | 
						|
class TestFileFormat:
 | 
						|
    def test_with_spaces(self, metadata_fixture):
 | 
						|
        title = internals.format_string(const.args.file_format, metadata_fixture)
 | 
						|
        assert title == EXPECTED_SPOTIFY_TITLE
 | 
						|
 | 
						|
    def test_without_spaces(self, metadata_fixture):
 | 
						|
        const.args.no_spaces = True
 | 
						|
        title = internals.format_string(const.args.file_format, metadata_fixture)
 | 
						|
        assert title == EXPECTED_SPOTIFY_TITLE.replace(" ", "_")
 | 
						|
 | 
						|
 | 
						|
def test_youtube_url(metadata_fixture, monkeypatch):
 | 
						|
    monkeypatch.setattr(
 | 
						|
        youtube_tools.GenerateYouTubeURL,
 | 
						|
        "_fetch_response",
 | 
						|
        loader.monkeypatch_youtube_search_page,
 | 
						|
    )
 | 
						|
    url = youtube_tools.generate_youtube_url(SPOTIFY_TRACK_URL, metadata_fixture)
 | 
						|
    assert url == EXPECTED_YOUTUBE_URL
 | 
						|
 | 
						|
 | 
						|
def test_youtube_title(metadata_fixture, monkeypatch):
 | 
						|
    monkeypatch.setattr(
 | 
						|
        youtube_tools.GenerateYouTubeURL,
 | 
						|
        "_fetch_response",
 | 
						|
        loader.monkeypatch_youtube_search_page,
 | 
						|
    )
 | 
						|
    content = youtube_tools.go_pafy(SPOTIFY_TRACK_URL, metadata_fixture)
 | 
						|
    pytest.content_fixture = content
 | 
						|
    title = youtube_tools.get_youtube_title(content)
 | 
						|
    assert title == EXPECTED_YOUTUBE_TITLE
 | 
						|
 | 
						|
 | 
						|
@pytest.fixture(scope="module")
 | 
						|
def filename_fixture(metadata_fixture):
 | 
						|
    songname = internals.format_string(const.args.file_format, metadata_fixture)
 | 
						|
    filename = internals.sanitize_title(songname)
 | 
						|
    return filename
 | 
						|
 | 
						|
 | 
						|
def test_check_track_exists_before_download(tmpdir, metadata_fixture, filename_fixture):
 | 
						|
    expect_check = False
 | 
						|
    const.args.folder = str(tmpdir)
 | 
						|
    # prerequisites for determining filename
 | 
						|
    track_existence = downloader.CheckExists(filename_fixture, metadata_fixture)
 | 
						|
    check = track_existence.already_exists(SPOTIFY_TRACK_URL)
 | 
						|
    assert check == expect_check
 | 
						|
 | 
						|
 | 
						|
class TestDownload:
 | 
						|
    def blank_audio_generator(self, filepath):
 | 
						|
        if filepath.endswith(".m4a"):
 | 
						|
            cmd = "ffmpeg -f lavfi -i anullsrc -t 1 -c:a aac {}".format(filepath)
 | 
						|
        elif filepath.endswith(".webm"):
 | 
						|
            cmd = "ffmpeg -f lavfi -i anullsrc -t 1 -c:a libopus {}".format(filepath)
 | 
						|
        subprocess.call(cmd.split(" "))
 | 
						|
 | 
						|
    def test_m4a(self, monkeypatch, filename_fixture):
 | 
						|
        expect_download = True
 | 
						|
        monkeypatch.setattr(
 | 
						|
            "pafy.backend_shared.BaseStream.download", self.blank_audio_generator
 | 
						|
        )
 | 
						|
        monkeypatch.setattr(
 | 
						|
            "pafy.backend_youtube_dl.YtdlStream.download", self.blank_audio_generator
 | 
						|
        )
 | 
						|
        download = youtube_tools.download_song(
 | 
						|
            filename_fixture + ".m4a", pytest.content_fixture
 | 
						|
        )
 | 
						|
        assert download == expect_download
 | 
						|
 | 
						|
    def test_webm(self, monkeypatch, filename_fixture):
 | 
						|
        expect_download = True
 | 
						|
        monkeypatch.setattr(
 | 
						|
            "pafy.backend_shared.BaseStream.download", self.blank_audio_generator
 | 
						|
        )
 | 
						|
        monkeypatch.setattr(
 | 
						|
            "pafy.backend_youtube_dl.YtdlStream.download", self.blank_audio_generator
 | 
						|
        )
 | 
						|
        download = youtube_tools.download_song(
 | 
						|
            filename_fixture + ".webm", pytest.content_fixture
 | 
						|
        )
 | 
						|
        assert download == expect_download
 | 
						|
 | 
						|
 | 
						|
class TestFFmpeg:
 | 
						|
    def test_convert_from_webm_to_mp3(self, filename_fixture, monkeypatch):
 | 
						|
        expect_command = "ffmpeg -y -nostdin -hide_banner -nostats -v panic -i {0}.webm -codec:a libmp3lame -ar 44100 -b:a 192k -vn {0}.mp3".format(
 | 
						|
            os.path.join(const.args.folder, filename_fixture)
 | 
						|
        )
 | 
						|
        monkeypatch.setattr("os.remove", lambda x: None)
 | 
						|
        _, command = convert.song(
 | 
						|
            filename_fixture + ".webm", filename_fixture + ".mp3", const.args.folder
 | 
						|
        )
 | 
						|
        assert " ".join(command) == expect_command
 | 
						|
 | 
						|
    def test_convert_from_webm_to_m4a(self, filename_fixture, monkeypatch):
 | 
						|
        expect_command = "ffmpeg -y -nostdin -hide_banner -nostats -v panic -i {0}.webm -cutoff 20000 -codec:a aac -ar 44100 -b:a 192k -vn {0}.m4a".format(
 | 
						|
            os.path.join(const.args.folder, filename_fixture)
 | 
						|
        )
 | 
						|
        monkeypatch.setattr("os.remove", lambda x: None)
 | 
						|
        _, command = convert.song(
 | 
						|
            filename_fixture + ".webm", filename_fixture + ".m4a", const.args.folder
 | 
						|
        )
 | 
						|
        assert " ".join(command) == expect_command
 | 
						|
 | 
						|
    def test_convert_from_m4a_to_mp3(self, filename_fixture, monkeypatch):
 | 
						|
        expect_command = "ffmpeg -y -nostdin -hide_banner -nostats -v panic -i {0}.m4a -codec:v copy -codec:a libmp3lame -ar 44100 -b:a 192k -vn {0}.mp3".format(
 | 
						|
            os.path.join(const.args.folder, filename_fixture)
 | 
						|
        )
 | 
						|
        monkeypatch.setattr("os.remove", lambda x: None)
 | 
						|
        _, command = convert.song(
 | 
						|
            filename_fixture + ".m4a", filename_fixture + ".mp3", const.args.folder
 | 
						|
        )
 | 
						|
        assert " ".join(command) == expect_command
 | 
						|
 | 
						|
    def test_convert_from_m4a_to_webm(self, filename_fixture, monkeypatch):
 | 
						|
        expect_command = "ffmpeg -y -nostdin -hide_banner -nostats -v panic -i {0}.m4a -codec:a libopus -vbr on -b:a 192k -vn {0}.webm".format(
 | 
						|
            os.path.join(const.args.folder, filename_fixture)
 | 
						|
        )
 | 
						|
        monkeypatch.setattr("os.remove", lambda x: None)
 | 
						|
        _, command = convert.song(
 | 
						|
            filename_fixture + ".m4a", filename_fixture + ".webm", const.args.folder
 | 
						|
        )
 | 
						|
        assert " ".join(command) == expect_command
 | 
						|
 | 
						|
    def test_convert_from_m4a_to_flac(self, filename_fixture, monkeypatch):
 | 
						|
        expect_command = "ffmpeg -y -nostdin -hide_banner -nostats -v panic -i {0}.m4a -codec:a flac -ar 44100 -b:a 192k -vn {0}.flac".format(
 | 
						|
            os.path.join(const.args.folder, filename_fixture)
 | 
						|
        )
 | 
						|
        monkeypatch.setattr("os.remove", lambda x: None)
 | 
						|
        _, command = convert.song(
 | 
						|
            filename_fixture + ".m4a", filename_fixture + ".flac", const.args.folder
 | 
						|
        )
 | 
						|
        assert " ".join(command) == expect_command
 | 
						|
 | 
						|
    def test_correct_container_for_m4a(self, filename_fixture, monkeypatch):
 | 
						|
        expect_command = "ffmpeg -y -nostdin -hide_banner -nostats -v panic -i {0}.m4a.temp -acodec copy -b:a 192k -vn {0}.m4a".format(
 | 
						|
            os.path.join(const.args.folder, filename_fixture)
 | 
						|
        )
 | 
						|
        _, command = convert.song(
 | 
						|
            filename_fixture + ".m4a", filename_fixture + ".m4a", const.args.folder
 | 
						|
        )
 | 
						|
        assert " ".join(command) == expect_command
 | 
						|
 | 
						|
 | 
						|
class TestAvconv:
 | 
						|
    @pytest.mark.skip(reason="avconv is no longer provided with FFmpeg")
 | 
						|
    def test_convert_from_m4a_to_mp3(self, filename_fixture, monkeypatch):
 | 
						|
        monkeypatch.setattr("os.remove", lambda x: None)
 | 
						|
        expect_command = "avconv -loglevel 0 -i {0}.m4a -ab 192k {0}.mp3 -y".format(
 | 
						|
            os.path.join(const.args.folder, filename_fixture)
 | 
						|
        )
 | 
						|
        _, command = convert.song(
 | 
						|
            filename_fixture + ".m4a",
 | 
						|
            filename_fixture + ".mp3",
 | 
						|
            const.args.folder,
 | 
						|
            avconv=True,
 | 
						|
        )
 | 
						|
        assert " ".join(command) == expect_command
 | 
						|
 | 
						|
 | 
						|
@pytest.fixture(scope="module")
 | 
						|
def trackpath_fixture(filename_fixture):
 | 
						|
    trackpath = os.path.join(const.args.folder, filename_fixture)
 | 
						|
    return trackpath
 | 
						|
 | 
						|
 | 
						|
class TestEmbedMetadata:
 | 
						|
    def test_embed_in_mp3(self, metadata_fixture, trackpath_fixture):
 | 
						|
        expect_embed = True
 | 
						|
        embed = metadata.embed(trackpath_fixture + ".mp3", metadata_fixture)
 | 
						|
        assert embed == expect_embed
 | 
						|
 | 
						|
    def test_embed_in_m4a(self, metadata_fixture, trackpath_fixture):
 | 
						|
        expect_embed = True
 | 
						|
        embed = metadata.embed(trackpath_fixture + ".m4a", metadata_fixture)
 | 
						|
        os.remove(trackpath_fixture + ".m4a")
 | 
						|
        assert embed == expect_embed
 | 
						|
 | 
						|
    def test_embed_in_webm(self, metadata_fixture, trackpath_fixture):
 | 
						|
        expect_embed = False
 | 
						|
        embed = metadata.embed(trackpath_fixture + ".webm", metadata_fixture)
 | 
						|
        os.remove(trackpath_fixture + ".webm")
 | 
						|
        assert embed == expect_embed
 | 
						|
 | 
						|
    def test_embed_in_flac(self, metadata_fixture, trackpath_fixture):
 | 
						|
        expect_embed = True
 | 
						|
        embed = metadata.embed(trackpath_fixture + ".flac", metadata_fixture)
 | 
						|
        os.remove(trackpath_fixture + ".flac")
 | 
						|
        assert embed == expect_embed
 | 
						|
 | 
						|
 | 
						|
def test_check_track_exists_after_download(
 | 
						|
    metadata_fixture, filename_fixture, trackpath_fixture
 | 
						|
):
 | 
						|
    expect_check = True
 | 
						|
    track_existence = downloader.CheckExists(filename_fixture, metadata_fixture)
 | 
						|
    check = track_existence.already_exists(SPOTIFY_TRACK_URL)
 | 
						|
    os.remove(trackpath_fixture + ".mp3")
 | 
						|
    assert check == expect_check
 |