Beispiel #1
0
    def backup_to_disk(self):
        """
        RETURNS `~`

        backs up details of songObj's yet to be downloaded to a .spotdlTrackingFile
        """
        # remove tracking file if no songs left in queue
        # ! we use 'return None' as a convenient exit point
        if len(self.song_list) == 0:
            if self.save_file and self.save_file.is_file():
                self.save_file.unlink()

            return None

        # prepare datadumps of all songObj's yet to be downloaded
        song_data_dumps = [song.data_dump for song in self.song_list]

        # ! the default naming of a tracking file is $nameOfFirstSOng.spotdlTrackingFile,
        # ! it needs a little fixing because of disallowed characters in file naming
        if not self.save_file:
            song_name = self.song_list[0].song_name

            song_name = format_name(song_name)

            self.save_file = Path(song_name + ".spotdlTrackingFile")

        # save encoded string to a file
        with open(self.save_file, "wb") as file_handle:
            file_handle.write(str(song_data_dumps).encode())
    def create_file_name(song_name: str, song_artists: List[str]) -> str:
        # build file name of converted file
        # the main artist is always included
        artist_string = song_artists[0]

        # ! we eliminate contributing artist names that are also in the song name, else we
        # ! would end up with things like 'Jetta, Mastubs - I'd love to change the world
        # ! (Mastubs REMIX).mp3' which is kinda an odd file name.
        for artist in song_artists[1:]:
            if artist.lower() not in song_name.lower():
                artist_string += ", " + artist

        converted_file_name = artist_string + " - " + song_name

        return format_name(converted_file_name)
def from_playlist(
    playlist_url: str,
    output_format: str = None,
    use_youtube: bool = False,
    lyrics_provider: str = None,
    generate_m3u: bool = False,
    threads: int = 1,
    path_template: str = None,
) -> List[SongObject]:
    """
    Create and return list containing SongObject for every song in the playlist

    `str` `album_url` : Spotify Url of the album whose tracks are to be retrieved
    `str` `output_format` : output format of the song

    returns a `list<SongObject>` containing Url's of each track in the given album
    """

    spotify_client = SpotifyClient()
    tracks = []

    playlist_response = spotify_client.playlist_tracks(playlist_url)
    playlist = spotify_client.playlist(playlist_url)
    if playlist_response is None:
        raise ValueError("Wrong playlist id")

    playlist_tracks = [
        track
        for track in playlist_response["items"]
        # check if track has id
        if track is not None
        and track.get("track") is not None
        and track["track"].get("id") is not None
    ]

    # Get all tracks from playlist
    while playlist_response["next"]:
        playlist_response = spotify_client.next(playlist_response)

        # Failed to get response, break the loop
        if playlist_response is None:
            break

        playlist_tracks.extend(playlist_response["items"])

    # Remove songs  without id
    playlist_tracks = [
        track
        for track in playlist_tracks
        if track is not None
        and track.get("track") is not None
        and track["track"].get("id") is not None
    ]

    def get_song(track):
        try:
            song = from_spotify_url(
                "https://open.spotify.com/track/" + track["track"]["id"],
                output_format,
                use_youtube,
                lyrics_provider,
                playlist,
            )

            if generate_m3u:
                if path_template:
                    file_path = _parse_path_template(path_template, song, output_format)
                else:
                    file_path = _get_converted_file_path(song, output_format)

                return song, f"{file_path}\n"

            return song, None
        except (LookupError, ValueError):
            return None, None
        except OSError:
            if generate_m3u:
                song_obj = SongObject(
                    track["track"], {}, {}, None, "", playlist_response
                )
                if path_template:
                    file_path = _parse_path_template(
                        path_template, song_obj, output_format
                    )
                else:
                    file_path = _get_converted_file_path(song_obj, output_format)

                return None, f"{file_path}\n"

            return None, None

    with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor:
        results = executor.map(get_song, playlist_tracks)

    playlist_text = ""
    for result in results:
        if result[1] is not None:
            playlist_text += result[1]

        if result[0] is not None and result[0].youtube_link is not None:
            tracks.append(result[0])

    if playlist_response and generate_m3u is True:
        playlist_data = spotify_client.playlist(playlist_url)

        if playlist_data is not None:
            playlist_name = playlist_data["name"]
        else:
            playlist_name = playlist_tracks[0]["track"]["name"]

        playlist_name = format_name(playlist_name)

        playlist_file = Path(f"{playlist_name}.m3u")

        with open(playlist_file, "w", encoding="utf-8") as file:
            file.write(playlist_text)

    return tracks
def _sanitize_filename(input_str: str) -> str:
    return format_name(input_str)