Generating Playlists from Paths #1389
-
Is it possible to generate multiple, named playlists using scripts? I'd like to create playlists but without having to manually use the GUI.
audacious uses XML playlists which include more information than a file-path, making manual generation non-trivial. while it's possible to use the command line to Are there tools to support this? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
Look into "audtool" which comes with Audacious. |
Beta Was this translation helpful? Give feedback.
-
Thanks, for reference this is a script I have that sets up playlists for both CMUS & Audactious. Basically I'd like to be able to generate playlists and switch players without having to set up the playlists in each. #!/usr/bin/env python3
import os
import tempfile
from typing import (
List,
Tuple,
Generator,
Iterator,
Sequence,
)
PlayList = Tuple[str, List[str]]
XDG_CONFIG_HOME = os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser("~/.config")
def scandir_with_demoted_errors(path: str) -> Generator[os.DirEntry[str], None, None]:
try:
for entry in os.scandir(path):
yield entry
except Exception as ex:
print("Error: scandir", ex)
def scantree(path: str) -> Iterator[os.DirEntry[str]]:
"""Recursively yield DirEntry objects for given directory."""
for entry in scandir_with_demoted_errors(path):
if entry.is_dir(follow_symlinks=False):
yield from scantree(entry.path)
else:
yield entry
def generate_playlist(*, name: str, dirs: Sequence[str]) -> PlayList:
file_list = []
for directory in dirs:
for entry in scantree(directory):
# Skip all of these silently.
if entry.name.endswith((".jpg", ".jpeg", ".webp", ".png", ".m3u", ".rst", ".txt")):
continue
if not entry.name.endswith((".mp3", ".m4a", ".opus", ".ogg")):
print("UNKNOWN FORMAT", entry.path)
continue
file_list.append(entry.path)
file_list.sort()
return name, file_list
def write_playlists_cmus(playlists: List[PlayList]) -> None:
cmus_playlist_dir = os.path.join(XDG_CONFIG_HOME, "cmus", "playlists")
if os.path.exists(cmus_playlist_dir):
for entry in scandir_with_demoted_errors(cmus_playlist_dir):
# Unlikely but don't fail if it occurs.
if entry.is_dir(follow_symlinks=False):
continue
os.unlink(entry.path)
else:
os.makedirs(cmus_playlist_dir)
for name, file_list in playlists:
with open(os.path.join(cmus_playlist_dir, name), "w", encoding="utf-8") as fh:
for filepath in file_list:
fh.write("{:s}\n".format(filepath))
def write_playlists_audacious(playlists: List[PlayList]) -> None:
import subprocess
import signal
import time
subprocess.check_call(["audtool", "shutdown"])
proc = subprocess.Popen(["audacious", "--headless"])
# Not sure of a better way to wait until audacious starts.
time.sleep(0.1)
out = subprocess.check_output(["audtool", "number-of-playlists"])
playlists_to_remove = int(out.strip())
for i in range(playlists_to_remove):
subprocess.check_call(["audtool", "delete-current-playlist"])
subprocess.check_call(["audtool", "playqueue-clear"])
first = True
with tempfile.TemporaryDirectory() as temp_dir:
for index, (name, file_list) in enumerate(playlists):
if not first:
# It will be made active.
subprocess.check_call(["audtool", "new-playlist"])
first = False
subprocess.check_call(["audtool", "set-current-playlist-name", name])
# Slow adding files 1 by one, use a temporary playlist.
if False:
for filepath in file_list:
subprocess.check_call(["audtool", "playlist-addurl", filepath])
else:
temp_playlist = os.path.join(temp_dir, "{:d}.m3u".format(index))
with open(temp_playlist, "w", encoding="utf8") as fh:
for filepath in file_list:
fh.write("{:s}\n".format(filepath))
subprocess.check_call(["audtool", "playlist-addurl", temp_playlist])
os.unlink(temp_playlist)
subprocess.check_call(["audtool", "shutdown"])
# Can also work: `os.kill(proc.pid, signal.SIGTERM)`.
while proc.poll():
time.sleep(0.1)
def write_playlists(playlists: List[PlayList]) -> None:
write_playlists_cmus(playlists)
write_playlists_audacious(playlists)
def main() -> None:
playlists: List[PlayList] = []
# Example playlists generated from directories.
playlists.append(generate_playlist(
name="Baroque",
dirs=[
"/my/music/baroque",
"/my/music/more_baroque",
],
))
playlists.append(generate_playlist(
name="Piano",
dirs=[
"/my/music/piano",
],
))
write_playlists(playlists)
if __name__ == "__main__":
main() |
Beta Was this translation helpful? Give feedback.
Look into "audtool" which comes with Audacious.