mirror of
https://github.com/rembo10/headphones.git
synced 2026-01-14 01:08:09 -05:00
98 lines
3.5 KiB
Python
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()
|
|
)
|