Files
headphones/lib/beets/util/m3u.py
2024-08-24 16:44:41 +12:00

98 lines
3.5 KiB
Python

# This file is part of beets.
# Copyright 2022, J0J0 Todos.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
"""Provides utilities to read, write and manipulate m3u playlist files."""
import traceback
from beets.util import FilesystemError, mkdirall, normpath, syspath
class EmptyPlaylistError(Exception):
"""Raised when a playlist file without media files is saved or loaded."""
pass
class M3UFile:
"""Reads and writes m3u or m3u8 playlist files."""
def __init__(self, path):
"""``path`` is the absolute path to the playlist file.
The playlist file type, m3u or m3u8 is determined by 1) the ending
being m3u8 and 2) the file paths contained in the list being utf-8
encoded. Since the list is passed from the outside, this is currently
out of control of this class.
"""
self.path = path
self.extm3u = False
self.media_list = []
def load(self):
"""Reads the m3u file from disk and sets the object's attributes."""
pl_normpath = normpath(self.path)
try:
with open(syspath(pl_normpath), "rb") as pl_file:
raw_contents = pl_file.readlines()
except OSError as exc:
raise FilesystemError(
exc, "read", (pl_normpath,), traceback.format_exc()
)
self.extm3u = True if raw_contents[0].rstrip() == b"#EXTM3U" else False
for line in raw_contents[1:]:
if line.startswith(b"#"):
# Support for specific EXTM3U comments could be added here.
continue
self.media_list.append(normpath(line.rstrip()))
if not self.media_list:
raise EmptyPlaylistError
def set_contents(self, media_list, extm3u=True):
"""Sets self.media_list to a list of media file paths.
Also sets additional flags, changing the final m3u-file's format.
``media_list`` is a list of paths to media files that should be added
to the playlist (relative or absolute paths, that's the responsibility
of the caller). By default the ``extm3u`` flag is set, to ensure a
save-operation writes an m3u-extended playlist (comment "#EXTM3U" at
the top of the file).
"""
self.media_list = media_list
self.extm3u = extm3u
def write(self):
"""Writes the m3u file to disk.
Handles the creation of potential parent directories.
"""
header = [b"#EXTM3U"] if self.extm3u else []
if not self.media_list:
raise EmptyPlaylistError
contents = header + self.media_list
pl_normpath = normpath(self.path)
mkdirall(pl_normpath)
try:
with open(syspath(pl_normpath), "wb") as pl_file:
for line in contents:
pl_file.write(line + b"\n")
pl_file.write(b"\n") # Final linefeed to prevent noeol file.
except OSError as exc:
raise FilesystemError(
exc, "create", (pl_normpath,), traceback.format_exc()
)