예제 #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
예제 #2
0
    def get_tracks(track):
        try:
            song = from_spotify_url(
                "https://open.spotify.com/track/" + track["id"],
                output_format,
                use_youtube,
            )

            if generate_m3u:
                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:
                file_path = (str(
                    provider_utils._create_song_title(
                        track["name"],
                        [artist["name"] for artist in track["artists"]],
                    )) + "." + output_format
                             if output_format is not None else "mp3")

                if len(file_path) > 256:
                    file_path = (str(
                        provider_utils._create_song_title(
                            track["name"], [track["artists"][0]["name"]])) +
                                 "." + output_format
                                 if output_format is not None else "mp3")

                return None, f"{file_path}\n"

            return None, None
예제 #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