mirror of
https://github.com/oskvr37/tiddl.git
synced 2026-06-13 04:05:08 +03:00
✨ 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:
@@ -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,
|
||||
|
||||
@@ -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
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user