Exemplo n.º 1
0
    def get_tracks(track):
        try:
            song = from_spotify_url(
                "https://open.spotify.com/track/" + track["id"],
                output_format,
                use_youtube,
                lyrics_provider,
                None,
            )

            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, album_response, {}, None, "", None)
                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
Exemplo n.º 2
0
    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:
                file_path = _parse_path_template(path_template, 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
                )
                file_path = _parse_path_template(path_template, song_obj, output_format)

                return None, f"{file_path}\n"

            return None, None
Exemplo n.º 3
0
    async def download_song(self, song_object: SongObject) -> None:
        """
        `song_object` `song_object` : song to be downloaded

        RETURNS `~`

        Downloads, Converts, Normalizes song & embeds metadata as ID3 tags.
        """

        display_progress_tracker = self.display_manager.new_progress_tracker(
            song_object)

        # ! since most errors are expected to happen within this function, we wrap in
        # ! exception catcher to prevent blocking on multiple downloads
        try:

            # ! all YouTube downloads are to .\Temp; they are then converted and put into .\ and
            # ! finally followed up with ID3 metadata tags

            # ! we explicitly use the os.path.join function here to ensure download is
            # ! platform agnostic

            # Create a spotdl-temp folder if not present
            temp_folder = Path("spotdl-temp")

            if not temp_folder.exists():
                temp_folder.mkdir()

            if self.arguments["path_template"] is not None:
                converted_file_path = _parse_path_template(
                    self.arguments["path_template"],
                    song_object,
                    self.arguments["output_format"],
                )
            else:
                converted_file_path = _get_converted_file_path(
                    song_object, self.arguments["output_format"])

            # if a song is already downloaded skip it
            if converted_file_path.is_file():
                if self.display_manager:
                    display_progress_tracker.notify_download_skip()
                if self.download_tracker:
                    self.download_tracker.notify_download_completion(
                        song_object)

                # ! None is the default return value of all functions, we just explicitly define
                # ! it here as a continent way to avoid executing the rest of the function.
                return None

            converted_file_path.parent.mkdir(parents=True, exist_ok=True)

            if self.arguments["output_format"] == "m4a":
                ytdl_format = "bestaudio[ext=m4a]/bestaudio/best"
            elif self.arguments["output_format"] == "opus":
                ytdl_format = "bestaudio[ext=webm]/bestaudio/best"
            else:
                ytdl_format = "bestaudio"

            # download Audio from YouTube
            audio_handler = YoutubeDL({
                "format":
                ytdl_format,
                "outtmpl":
                f"{temp_folder}/%(id)s.%(ext)s",
                "quiet":
                True,
                "no_warnings":
                True,
                "encoding":
                "UTF-8",
                "logger":
                YTDLLogger(),
                "progress_hooks":
                [display_progress_tracker.ytdl_progress_hook]
                if display_progress_tracker else [],
            })

            try:
                downloaded_file_path_string = await self._perform_audio_download_async(
                    converted_file_path.name.rsplit(".", 1)[0],
                    temp_folder,
                    audio_handler,
                    song_object.youtube_link,
                )
            except Exception:
                print(
                    f'Unable to get audio stream for "{song_object.song_name}" '
                    f'by "{song_object.contributing_artists[0]}" '
                    f'from video "{song_object.youtube_link}"')
                return None

            if downloaded_file_path_string is None:
                return None

            if display_progress_tracker:
                display_progress_tracker.notify_youtube_download_completion()

            downloaded_file_path = Path(downloaded_file_path_string)

            if (downloaded_file_path.suffix == ".m4a"
                    and self.arguments["output_format"] == "m4a"):
                downloaded_file_path.rename(converted_file_path)
                ffmpeg_success = True
            else:
                ffmpeg_success = await ffmpeg.convert(
                    downloaded_file_path=downloaded_file_path,
                    converted_file_path=converted_file_path,
                    output_format=self.arguments["output_format"],
                    ffmpeg_path=self.arguments["ffmpeg"],
                )

            if display_progress_tracker:
                display_progress_tracker.notify_conversion_completion()

            if ffmpeg_success is False:
                # delete the file that wasn't successfully converted
                converted_file_path.unlink()
            else:
                # if a file was successfully downloaded, tag it
                set_id3_data(converted_file_path, song_object,
                             self.arguments["output_format"])

            # Do the necessary cleanup
            if display_progress_tracker:
                display_progress_tracker.notify_download_completion()

            if self.download_tracker:
                self.download_tracker.notify_download_completion(song_object)

            # delete the unnecessary YouTube download File
            if downloaded_file_path and downloaded_file_path.is_file():
                downloaded_file_path.unlink()

        except Exception as e:
            tb = traceback.format_exc()
            if display_progress_tracker:
                display_progress_tracker.notify_error(e, tb)
            else:
                raise e