Files
2026-10-09 15:33:43 +02:00

85 lines
3.1 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Saved playlists: one TOML file each in ~/.config/SaraSuite/SaraTune/Playlists/,
holding the playlist's name and its files' paths in order. Also reads and
writes .m3u/.m3u8, so playlists can move to and from other players."""
import re
from dataclasses import dataclass, field
from pathlib import Path
import tomlkit
from Core import IsAudioFile, IsWindows, PlaylistsDir
@dataclass
class Playlist:
Name: str
Tracks: list[str] = field(default_factory=list)
@property
def FilePath(self) -> Path:
return PlaylistsDir / f"{SafeFileName(self.Name)}.toml"
def Save(self) -> None:
PlaylistsDir.mkdir(parents=True, exist_ok=True)
Document = tomlkit.document()
Document["Name"] = self.Name
Document["Tracks"] = tomlkit.array()
Document["Tracks"].multiline(True)
Document["Tracks"].extend(self.Tracks)
self.FilePath.write_text(tomlkit.dumps(Document), encoding="utf-8")
def Delete(self) -> None:
self.FilePath.unlink(missing_ok=True)
def SafeFileName(Name: str) -> str:
"""A name that works as a file name on Linux and Windows."""
return re.sub(r'[<>:"/\\|?*\x00-\x1f]', "_", Name).strip(" .") or "Playlist"
def LoadPlaylists() -> list[Playlist]:
Result: list[Playlist] = []
if not PlaylistsDir.is_dir():
return Result
for FilePath in PlaylistsDir.glob("*.toml"):
try:
Data = tomlkit.parse(FilePath.read_text(encoding="utf-8")).unwrap()
except (OSError, tomlkit.exceptions.TOMLKitError):
continue
Tracks = [str(Item) for Item in Data.get("Tracks", []) if isinstance(Item, str)]
Result.append(Playlist(str(Data.get("Name") or FilePath.stem), Tracks))
return sorted(Result, key=lambda Item: Item.Name.lower())
def NameTaken(Name: str, Except: str = "") -> bool:
"""Two names that make the same file count as the same playlist."""
Wanted = SafeFileName(Name).lower()
return any(SafeFileName(Item.Name).lower() == Wanted and Item.Name != Except for Item in LoadPlaylists())
def ExportM3U(Item: Playlist, Destination: Path) -> None:
Lines = ["#EXTM3U", f"#PLAYLIST:{Item.Name}"] + Item.Tracks
Destination.write_text("\n".join(Lines) + "\n", encoding="utf-8")
def ReadM3U(Source: Path) -> Playlist:
"""Relative entries count from the .m3u file's folder; URLs and missing
files are skipped."""
Name = Source.stem
Tracks: list[str] = []
for Line in Source.read_text(encoding="utf-8", errors="replace").splitlines():
Line = Line.strip().lstrip("")
if Line.startswith("#PLAYLIST:"):
Name = Line.split(":", 1)[1].strip() or Name
continue
if not Line or Line.startswith("#") or "://" in Line:
continue
# Playlists made on Windows use backslashes, which Linux paths don't split on.
Entry = Path(Line if IsWindows else Line.replace("\\", "/"))
if not Entry.is_absolute():
Entry = Source.parent / Entry
if Entry.is_file() and IsAudioFile(Entry):
Tracks.append(str(Entry.resolve()))
return Playlist(Name, Tracks)