Added config and flag for saving m3u file (#158)

*  Added `save_playlist_m3u` flag

*  Changed scan path flag to `--scan-path`

* ♻️ Refactored, edited logs
This commit is contained in:
Oskar Dudziński
2025-09-25 18:51:15 +02:00
committed by GitHub
parent bc66861f94
commit 3b12f92bd2
3 changed files with 40 additions and 16 deletions
+14 -5
View File
@@ -96,10 +96,17 @@ from typing import List, Union
help="Enable downloading videos",
)
@click.option(
"--scan_path",
"--scan-path",
"SCAN_PATH",
type=str,
help="Base music directory to scan for existing. Default is 'path'",
help="Base directory to scan for existing tracks. Default is 'path'",
)
@click.option(
"--save-m3u",
"-m3u",
"SAVE_M3U",
is_flag=True,
help="Save M3U file for playlists.",
)
@passContext
def DownloadCommand(
@@ -113,6 +120,7 @@ def DownloadCommand(
EMBED_LYRICS: bool,
DOWNLOAD_VIDEO: bool,
SCAN_PATH: str | None,
SAVE_M3U: bool,
):
"""Download resources"""
DOWNLOAD_VIDEO = DOWNLOAD_VIDEO or ctx.obj.config.download.download_video
@@ -131,6 +139,7 @@ def DownloadCommand(
EMBED_LYRICS,
DOWNLOAD_VIDEO,
SCAN_PATH,
SAVE_M3U,
)
)
@@ -371,7 +380,7 @@ def DownloadCommand(
offset += album_items.limit
def handleResource(resource: TidalResource) -> None:
logging.debug(f"Handling Resource '{resource}'")
logging.debug(f"'{resource}'")
match resource.type:
case "track":
@@ -428,7 +437,7 @@ def DownloadCommand(
case "playlist":
playlist = api.getPlaylist(resource.id)
logging.info(f"Playlist {playlist.title!r}")
logging.info(f"downloading playlist {playlist.title!r}")
offset = 0
playlist_path = None
playlist_tracks: dict[str, Track] = {}
@@ -462,7 +471,7 @@ def DownloadCommand(
path = Path(PATH) if PATH else ctx.obj.config.download.path
if playlist_path:
if playlist_path and SAVE_M3U:
savePlaylistM3U(
playlist_tracks=playlist_tracks,
path=path / playlist_path,
+1
View File
@@ -31,6 +31,7 @@ class DownloadConfig(BaseModel):
embed_lyrics: bool = False
download_video: bool = False
scan_path: Path | None = path
save_playlist_m3u: bool = False
class AuthConfig(BaseModel):
+25 -11
View File
@@ -3,6 +3,7 @@ import os
import logging
from ffmpeg_asyncio import FFmpeg
from ffmpeg_asyncio.types import Option as FFmpegOption
from pydantic import BaseModel
from urllib.parse import urlparse
@@ -184,7 +185,6 @@ def findTrackFilename(
return full_file_name
async def convertFileExtension(
source_file: Path,
extension: str,
@@ -210,9 +210,11 @@ async def convertFileExtension(
logging.debug("Conversion not required, already %s", extension)
return source_file
ffmpeg_args = {"loglevel": "error"}
ffmpeg_args: dict[str, FFmpegOption | None] = {"loglevel": "error"}
if copy_audio:
ffmpeg_args["acodec"] = "copy"
if is_video:
ffmpeg_args["vcodec"] = "copy"
@@ -220,21 +222,23 @@ async def convertFileExtension(
logging.debug("Trying conversion")
ffmpeg = FFmpeg().option("y")
ffmpeg.input(str(source_file))
ffmpeg.output(str(output_file), **ffmpeg_args)
ffmpeg.output(str(output_file), ffmpeg_args)
@ffmpeg.on("completed")
def on_completed():
logging.debug("Conversion successful for: %s", output_file)
logging.debug(f"converted {output_file}")
if remove_source:
try:
os.remove(source_file)
except OSError as e:
logging.error(f"Error removing source file {source_file}: {e}")
logging.error(f"can't remove source file {source_file}: {e}")
await ffmpeg.execute()
except Exception as e:
logging.error(f"FFMPEG Error during conversion of {source_file}: {e}")
logging.error(f"can't convert file {source_file}: {e}")
return source_file
return output_file
@@ -242,13 +246,23 @@ def savePlaylistM3U(
playlist_tracks: dict[str, Track], path: Path, filename="playlist.m3u"
):
file = path / filename
logging.debug(f"saving m3u file at {file}")
if not playlist_tracks:
logging.warning(f"playlist {file} is empty")
return
with file.open("w", encoding="utf-8") as f:
f.write("#EXTM3U\n")
for track_path, track in playlist_tracks.items():
f.write(
f"#EXTINF:{track.duration},{track.artist.name} - {track.title}\n{track_path}\n"
try:
with file.open("w", encoding="utf-8") as f:
f.write("#EXTM3U\n")
for track_path, track in playlist_tracks.items():
f.write(
f"#EXTINF:{track.duration},{track.artist.name if track.artist else ''} - {track.title}\n{track_path}\n"
)
logging.debug(
f"saved m3u file as {file} with {len(playlist_tracks)} tracks"
)
except Exception as e:
logging.error(f"can't save playlist m3u file: {e}")