From 4c80afad226c639f9d3605201a7907d22c9a2c27 Mon Sep 17 00:00:00 2001 From: Neraste Date: Mon, 27 Jan 2025 02:52:09 +0100 Subject: [PATCH 01/25] [skip ci] First round of removing path in favor of pathlib --- src/dakara_player/background.py | 22 +++--- src/dakara_player/font.py | 32 ++++---- src/dakara_player/media_player/base.py | 22 +++--- src/dakara_player/mrl.py | 12 ++- src/dakara_player/player.py | 5 +- src/dakara_player/text.py | 8 +- src/dakara_player/user_resources.py | 10 +-- tests/integration/base.py | 39 ++++++---- tests/integration/test_media_player_mpv.py | 8 +- tests/integration/test_media_player_vlc.py | 8 +- tests/unit/test_background.py | 5 +- tests/unit/test_font.py | 3 +- tests/unit/test_media_player_mpv.py | 2 +- tests/unit/test_media_player_vlc.py | 20 ++--- tests/unit/test_mrl.py | 11 ++- tests/unit/test_player.py | 2 +- tests/unit/test_text.py | 17 +++-- tests/unit/test_user_resources.py | 88 +++++++++++----------- 18 files changed, 169 insertions(+), 145 deletions(-) diff --git a/src/dakara_player/background.py b/src/dakara_player/background.py index 47eb3bd8..eacf87d0 100644 --- a/src/dakara_player/background.py +++ b/src/dakara_player/background.py @@ -2,9 +2,10 @@ import logging from importlib.resources import path +from pathlib import Path +from shutil import copy from dakara_base.exceptions import DakaraError -from path import Path logger = logging.getLogger(__name__) @@ -27,6 +28,7 @@ class BackgroundLoader: - `something.png` and you use the following configuration: + >>> from pathlib import Path >>> loader = BackgroundLoader( ... destination=Path("/destination"), ... directory=Path("/directory/custom"), @@ -48,18 +50,18 @@ class BackgroundLoader: } Args: - destination (path.Path): Where to copy found background files. + destination (pathlib.Path): Where to copy found background files. package (str): Package checked for backgrounds by default. - directory (path.Path): Custom directory checked for backgrounds. + directory (pathlib.Path): Custom directory checked for backgrounds. filenames (dict): Dictionary of background filenames. The key is the background name, the value the background file name. Attributes: backgrounds (dict): Dictionary of background file paths. The key is the background name, the value the background file path. - destination (path.Path): Where to copy found background files. + destination (pathlib.Path): Where to copy found background files. package (str): Package checked for backgrounds by default. - directory (path.Path): Custom directory checked for backgrounds. + directory (pathlib.Path): Custom directory checked for backgrounds. filenames (dict): Dictionary of background filenames. The key is the background name, the value the background file name. """ @@ -73,7 +75,7 @@ def __init__( ): self.destination = destination self.package = package - self.directory = directory or Path() + self.directory = directory self.filenames = filenames or {} self.backgrounds = {} @@ -91,16 +93,16 @@ def get_background_path(self, background_name, file_name): file_name (str): Name of the background file. Returns: - path.Path: Absolute path to the background file. + pathlib.Path: Absolute path to the background file. """ # trying to load from custom directory - if self.directory: + if self.directory is not None: file_path = self.directory / file_name if file_path.exists(): logger.debug( "Loading custom %s background file '%s'", background_name, file_name ) - return file_path.copy(self.destination) + return copy(file_path, self.destination) # trying to load from package by default try: @@ -111,7 +113,7 @@ def get_background_path(self, background_name, file_name): file_name, ) file_path = Path(file) - return file_path.copy(self.destination) + return copy(file_path, self.destination) except FileNotFoundError as error: raise BackgroundNotFoundError( diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index 87a710b7..11de30b0 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -5,9 +5,9 @@ import platform from abc import ABC, abstractmethod from importlib.resources import contents, path +from pathlib import Path from dakara_base.exceptions import DakaraError -from path import Path logger = logging.getLogger(__name__) @@ -77,7 +77,7 @@ def get_font_name_list(self): font_file_name_list = [ file for file in contents(self.package) - if Path(file).ext.lower() in FONT_EXTENSIONS + if Path(file).suffix.lower() in FONT_EXTENSIONS ] logger.debug("Found %i font(s) to load", len(font_file_name_list)) @@ -87,7 +87,7 @@ def get_font_path_iterator(self): """Give font paths in font package. Yields: - path.Path: Absolute path to the font, from the package. + pathlib.Path: Absolute path to the font, from the package. """ for font_file_name in self.get_font_name_list(): with path(self.package, font_file_name) as font_file_path: @@ -116,9 +116,9 @@ class FontLoaderLinux(FontLoader): Attributes: package (str): Package checked for font files. - font_loaded (dict of path.Path): List of loaded fonts. The key is the - font file name and the value is the path of the installed font in - user directory. + font_loaded (dict of pathlib.Path): List of loaded fonts. The key is + the font file name and the value is the path of the installed font + in user directory. """ GREETINGS = "Font loader for Linux selected" @@ -136,7 +136,7 @@ def get_system_font_path_list(self): """Retrieve the list of system fonts. Returns: - list of path.Path: List of font paths. + list of pathlib.Path: List of font paths. """ return list(self.FONT_DIR_SYSTEM.walkfiles()) @@ -144,7 +144,7 @@ def get_user_font_path_list(self): """Retrieve the list of user fonts. Returns: - list of path.Path: List of font paths. + list of pathlib.Path: List of font paths. """ return list(self.FONT_DIR_USER.expanduser().walkfiles()) @@ -165,11 +165,11 @@ def load_font(self, font_file_path, system_font_path_list, user_font_path_list): """Load the provided font. Args: - font_file_path (path.Path): Absolute path of the font to load. - system_font_path_list (list of path.Path): List of absolute paths - of system fonts. - user_font_path_list (list of path.Path): List of absolute paths of - user fonts. + font_file_path (pathlib.Path): Absolute path of the font to load. + system_font_path_list (list of pathlib.Path): List of absolute + paths of system fonts. + user_font_path_list (list of pathlib.Path): List of absolute paths + of user fonts. """ # get font file name font_file_name = font_file_path.basename() @@ -250,8 +250,8 @@ class FontLoaderWindows(FontLoader): Attributes: package (str): Package checked for font files. - font_loaded (dict of path.Path): List of loaded fonts. The key is the - font file name and the value is the path of font used at + font_loaded (dict of pathlib.Path): List of loaded fonts. The key is + the font file name and the value is the path of font used at installation. """ @@ -279,7 +279,7 @@ def load_font(self, font_file_path): """Load the provided font. Args: - font_file_path (path.Path): Absolute path of the font to load. + font_file_path (pathlib.Path): Absolute path of the font to load. """ success = ctypes.windll.gdi32.AddFontResourceW(font_file_path) if success: diff --git a/src/dakara_player/media_player/base.py b/src/dakara_player/media_player/base.py index e0431ba4..dc18938c 100644 --- a/src/dakara_player/media_player/base.py +++ b/src/dakara_player/media_player/base.py @@ -3,12 +3,12 @@ import logging from abc import ABC, abstractmethod from functools import wraps +from pathlib import Path from threading import Timer from dakara_base.directory import directories from dakara_base.exceptions import DakaraError from dakara_base.safe_workers import Worker -from path import Path from dakara_player.audio import get_audio_files from dakara_player.background import BackgroundLoader @@ -43,7 +43,7 @@ class MediaPlayer(Worker, ABC): errors (queue.Queue): Error queue to communicate the exception to the main thread. config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. Attributes: stop (threading.Event): Stop event that notify to stop the entire @@ -52,14 +52,14 @@ class MediaPlayer(Worker, ABC): main thread. player_name (str): Name of the media player. fullscreen (bool): If `True`, the media player will be fullscreen. - kara_folder_path (path.Path): Path to the karaoke folder. + kara_folder_path (pathlib.Path): Path to the karaoke folder. playlist_entry (dict): Playlist entyr object. callbacks (dict): High level callbacks associated with the media player. warn_long_exit (bool): If `True`, display a warning message if the media player takes too long to stop. durations (dict of int): Duration of the different screens in seconds. - text_paths (dict of path.Path): Path of the different text screens. + text_paths (dict of pathlib.Path): Path of the different text screens. text_generator (dakara_player.text.TextGenerator): Text generator instance. background_loader @@ -88,7 +88,7 @@ def init_worker(self, config, tempdir, warn_long_exit=True): Args: config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. warn_long_exit (bool): If `True`, the class will display a warning message if the media player takes too long to stop. """ @@ -164,7 +164,7 @@ def init_player(self, config, tempdir): Args: config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. """ def load(self): @@ -351,7 +351,7 @@ def set_playlist_entry(self, playlist_entry, autoplay=True): """ file_path = self.kara_folder_path / playlist_entry["song"]["file_path"] - if not file_path.isfile(): + if not file_path.is_file(): logger.error("File not found '%s'", file_path) self.callbacks["error"](playlist_entry["id"], "File not found") self.callbacks["could_not_play"](playlist_entry["id"]) @@ -373,7 +373,7 @@ def set_playlist_entry_player(self, playlist_entry, file_path, autoplay): Args: playlist_entry (dict): Playlist entry object. - file_path (path.Path): Absolute path to the song file. + file_path (pathlib.Path): Absolute path to the song file. autoplay (bool): If `True`, start to play transition screen as soon as possible (i.e. as soon as the transition screen media is ready). The song media is prepared when the transition screen @@ -410,10 +410,10 @@ def get_instrumental_file(filepath): Consider that this instrumental file should be the only one audio file found. Args: - filepath (path.Path): Path to the media file. + filepath (pathlib.Path): Path to the media file. Returns: - path.Path: Path to the instrumental file. None if not found. + pathlib.Path: Path to the instrumental file. None if not found. """ audio_files = get_audio_files(filepath) @@ -487,7 +487,7 @@ def generate_text(self, what, **kwargs): have no fade in effect. Returns: - path.Path: Path of the text screen. + pathlib.Path: Path of the text screen. Raises: ValueError: If the type of screen to generate is unknown. diff --git a/src/dakara_player/mrl.py b/src/dakara_player/mrl.py index 79c90244..109d97c9 100644 --- a/src/dakara_player/mrl.py +++ b/src/dakara_player/mrl.py @@ -1,10 +1,8 @@ """Manage MRL.""" -import pathlib +from pathlib import Path from urllib.parse import unquote, urlparse -from path import Path - def mrl_to_path(file_mrl): """Convert a MRL to a filesystem path. @@ -16,7 +14,7 @@ def mrl_to_path(file_mrl): file_mrl (str): Path to the resource within MRL format. Returns: - path.Path: Path to the resource. + pathlib.Path: Path to the resource. """ path_string = unquote(urlparse(file_mrl).path) @@ -24,16 +22,16 @@ def mrl_to_path(file_mrl): if path_string[0] == "/" and path_string[2] == ":": path_string = path_string[1:] - return Path(path_string).normpath() + return Path(path_string).resolve() def path_to_mrl(file_path): """Convert a filesystem path to MRL. Args: - file_path (path.Path or str): Path to the resource. + file_path (pathlib.Path or str): Path to the resource. Returns: str: Path to the resource within MRL format. """ - return pathlib.Path(file_path).as_uri() + return file_path.as_uri() diff --git a/src/dakara_player/player.py b/src/dakara_player/player.py index b97b6827..2568dd7f 100644 --- a/src/dakara_player/player.py +++ b/src/dakara_player/player.py @@ -2,10 +2,11 @@ import logging from contextlib import ExitStack +from pathlib import Path +from tempfile import TemporaryDirectory from dakara_base.exceptions import DakaraError from dakara_base.safe_workers import Runner, WorkerSafeThread -from path import TempDir from dakara_player.font import get_font_loader_class from dakara_player.manager import DakaraManager @@ -123,7 +124,7 @@ def run(self): # be executed. with ExitStack() as stack: # temporary directory - tempdir = stack.enter_context(TempDir(suffix=".dakara")) + tempdir = Path(stack.enter_context(TemporaryDirectory(suffix=".dakara"))) # font loader font_loader = stack.enter_context( diff --git a/src/dakara_player/text.py b/src/dakara_player/text.py index fca81892..d07bd13a 100644 --- a/src/dakara_player/text.py +++ b/src/dakara_player/text.py @@ -3,10 +3,10 @@ import json import logging from importlib.resources import path +from pathlib import Path from dakara_base.exceptions import DakaraError from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PackageLoader -from path import Path ICON_MAP_FILE = "line-awesome.json" @@ -29,7 +29,7 @@ class TextGenerator: Example of use: - >>> from path import Path + >>> from pathlib import Path >>> generator = TextGenerator( ... package="package", ... directory=Path("directory"), @@ -51,13 +51,13 @@ class TextGenerator: Args: package (str): Package checked for text templates by default. - directory (path.Path): Custom directory checked for text templates. + directory (pathlib.Path): Custom directory checked for text templates. filenames (dict): Dictionary of text templates filenames. The key is the template name, the value the template file name. Attributes: package (str): Package checked for text templates by default. - directory (path.Path): Custom directory checked for text templates. + directory (pathlib.Path): Custom directory checked for text templates. filenames (dict): Dictionary of text templates filenames. The key is the template name, the value the template file name. environment (jinja2.Environment): Environment for Jinja2. diff --git a/src/dakara_player/user_resources.py b/src/dakara_player/user_resources.py index c7ee9bec..e2811f86 100644 --- a/src/dakara_player/user_resources.py +++ b/src/dakara_player/user_resources.py @@ -3,9 +3,9 @@ import logging from distutils.util import strtobool from importlib.resources import contents, path +from shutil import copy from dakara_base.directory import directories -from path import Path logger = logging.getLogger(__name__) @@ -15,7 +15,7 @@ def copy_resource(resource, destination, force): Args: resource (str): Resource to copy. - destination (path.Path): Directory where to copy the resource. + destination (pathlib.Path): Directory where to copy the resource. force (bool): If the destination exists and this flag is set to `True`, overwrite the destination. """ @@ -34,7 +34,7 @@ def copy_resource(resource, destination, force): if not result: return - destination.makedirs_p() + destination.mkdir(parents=True, exist_ok=True) for file_name in contents(resource): # ignore Python files @@ -42,7 +42,7 @@ def copy_resource(resource, destination, force): continue with path(resource, file_name) as file: - Path(file).copy(destination) + copy(file, destination) def create_resource_files(force=False): @@ -53,7 +53,7 @@ def create_resource_files(force=False): directories and this flag is set, overwrite the directories. """ user_directory = directories.user_data_dir - user_directory.makedirs_p() + user_directory.mkdir(parents=True, exist_ok=True) for directory in ["backgrounds", "templates"]: copy_resource( diff --git a/tests/integration/base.py b/tests/integration/base.py index 383d91a4..25a7e723 100644 --- a/tests/integration/base.py +++ b/tests/integration/base.py @@ -1,3 +1,6 @@ +from pathlib import Path +from shutil import copy, rmtree +from tempfile import TemporaryDirectory from time import sleep from unittest import TestCase @@ -7,8 +10,6 @@ except ImportError: from importlib_resources import path -from path import Path, TempDir - class TestCasePoller(TestCase): """Test class that can poll the state of tested player.""" @@ -75,53 +76,65 @@ class TestCaseKara(TestCase): def setUp(self): # create kara folder - self.kara_folder = TempDir() + self.kara_folder = Path(TemporaryDirectory().name) # create subtitle with path("tests.resources", "song1.ass") as file: - self.subtitle1_path = Path(file).copy(self.kara_folder) + self.subtitle1_path = copy(file, self.kara_folder) with path("tests.resources", "song2.ass") as file: - self.subtitle2_path = Path(file).copy(self.kara_folder) + self.subtitle2_path = copy(file, self.kara_folder) # create song with path("tests.resources", "song1.mkv") as file: - self.song1_path = Path(file).copy(self.kara_folder) + self.song1_path = copy(file, self.kara_folder) with path("tests.resources", "song2.mkv") as file: - self.song2_path = Path(file).copy(self.kara_folder) + self.song2_path = copy(file, self.kara_folder) with path("tests.resources", "song3.avi") as file: - self.song3_path = Path(file).copy(self.kara_folder) + self.song3_path = copy(file, self.kara_folder) # create audio with path("tests.resources", "song2.mp3") as file: - self.audio2_path = Path(file).copy(self.kara_folder) + self.audio2_path = copy(file, self.kara_folder) # create playlist entry self.playlist_entry1 = { "id": 42, - "song": {"title": "Song 1", "file_path": self.song1_path, "duration": 60}, + "song": { + "title": "Song 1", + "file_path": str(self.song1_path), + "duration": 60, + }, "owner": "me", "use_instrumental": False, } self.playlist_entry2 = { "id": 43, - "song": {"title": "Song 2", "file_path": self.song2_path, "duration": 60}, + "song": { + "title": "Song 2", + "file_path": str(self.song2_path), + "duration": 60, + }, "owner": "me", "use_instrumental": False, } self.playlist_entry3 = { "id": 44, - "song": {"title": "Song 3", "file_path": self.song3_path, "duration": 60}, + "song": { + "title": "Song 3", + "file_path": str(self.song3_path), + "duration": 60, + }, "owner": "me", "use_instrumental": False, } def tearDown(self): - self.kara_folder.rmtree(ignore_errors=True) + rmtree(self.kara_folder, ignore_errors=True) class TestCasePollerKara(TestCasePoller, TestCaseKara): diff --git a/tests/integration/test_media_player_mpv.py b/tests/integration/test_media_player_mpv.py index 4d951233..06abe52a 100644 --- a/tests/integration/test_media_player_mpv.py +++ b/tests/integration/test_media_player_mpv.py @@ -1,5 +1,6 @@ from contextlib import ExitStack, contextmanager from queue import Queue +from tempfile import TemporaryDirectory from threading import Event from time import sleep from unittest import skipUnless @@ -7,7 +8,7 @@ from dakara_base.config import Config from func_timeout import func_set_timeout -from path import TempDir +from path import Path from dakara_player.media_player.base import ( IDLE_BG_NAME, @@ -54,7 +55,7 @@ def get_instance(self, config=None, check_error=True): Yields: tuple: Containing the following elements: MediaPlayerMpv: Instance; - path.Path: Path of the temporary directory; + pathlib.Path: Path of the temporary directory; unittest.case._LoggingWatcher: Captured output. """ config_full = { @@ -66,7 +67,8 @@ def get_instance(self, config=None, check_error=True): if config: config_full.update(config) - with TempDir() as temp: + with TemporaryDirectory() as temp_str: + temp = Path(temp_str) try: with ExitStack() as stack: mpv_player = stack.enter_context( diff --git a/tests/integration/test_media_player_vlc.py b/tests/integration/test_media_player_vlc.py index 1997af4c..1f607401 100644 --- a/tests/integration/test_media_player_vlc.py +++ b/tests/integration/test_media_player_vlc.py @@ -1,9 +1,12 @@ from contextlib import ExitStack, contextmanager from queue import Queue +from tempfile import TemporaryDirectory from threading import Event from unittest import skipIf, skipUnless from unittest.mock import MagicMock +from path import Path + try: import vlc @@ -12,7 +15,6 @@ from dakara_base.config import Config from func_timeout import func_set_timeout -from path import TempDir from dakara_player.media_player.base import IDLE_BG_NAME, TRANSITION_BG_NAME from dakara_player.media_player.vlc import METADATA_KEYS_COUNT, MediaPlayerVlc @@ -67,7 +69,7 @@ def get_instance(self, config=None, check_error=True): Yields: tuple: Containing the following elements: MediaPlayerVlc: Instance; - path.Path: Path of the temporary directory; + pathlib.Path: Path of the temporary directory; unittest.case._LoggingWatcher: Captured output. """ @@ -85,7 +87,7 @@ def get_instance(self, config=None, check_error=True): config_full.update(config) with ExitStack() as stack: - temp = stack.enter_context(TempDir()) + temp = Path(stack.enter_context(TemporaryDirectory())) vlc_player = stack.enter_context( MediaPlayerVlc( Event(), diff --git a/tests/unit/test_background.py b/tests/unit/test_background.py index aa1a3bc3..e29332ad 100644 --- a/tests/unit/test_background.py +++ b/tests/unit/test_background.py @@ -1,13 +1,12 @@ +from pathlib import Path from unittest import TestCase from unittest.mock import patch -from path import Path - from dakara_player.background import BackgroundLoader, BackgroundNotFoundError @patch("dakara_player.background.path", autospec=True) -@patch.object(Path, "copy", autospec=True) +@patch("dakara_player.background.copy", autospec=True) @patch.object(Path, "exists", autospec=True) class BackgroundLoaderTestCase(TestCase): """Test the loader for backgrounds.""" diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index 16d5f3b3..b3f77b34 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -1,9 +1,8 @@ import platform +from pathlib import Path from unittest import TestCase, skipUnless from unittest.mock import call, patch -from path import Path - from dakara_player.font import ( FontLoaderLinux, FontLoaderNotAvailableError, diff --git a/tests/unit/test_media_player_mpv.py b/tests/unit/test_media_player_mpv.py index cd755eda..d7a9367a 100644 --- a/tests/unit/test_media_player_mpv.py +++ b/tests/unit/test_media_player_mpv.py @@ -1,4 +1,5 @@ from contextlib import ExitStack +from pathlib import Path from queue import Queue from tempfile import gettempdir from threading import Event @@ -6,7 +7,6 @@ from unittest.mock import MagicMock, patch from packaging.version import Version -from path import Path from dakara_player.media_player.base import ( InvalidStateError, diff --git a/tests/unit/test_media_player_vlc.py b/tests/unit/test_media_player_vlc.py index 58248d4a..e0ba5787 100644 --- a/tests/unit/test_media_player_vlc.py +++ b/tests/unit/test_media_player_vlc.py @@ -1,5 +1,6 @@ import re from contextlib import ExitStack, contextmanager +from pathlib import Path from queue import Queue from tempfile import gettempdir from threading import Event @@ -15,7 +16,6 @@ from dakara_base.directory import AppDirsPath from packaging.version import parse -from path import Path from dakara_player.media_player.base import ( InvalidStateError, @@ -62,7 +62,7 @@ def get_instance( Args: config (dict): Configuration passed to the constructor. - tempdir (path.Path): Path to temporary directory. + tempdir (pathlib.Path): Path to temporary directory. Yields: tuple: Contains the following elements: @@ -388,12 +388,12 @@ def test_load( logger.output, ["INFO:dakara_player.media_player.vlc:VLC 3.0.0 NoName"] ) - @patch.object(Path, "isfile") - def test_set_playlist_entry_error_file(self, mocked_isfile): + @patch.object(Path, "is_file") + def test_set_playlist_entry_error_file(self, mocked_is_file): """Test to set a playlist entry that does not exist.""" with self.get_instance() as (vlc_player, _, _): # mock the system call - mocked_isfile.return_value = False + mocked_is_file.return_value = False # mock the callbacks vlc_player.set_callback("could_not_play", MagicMock()) @@ -407,7 +407,7 @@ def test_set_playlist_entry_error_file(self, mocked_isfile): vlc_player.set_playlist_entry(self.playlist_entry) # call assertions - mocked_isfile.assert_called_once_with() + mocked_is_file.assert_called_once_with() # post assertions self.assertIsNone(vlc_player.playlist_entry) @@ -431,10 +431,10 @@ def test_set_playlist_entry_error_file(self, mocked_isfile): @patch.object(MediaPlayerVlc, "manage_instrumental") @patch.object(MediaPlayerVlc, "play") @patch.object(MediaPlayerVlc, "generate_text") - @patch.object(Path, "isfile") + @patch.object(Path, "is_file") def test_set_playlist_entry( self, - mocked_isfile, + mocked_is_file, mocked_generate_text, mocked_play, mocked_manage_instrumental, @@ -444,7 +444,7 @@ def test_set_playlist_entry( """Test to set a playlist entry.""" with self.get_instance() as (vlc_player, (_, mocked_background_loader, _), _): # setup mocks - mocked_isfile.return_value = True + mocked_is_file.return_value = True mocked_background_loader.backgrounds = { "transition": Path(gettempdir()) / "transition.png" } @@ -468,7 +468,7 @@ def test_set_playlist_entry( vlc_player.callbacks["error"].assert_not_called() # assert mocks - mocked_isfile.assert_called_with() + mocked_is_file.assert_called_with() mocked_generate_text.assert_called_with("transition") mocked_play.assert_called_with("transition") mocked_manage_instrumental.assert_not_called() diff --git a/tests/unit/test_mrl.py b/tests/unit/test_mrl.py index b5c5c432..af506b3f 100644 --- a/tests/unit/test_mrl.py +++ b/tests/unit/test_mrl.py @@ -1,8 +1,7 @@ import sys +from pathlib import Path from unittest import TestCase, skipIf -from path import Path - from dakara_player.mrl import mrl_to_path, path_to_mrl @@ -16,7 +15,7 @@ def test_mrl_to_path_posix(self): """Test to convert MRL to path for POSIX.""" path = mrl_to_path("file:///home/username/directory/file%20name.ext") self.assertEqual( - path, Path("/home").normpath() / "username" / "directory" / "file name.ext" + path, Path("/home").resolve() / "username" / "directory" / "file name.ext" ) @skipIf(not is_windows, "Tested on Windows") @@ -25,14 +24,14 @@ def test_mrl_to_path_windows(self): path = mrl_to_path("file:///C:/Users/username/directory/file%20name.ext") self.assertEqual( path, - Path("C:/Users").normpath() / "username" / "directory" / "file name.ext", + Path("C:/Users").resolve() / "username" / "directory" / "file name.ext", ) @skipIf(is_windows, "Tested on POSIX") def test_path_to_mrl_posix(self): """Test to convert path to MRL for POSIX.""" mrl = path_to_mrl( - Path("/home").normpath() / "username" / "directory" / "file name.ext" + Path("/home").resolve() / "username" / "directory" / "file name.ext" ) self.assertEqual(mrl, "file:///home/username/directory/file%20name.ext") @@ -40,6 +39,6 @@ def test_path_to_mrl_posix(self): def test_path_to_mrl_windows(self): """Test to convert path to MRL for Windows.""" mrl = path_to_mrl( - Path("C:/Users").normpath() / "username" / "directory" / "file name.ext" + Path("C:/Users").resolve() / "username" / "directory" / "file name.ext" ) self.assertEqual(mrl, "file:///C:/Users/username/directory/file%20name.ext") diff --git a/tests/unit/test_player.py b/tests/unit/test_player.py index 6c91d201..b0ed5c18 100644 --- a/tests/unit/test_player.py +++ b/tests/unit/test_player.py @@ -94,7 +94,7 @@ def test_get_media_player_class_unsupported(self): ): worker.get_media_player_class() - @patch("dakara_player.player.TempDir", autospec=True) + @patch("dakara_player.player.TemporaryDirectory", autospec=True) @patch("dakara_player.player.FontLoader", autospec=True) @patch("dakara_player.player.MediaPlayerVlc", autospec=True) @patch("dakara_player.player.HTTPClientDakara", autospec=True) diff --git a/tests/unit/test_text.py b/tests/unit/test_text.py index 5c198a25..c218af0c 100644 --- a/tests/unit/test_text.py +++ b/tests/unit/test_text.py @@ -1,9 +1,10 @@ +from pathlib import Path from pathlib import Path as Path_pathlib +from shutil import copy +from tempfile import TemporaryDirectory from unittest import TestCase from unittest.mock import MagicMock, patch -from path import Path, TempDir - try: from importlib.resources import path @@ -260,7 +261,9 @@ def test_load_templates_default(self): Integration test. """ - with TempDir() as temp: + with TemporaryDirectory() as temp_str: + temp = Path(temp_str) + # create object text_generator = TextGenerator( package="dakara_player.resources.templates", @@ -280,13 +283,15 @@ def test_load_templates_default(self): def test_load_templates_custom(self): """Test to load custom templates using an existing directory.""" - with TempDir() as temp: + with TemporaryDirectory() as temp_str: + temp = Path(temp_str) + # prepare directory with path("dakara_player.resources.templates", "idle.ass") as file: - Path(file).copy(temp) + copy(file, temp) with path("dakara_player.resources.templates", "transition.ass") as file: - Path(file).copy(temp) + copy(file, temp) # create object text_generator = TextGenerator( diff --git a/tests/unit/test_user_resources.py b/tests/unit/test_user_resources.py index 11c814f0..6fee27bc 100644 --- a/tests/unit/test_user_resources.py +++ b/tests/unit/test_user_resources.py @@ -1,8 +1,8 @@ +from pathlib import Path from unittest import TestCase from unittest.mock import PropertyMock, call, patch from dakara_base.directory import AppDirsPath -from path import Path from dakara_player import user_resources @@ -10,15 +10,15 @@ class CopyResourceTestCase(TestCase): """Test the copy_resource function.""" - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.user_resources.copy", autospec=True) @patch("dakara_player.user_resources.path", autospec=True) @patch("dakara_player.user_resources.contents", autospec=True) - @patch.object(Path, "makedirs_p", autospec=True) + @patch.object(Path, "mkdir", autospec=True) @patch.object(Path, "exists", autospec=True) def test_copy( self, mocked_exists, - mocked_makedirs_p, + mocked_mkdir, mocked_contents, mocked_path, mocked_copy, @@ -27,32 +27,34 @@ def test_copy( mocked_exists.return_value = False mocked_contents.return_value = ["file1.ext", "file2.ext", "__init__.py"] mocked_path.return_value.__enter__.side_effect = [ - "path/to/file1.ext", - "path/to/file2.ext", + Path("path/to/file1.ext"), + Path("path/to/file2.ext"), ] user_resources.copy_resource("package.resources", Path("destination"), False) mocked_exists.assert_called_with(Path("destination")) - mocked_makedirs_p.assert_called_with(Path("destination")) + mocked_mkdir.assert_called_with( + Path("destination"), parents=True, exist_ok=True + ) mocked_contents.assert_called_with("package.resources") mocked_copy.assert_has_calls( [ - call(Path("path/to/file1.ext"), "destination"), - call(Path("path/to/file2.ext"), "destination"), + call(Path("path/to/file1.ext"), Path("destination")), + call(Path("path/to/file2.ext"), Path("destination")), ] ) @patch("dakara_player.user_resources.input") - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.user_resources.copy", autospec=True) @patch("dakara_player.user_resources.path", autospec=True) @patch("dakara_player.user_resources.contents", autospec=True) - @patch.object(Path, "makedirs_p", autospec=True) + @patch.object(Path, "mkdir", autospec=True) @patch.object(Path, "exists", autospec=True) def test_copy_existing_abort( self, mocked_exists, - mocked_makedirs_p, + mocked_mkdir, mocked_contents, mocked_path, mocked_copy, @@ -62,28 +64,28 @@ def test_copy_existing_abort( mocked_exists.return_value = True mocked_contents.return_value = ["file1.ext", "file2.ext", "__init__.py"] mocked_path.return_value.__enter__.side_effect = [ - "path/to/file1.ext", - "path/to/file2.ext", + Path("path/to/file1.ext"), + Path("path/to/file2.ext"), ] mocked_input.return_value = "no" user_resources.copy_resource("package.resources", Path("destination"), False) mocked_exists.assert_called_with(Path("destination")) - mocked_makedirs_p.assert_not_called() + mocked_mkdir.assert_not_called() mocked_contents.assert_not_called() mocked_copy.assert_not_called() @patch("dakara_player.user_resources.input") - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.user_resources.copy", autospec=True) @patch("dakara_player.user_resources.path", autospec=True) @patch("dakara_player.user_resources.contents", autospec=True) - @patch.object(Path, "makedirs_p", autospec=True) + @patch.object(Path, "mkdir", autospec=True) @patch.object(Path, "exists", autospec=True) def test_copy_existing_abort_invalid( self, mocked_exists, - mocked_makedirs_p, + mocked_mkdir, mocked_contents, mocked_path, mocked_copy, @@ -93,28 +95,28 @@ def test_copy_existing_abort_invalid( mocked_exists.return_value = True mocked_contents.return_value = ["file1.ext", "file2.ext", "__init__.py"] mocked_path.return_value.__enter__.side_effect = [ - "path/to/file1.ext", - "path/to/file2.ext", + Path("path/to/file1.ext"), + Path("path/to/file2.ext"), ] mocked_input.return_value = "aaa" user_resources.copy_resource("package.resources", Path("destination"), False) mocked_exists.assert_called_with(Path("destination")) - mocked_makedirs_p.assert_not_called() + mocked_mkdir.assert_not_called() mocked_contents.assert_not_called() mocked_copy.assert_not_called() @patch("dakara_player.user_resources.input") - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.user_resources.copy", autospec=True) @patch("dakara_player.user_resources.path", autospec=True) @patch("dakara_player.user_resources.contents", autospec=True) - @patch.object(Path, "makedirs_p", autospec=True) + @patch.object(Path, "mkdir", autospec=True) @patch.object(Path, "exists", autospec=True) def test_copy_existing_overwrite( self, mocked_exists, - mocked_makedirs_p, + mocked_mkdir, mocked_contents, mocked_path, mocked_copy, @@ -124,33 +126,35 @@ def test_copy_existing_overwrite( mocked_exists.return_value = True mocked_contents.return_value = ["file1.ext", "file2.ext", "__init__.py"] mocked_path.return_value.__enter__.side_effect = [ - "path/to/file1.ext", - "path/to/file2.ext", + Path("path/to/file1.ext"), + Path("path/to/file2.ext"), ] mocked_input.return_value = "yes" user_resources.copy_resource("package.resources", Path("destination"), False) mocked_exists.assert_called_with(Path("destination")) - mocked_makedirs_p.assert_called_with(Path("destination")) + mocked_mkdir.assert_called_with( + Path("destination"), parents=True, exist_ok=True + ) mocked_contents.assert_called_with("package.resources") mocked_copy.assert_has_calls( [ - call(Path("path/to/file1.ext"), "destination"), - call(Path("path/to/file2.ext"), "destination"), + call(Path("path/to/file1.ext"), Path("destination")), + call(Path("path/to/file2.ext"), Path("destination")), ] ) @patch("dakara_player.user_resources.input") - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.user_resources.copy", autospec=True) @patch("dakara_player.user_resources.path", autospec=True) @patch("dakara_player.user_resources.contents", autospec=True) - @patch.object(Path, "makedirs_p", autospec=True) + @patch.object(Path, "mkdir", autospec=True) @patch.object(Path, "exists", autospec=True) def test_copy_existing_force( self, mocked_exists, - mocked_makedirs_p, + mocked_mkdir, mocked_contents, mocked_path, mocked_copy, @@ -160,20 +164,22 @@ def test_copy_existing_force( mocked_exists.return_value = True mocked_contents.return_value = ["file1.ext", "file2.ext", "__init__.py"] mocked_path.return_value.__enter__.side_effect = [ - "path/to/file1.ext", - "path/to/file2.ext", + Path("path/to/file1.ext"), + Path("path/to/file2.ext"), ] user_resources.copy_resource("package.resources", Path("destination"), True) mocked_input.assert_not_called() mocked_exists.assert_not_called() - mocked_makedirs_p.assert_called_with(Path("destination")) + mocked_mkdir.assert_called_with( + Path("destination"), parents=True, exist_ok=True + ) mocked_contents.assert_called_with("package.resources") mocked_copy.assert_has_calls( [ - call(Path("path/to/file1.ext"), "destination"), - call(Path("path/to/file2.ext"), "destination"), + call(Path("path/to/file1.ext"), Path("destination")), + call(Path("path/to/file2.ext"), Path("destination")), ] ) @@ -183,16 +189,14 @@ def test_copy_existing_force( class CreateResourceFilesTestCase(TestCase): """Test the create_resource_files function.""" - @patch.object(Path, "makedirs_p", autospec=True) - def test_create( - self, mocked_makedirs_p, mocked_copy_resource, mocked_user_data_dir - ): + @patch.object(Path, "mkdir", autospec=True) + def test_create(self, mocked_mkdir, mocked_copy_resource, mocked_user_data_dir): """Test to create resource files.""" mocked_user_data_dir.return_value = Path("directory") with self.assertLogs("dakara_player.user_resources", "DEBUG") as logger: user_resources.create_resource_files() - mocked_makedirs_p.assert_called_with(Path("directory")) + mocked_mkdir.assert_called_with(Path("directory"), parents=True, exist_ok=True) mocked_copy_resource.assert_has_calls( [ call( From f323ffb4d4b7ad9c903acbd28495f274c5c30c59 Mon Sep 17 00:00:00 2001 From: Neraste Date: Mon, 27 Jan 2025 23:09:29 +0100 Subject: [PATCH 02/25] Update dakarabase to 2.0.0 --- pyproject.toml | 2 +- src/dakara_player/__main__.py | 6 +++--- src/dakara_player/user_resources.py | 2 +- tests/integration/base.py | 19 ++++++++++--------- tests/unit/test_media_player_vlc.py | 8 ++++---- tests/unit/test_user_resources.py | 8 ++++---- 6 files changed, 23 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d6a23694..15bbf89c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ ] dependencies = [ "Jinja2>=3.1.1,<3.2.0", - "dakarabase>=1.4.2,<1.5.0", + "dakarabase>=2.0.0,<2.1.0", "filetype>=1.2.0,<1.3.0", "packaging>=21.3,<22.0", "path>=16.4.0,<16.5.0", diff --git a/src/dakara_player/__main__.py b/src/dakara_player/__main__.py index ef82307d..b4fd4da7 100755 --- a/src/dakara_player/__main__.py +++ b/src/dakara_player/__main__.py @@ -34,13 +34,13 @@ handle_config_incomplete = generate_exception_handler( DakaraError, "Config may be incomplete, please check '{}'".format( - directories.user_config_dir / CONFIG_FILE + directories.user_config_path / CONFIG_FILE ), ) handle_parameter_error = generate_exception_handler( ParameterError, "Config may be incomplete, please check '{}'".format( - directories.user_config_dir / CONFIG_FILE + directories.user_config_path / CONFIG_FILE ), ) @@ -123,7 +123,7 @@ def play(args): with handle_config_not_found(): create_logger() config = Config(CONFIG_PREFIX) - config.load_file(directories.user_config_dir / CONFIG_FILE) + config.load_file(directories.user_config_path / CONFIG_FILE) config.check_mandatory_keys(["player", "server"]) config.set_debug(args.debug) set_loglevel(config) diff --git a/src/dakara_player/user_resources.py b/src/dakara_player/user_resources.py index e2811f86..3ab2f7df 100644 --- a/src/dakara_player/user_resources.py +++ b/src/dakara_player/user_resources.py @@ -52,7 +52,7 @@ def create_resource_files(force=False): force (bool): If the user directory already contains the resource directories and this flag is set, overwrite the directories. """ - user_directory = directories.user_data_dir + user_directory = directories.user_data_path user_directory.mkdir(parents=True, exist_ok=True) for directory in ["backgrounds", "templates"]: diff --git a/tests/integration/base.py b/tests/integration/base.py index 25a7e723..de8a9ba8 100644 --- a/tests/integration/base.py +++ b/tests/integration/base.py @@ -1,5 +1,5 @@ from pathlib import Path -from shutil import copy, rmtree +from shutil import copy from tempfile import TemporaryDirectory from time import sleep from unittest import TestCase @@ -76,28 +76,29 @@ class TestCaseKara(TestCase): def setUp(self): # create kara folder - self.kara_folder = Path(TemporaryDirectory().name) + self.kara_folder = TemporaryDirectory() + self.kara_folder_path = Path(self.kara_folder.name) # create subtitle with path("tests.resources", "song1.ass") as file: - self.subtitle1_path = copy(file, self.kara_folder) + self.subtitle1_path = Path(copy(file, self.kara_folder_path)) with path("tests.resources", "song2.ass") as file: - self.subtitle2_path = copy(file, self.kara_folder) + self.subtitle2_path = Path(copy(file, self.kara_folder_path)) # create song with path("tests.resources", "song1.mkv") as file: - self.song1_path = copy(file, self.kara_folder) + self.song1_path = Path(copy(file, self.kara_folder_path)) with path("tests.resources", "song2.mkv") as file: - self.song2_path = copy(file, self.kara_folder) + self.song2_path = Path(copy(file, self.kara_folder_path)) with path("tests.resources", "song3.avi") as file: - self.song3_path = copy(file, self.kara_folder) + self.song3_path = Path(copy(file, self.kara_folder_path)) # create audio with path("tests.resources", "song2.mp3") as file: - self.audio2_path = copy(file, self.kara_folder) + self.audio2_path = Path(copy(file, self.kara_folder_path)) # create playlist entry self.playlist_entry1 = { @@ -134,7 +135,7 @@ def setUp(self): } def tearDown(self): - rmtree(self.kara_folder, ignore_errors=True) + self.kara_folder.cleanup() class TestCasePollerKara(TestCasePoller, TestCaseKara): diff --git a/tests/unit/test_media_player_vlc.py b/tests/unit/test_media_player_vlc.py index e0ba5787..c15bd871 100644 --- a/tests/unit/test_media_player_vlc.py +++ b/tests/unit/test_media_player_vlc.py @@ -14,7 +14,7 @@ except (ImportError, OSError): vlc = None -from dakara_base.directory import AppDirsPath +from dakara_base.directory import PlatformDirs from packaging.version import parse from dakara_player.media_player.base import ( @@ -1100,10 +1100,10 @@ def test_handle_paused(self, mocked_get_timing): # assert the call vlc_player.callbacks["paused"].assert_called_with(42, 25) - @patch.object(AppDirsPath, "user_data_dir", new_callable=PropertyMock) - def test_custom_backgrounds(self, mocked_user_data_dir): + @patch.object(PlatformDirs, "user_data_path", new_callable=PropertyMock) + def test_custom_backgrounds(self, mocked_user_data_path): """Test to instanciate with custom backgrounds.""" - mocked_user_data_dir.return_value = Path("directory") + mocked_user_data_path.return_value = Path("directory") # create object tempdir = Path("temp") diff --git a/tests/unit/test_user_resources.py b/tests/unit/test_user_resources.py index 6fee27bc..4161640b 100644 --- a/tests/unit/test_user_resources.py +++ b/tests/unit/test_user_resources.py @@ -2,7 +2,7 @@ from unittest import TestCase from unittest.mock import PropertyMock, call, patch -from dakara_base.directory import AppDirsPath +from dakara_base.directory import PlatformDirs from dakara_player import user_resources @@ -184,15 +184,15 @@ def test_copy_existing_force( ) -@patch.object(AppDirsPath, "user_data_dir", new_callable=PropertyMock) +@patch.object(PlatformDirs, "user_data_path", new_callable=PropertyMock) @patch("dakara_player.user_resources.copy_resource", autospec=True) class CreateResourceFilesTestCase(TestCase): """Test the create_resource_files function.""" @patch.object(Path, "mkdir", autospec=True) - def test_create(self, mocked_mkdir, mocked_copy_resource, mocked_user_data_dir): + def test_create(self, mocked_mkdir, mocked_copy_resource, mocked_user_data_path): """Test to create resource files.""" - mocked_user_data_dir.return_value = Path("directory") + mocked_user_data_path.return_value = Path("directory") with self.assertLogs("dakara_player.user_resources", "DEBUG") as logger: user_resources.create_resource_files() From df0cb8e75c3d1ba8037ae2bf593ee559a70ae39b Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 00:51:55 +0100 Subject: [PATCH 03/25] Second round of removing path in favor of pathlib --- src/dakara_player/audio.py | 8 +-- src/dakara_player/media_player/base.py | 4 +- src/dakara_player/media_player/mpv.py | 57 ++++++++++++---------- src/dakara_player/media_player/vlc.py | 18 +++---- tests/integration/test_media_player_mpv.py | 2 +- tests/integration/test_media_player_vlc.py | 7 ++- tests/unit/test_media_player_mpv.py | 12 +++-- 7 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/dakara_player/audio.py b/src/dakara_player/audio.py index e86b1425..19e2c592 100644 --- a/src/dakara_player/audio.py +++ b/src/dakara_player/audio.py @@ -7,13 +7,13 @@ def get_audio_files(filepath): """Get audio files with the same name as provided file. Args: - filepath (path.Path): Path of the initial file. + filepath (pathlib.Path): Path of the initial file. Returns: - list of path.Path: List of paths of audio files. + list of pathlib.Path: List of paths of audio files. """ # list files with similar stem - items = filepath.dirname().glob("{}.*".format(filepath.stem)) + items = filepath.parent.glob("{}.*".format(filepath.name)) return [item for item in items if item != filepath and is_audio_file(item)] @@ -21,7 +21,7 @@ def is_audio_file(file_path): """Detect if a file is audio file based on standard magic numbers. Args: - file_path (path.Path): Path of the file to investigate. + file_path (pathlib.Path): Path of the file to investigate. Returns: bool: `True` if the file is an audio file, `False` otherwise. diff --git a/src/dakara_player/media_player/base.py b/src/dakara_player/media_player/base.py index dc18938c..1469481e 100644 --- a/src/dakara_player/media_player/base.py +++ b/src/dakara_player/media_player/base.py @@ -125,7 +125,7 @@ def init_worker(self, config, tempdir, warn_long_exit=True): config_texts = config.get("templates") or {} self.text_generator = TextGenerator( package="dakara_player.resources.templates", - directory=directories.user_data_dir / "player" / "templates", + directory=directories.user_data_path / "player" / "templates", filenames={ "transition": config_texts.get( "transition_template_name", TRANSITION_TEXT_NAME @@ -139,7 +139,7 @@ def init_worker(self, config, tempdir, warn_long_exit=True): self.background_loader = BackgroundLoader( destination=tempdir, package="dakara_player.resources.backgrounds", - directory=directories.user_data_dir / "player" / "backgrounds", + directory=directories.user_data_path / "player" / "backgrounds", filenames={ "transition": config_backgrounds.get( "transition_background_name", TRANSITION_BG_NAME diff --git a/src/dakara_player/media_player/mpv.py b/src/dakara_player/media_player/mpv.py index 50ba7564..735af0df 100644 --- a/src/dakara_player/media_player/mpv.py +++ b/src/dakara_player/media_player/mpv.py @@ -3,6 +3,7 @@ import logging import re from abc import ABC +from pathlib import Path from dakara_base.exceptions import DakaraError from dakara_base.safe_workers import safe @@ -182,7 +183,7 @@ class MediaPlayerMpvOld(MediaPlayerMpv): errors (queue.Queue): Error queue to communicate the exception to the main thread. config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. Attributes: stop (threading.Event): Stop event that notify to stop the entire @@ -191,14 +192,14 @@ class MediaPlayerMpvOld(MediaPlayerMpv): main thread. player_name (str): Name of mpv. fullscreen (bool): If `True`, mpv will be fullscreen. - kara_folder_path (path.Path): Path to the karaoke folder. + kara_folder_path (pathlib.Path): Path to the karaoke folder. playlist_entry (dict): Playlist entyr object. callbacks (dict): High level callbacks associated with the media player. warn_long_exit (bool): If `True`, display a warning message if the media player takes too long to stop. durations (dict of int): Duration of the different screens in seconds. - text_paths (dict of path.Path): Path of the different text screens. + text_paths (dict of pathlib.Path): Path of the different text screens. text_generator (dakara_player.text_generator.TextGenerator): Text generator instance. background_loader @@ -217,7 +218,7 @@ def init_player(self, config, tempdir): Args: config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. """ # set mpv player options and logging loglevel = config.get("loglevel", "info") @@ -344,13 +345,13 @@ def is_playing_this(self, what): assert len(playlist) == 1, "Too many entries in mpv internal playlist" media = playlist[0] - media_path = media.get("filename") + media_path = Path(media.get("filename") or "") if what == "idle": return media_path == self.background_loader.backgrounds["idle"] - return ( - media_path == self.playlist_entry_data[what].path and media_path is not None + return (media_path == self.playlist_entry_data[what].path) and ( + media_path != Path("") ) def play(self, what): @@ -379,20 +380,20 @@ def play(self, what): self.generate_text("idle") self.player.play(self.background_loader.backgrounds["idle"]) - self.player.sub_files = self.text_paths["idle"] + self.player.sub_files = str(self.text_paths["idle"]) return if what == "transition": - self.player.play(self.playlist_entry_data["transition"].path) - self.player.sub_files = self.text_paths["transition"] + self.player.play(str(self.playlist_entry_data["transition"].path)) + self.player.sub_files = str(self.text_paths["transition"]) self.player.end = str(self.durations["transition"]) return if what == "song": # manage instrumental track/file - path_audio = self.playlist_entry_data["song"].path_audio + path_audio = str(self.playlist_entry_data["song"].path_audio) if path_audio: if path_audio == "self": # mpv use different index for each track, so we can safely request @@ -406,9 +407,11 @@ def play(self, what): # if the subtitle file cannot be discovered, do not request it if self.playlist_entry_data["song"].path_subtitle: - self.player.sub_files = [self.playlist_entry_data["song"].path_subtitle] + self.player.sub_files = [ + str(self.playlist_entry_data["song"].path_subtitle) + ] - self.player.play(self.playlist_entry_data["song"].path) + self.player.play(str(self.playlist_entry_data["song"].path)) return @@ -519,7 +522,7 @@ def set_playlist_entry_player(self, playlist_entry, file_path, autoplay): Args: playlist_entry (dict): Playlist entry object. - file_path (path.Path): Absolute path to the song file. + file_path (pathlib.Path): Absolute path to the song file. autoplay (bool): If `True`, start to play transition screen as soon as possible (i.e. as soon as the transition screen media is ready). The song media is prepared when the transition screen @@ -543,9 +546,9 @@ def set_playlist_entry_player(self, playlist_entry, file_path, autoplay): # manually set the subtitles as a workaround for the matching of # mpv being too permissive - path_without_ext = file_path.dirname() / file_path.stem + path_without_ext = file_path.parent / file_path.stem for subtitle_extension in SUBTITLE_EXTENSIONS: - path_subtitle = path_without_ext + subtitle_extension + path_subtitle = path_without_ext.with_suffix(subtitle_extension) if path_subtitle.exists(): break @@ -570,7 +573,7 @@ def manage_instrumental(self, playlist_entry, file_path): Args: playlist_entry (dict): Playlist entry data. Must contain the key `use_instrumental`. - file_path (path.Path): Path of the song file. + file_path (pathlib.Path): Path of the song file. """ # get instrumental file if possible audio_path = self.get_instrumental_file(file_path) @@ -777,7 +780,7 @@ class MediaPlayerMpvPost0330(MediaPlayerMpvOld): errors (queue.Queue): Error queue to communicate the exception to the main thread. config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. Attributes: stop (threading.Event): Stop event that notify to stop the entire @@ -786,14 +789,14 @@ class MediaPlayerMpvPost0330(MediaPlayerMpvOld): main thread. player_name (str): Name of mpv. fullscreen (bool): If `True`, mpv will be fullscreen. - kara_folder_path (path.Path): Path to the karaoke folder. + kara_folder_path (pathlib.Path): Path to the karaoke folder. playlist_entry (dict): Playlist entyr object. callbacks (dict): High level callbacks associated with the media player. warn_long_exit (bool): If `True`, display a warning message if the media player takes too long to stop. durations (dict of int): Duration of the different screens in seconds. - text_paths (dict of path.Path): Path of the different text screens. + text_paths (dict of pathlib.Path): Path of the different text screens. text_generator (dakara_player.text_generator.TextGenerator): Text generator instance. background_loader @@ -846,18 +849,18 @@ def is_playing_this(self, what, media_path=None): Args: what (str): Tell if mpv current track is of the requested type, but not if it is actually playing it (it can be in pause). - media_path (path.Path): Optional media path. + media_path (pathlib.Path): Optional media path. Returns: bool: `True` if mpv is playing the requested type. """ - media_path = media_path or self.player.path + media_path = Path(media_path or self.player.path or "") if what == "idle": return media_path == self.background_loader.backgrounds["idle"] - return ( - media_path == self.playlist_entry_data[what].path and media_path is not None + return (media_path == self.playlist_entry_data[what].path) and ( + media_path != Path("") ) @safe @@ -921,7 +924,7 @@ class MediaPlayerMpvPost0340(MediaPlayerMpvPost0330): errors (queue.Queue): Error queue to communicate the exception to the main thread. config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. Attributes: stop (threading.Event): Stop event that notify to stop the entire @@ -930,14 +933,14 @@ class MediaPlayerMpvPost0340(MediaPlayerMpvPost0330): main thread. player_name (str): Name of mpv. fullscreen (bool): If `True`, mpv will be fullscreen. - kara_folder_path (path.Path): Path to the karaoke folder. + kara_folder_path (pathlib.Path): Path to the karaoke folder. playlist_entry (dict): Playlist entyr object. callbacks (dict): High level callbacks associated with the media player. warn_long_exit (bool): If `True`, display a warning message if the media player takes too long to stop. durations (dict of int): Duration of the different screens in seconds. - text_paths (dict of path.Path): Path of the different text screens. + text_paths (dict of pathlib.Path): Path of the different text screens. text_generator (dakara_player.text_generator.TextGenerator): Text generator instance. background_loader diff --git a/src/dakara_player/media_player/vlc.py b/src/dakara_player/media_player/vlc.py index 9e5ea6a4..44e70642 100644 --- a/src/dakara_player/media_player/vlc.py +++ b/src/dakara_player/media_player/vlc.py @@ -51,7 +51,7 @@ class MediaPlayerVlc(MediaPlayer): errors (queue.Queue): Error queue to communicate the exception to the main thread. config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. Attributes: stop (threading.Event): Stop event that notify to stop the entire @@ -60,14 +60,14 @@ class MediaPlayerVlc(MediaPlayer): main thread. player_name (str): Name of VLC. fullscreen (bool): If `True`, VLC will be fullscreen. - kara_folder_path (path.Path): Path to the karaoke folder. + kara_folder_path (pathlib.Path): Path to the karaoke folder. playlist_entry (dict): Playlist entyr object. callbacks (dict): High level callbacks associated with the media player. warn_long_exit (bool): If `True`, display a warning message if the media player takes too long to stop. durations (dict of int): Duration of the different screens in seconds. - text_paths (dict of path.Path): Path of the different text screens. + text_paths (dict of pathlib.Path): Path of the different text screens. text_generator (dakara_player.text_generator.TextGenerator): Text generator instance. background_loader @@ -104,7 +104,7 @@ def init_player(self, config, tempdir): Args: config (dict): Dictionary of configuration. - tempdir (path.Path): Path of the temporary directory. + tempdir (pathlib.Path): Path of the temporary directory. """ # parameters config_vlc = config.get("vlc") or {} @@ -277,7 +277,7 @@ def play(self, what): if what == "idle": # create idle screen media media = self.instance.media_new_path( - self.background_loader.backgrounds["idle"] + str(self.background_loader.backgrounds["idle"]) ) media.add_options( @@ -415,7 +415,7 @@ def set_playlist_entry_player(self, playlist_entry, file_path, autoplay): Args: playlist_entry (dict): Playlist entry object. - file_path (path.Path): Absolute path to the song file. + file_path (pathlib.Path): Absolute path to the song file. autoplay (bool): If `True`, start to play transition screen as soon as possible (i.e. as soon as the transition screen media is ready). The song media is prepared when the transition screen @@ -423,7 +423,7 @@ def set_playlist_entry_player(self, playlist_entry, file_path, autoplay): """ # create transition screen media media_transition = self.instance.media_new_path( - self.background_loader.backgrounds["transition"] + str(self.background_loader.backgrounds["transition"]) ) media_transition.add_options( @@ -447,7 +447,7 @@ def set_playlist_entry_player(self, playlist_entry, file_path, autoplay): self.play("transition") # create song media - media_song = self.instance.media_new_path(file_path) + media_song = self.instance.media_new_path(str(file_path)) media_song.add_options(*self.media_parameters) media_song.parse() set_metadata( @@ -469,7 +469,7 @@ def manage_instrumental(self, playlist_entry, file_path): Args: playlist_entry (dict): Playlist entry data. Must contain the key `use_instrumental`. - file_path (path.Path): Path of the song file. + file_path (pathlib.Path): Path of the song file. """ # get instrumental file if possible audio_path = self.get_instrumental_file(file_path) diff --git a/tests/integration/test_media_player_mpv.py b/tests/integration/test_media_player_mpv.py index 06abe52a..581171ea 100644 --- a/tests/integration/test_media_player_mpv.py +++ b/tests/integration/test_media_player_mpv.py @@ -59,7 +59,7 @@ def get_instance(self, config=None, check_error=True): unittest.case._LoggingWatcher: Captured output. """ config_full = { - "kara_folder": self.kara_folder, + "kara_folder": str(self.kara_folder_path), "fullscreen": self.fullscreen, "mpv": {"vo": "null", "ao": "null"}, } diff --git a/tests/integration/test_media_player_vlc.py b/tests/integration/test_media_player_vlc.py index 1f607401..5f433ec6 100644 --- a/tests/integration/test_media_player_vlc.py +++ b/tests/integration/test_media_player_vlc.py @@ -1,12 +1,11 @@ from contextlib import ExitStack, contextmanager +from pathlib import Path from queue import Queue from tempfile import TemporaryDirectory from threading import Event from unittest import skipIf, skipUnless from unittest.mock import MagicMock -from path import Path - try: import vlc @@ -74,7 +73,7 @@ def get_instance(self, config=None, check_error=True): """ config_full = { - "kara_folder": self.kara_folder, + "kara_folder": str(self.kara_folder_path), "fullscreen": self.fullscreen, "vlc": { "instance_parameters": self.instance_parameters, @@ -344,7 +343,7 @@ def test_play_playlist_entry_instrumental_track_avi(self): def test_play_playlist_entry_instrumental_file(self): """Test to play a playlist entry using instrumental file.""" # request to use instrumental file - self.playlist_entry1["song"]["file_path"] = self.song2_path + self.playlist_entry1["song"]["file_path"] = str(self.song2_path) self.playlist_entry1["use_instrumental"] = True with self.get_instance() as (vlc_player, _, _): diff --git a/tests/unit/test_media_player_mpv.py b/tests/unit/test_media_player_mpv.py index d7a9367a..6ea4cbe0 100644 --- a/tests/unit/test_media_player_mpv.py +++ b/tests/unit/test_media_player_mpv.py @@ -413,7 +413,9 @@ def test_handle_end_file_transition(self, mocked_play, mocked_clear_playlist_ent mpv_player, (mocked_player, _, _), _ = self.get_instance() mpv_player.set_callback("finished", MagicMock()) self.set_playlist_entry(mpv_player) - mocked_player.playlist[0]["filename"] = Path(gettempdir()) / "transition.png" + mocked_player.playlist[0]["filename"] = str( + Path(gettempdir()) / "transition.png" + ) # call the method with self.assertLogs("dakara_player.media_player.mpv", "DEBUG") as logger: @@ -490,7 +492,7 @@ def test_handle_end_file_other(self, mocked_play, mocked_clear_playlist_entry): mpv_player, (mocked_player, _, _), _ = self.get_instance() mpv_player.set_callback("finished", MagicMock()) self.set_playlist_entry(mpv_player) - mocked_player.playlist[0]["filename"] = Path(gettempdir()) / "other" + mocked_player.playlist[0]["filename"] = str(Path(gettempdir()) / "other") self.assertFalse(mpv_player.stop.is_set()) @@ -768,7 +770,9 @@ def test_handle_end_file_transition(self, mocked_play, mocked_clear_playlist_ent mpv_player, (mocked_player, _, _), _ = self.get_instance() mpv_player.set_callback("finished", MagicMock()) self.set_playlist_entry(mpv_player) - mocked_player.playlist[0]["filename"] = Path(gettempdir()) / "transition.png" + mocked_player.playlist[0]["filename"] = str( + Path(gettempdir()) / "transition.png" + ) # call the method with self.assertLogs("dakara_player.media_player.mpv", "DEBUG") as logger: @@ -851,7 +855,7 @@ def test_handle_end_file_other(self, mocked_play, mocked_clear_playlist_entry): mpv_player, (mocked_player, _, _), _ = self.get_instance() mpv_player.set_callback("finished", MagicMock()) self.set_playlist_entry(mpv_player) - mocked_player.playlist[0]["filename"] = Path(gettempdir()) / "other" + mocked_player.playlist[0]["filename"] = str(Path(gettempdir()) / "other") self.assertFalse(mpv_player.stop.is_set()) From 4032644f5e88ad12b07cebc70045331af9c4da7a Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 00:54:26 +0100 Subject: [PATCH 04/25] Remove path dependency --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 15bbf89c..a8756685 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ dependencies = [ "dakarabase>=2.0.0,<2.1.0", "filetype>=1.2.0,<1.3.0", "packaging>=21.3,<22.0", - "path>=16.4.0,<16.5.0", "python-mpv-jsonipc>=1.1.13,<1.2.0", "python-vlc>=3.0.18121,<3.1.0,!=3.0.12117", "setuptools>=68", From 4c10d8ed7c710fc01a2dd13abe3b6134e1eaa443 Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:09:24 +0100 Subject: [PATCH 05/25] Fix islink --- src/dakara_player/font.py | 2 +- tests/unit/test_font.py | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index 11de30b0..ed9ca387 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -187,7 +187,7 @@ def load_font(self, font_file_path, system_font_path_list, user_font_path_list): # check if the font exists as broken link at user level # in this case remove it and continue execution font_file_user_path = self.FONT_DIR_USER.expanduser() / font_file_name - if font_file_user_path.islink(): + if font_file_user_path.is_link(): logger.debug( "Dead symbolic link found for font '%s' in user directory, " "removing it", diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index b3f77b34..b0eb37e7 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -166,8 +166,8 @@ def test_load( @patch.object(Path, "unlink", autospec=True) @patch.object(Path, "copy", autospec=True) - @patch.object(Path, "islink", autospec=True) - def test_load_font_system(self, mocked_islink, mocked_copy, mocked_unlink): + @patch.object(Path, "is_link", autospec=True) + def test_load_font_system(self, mocked_is_link, mocked_copy, mocked_unlink): """Test to load one font which is in system directory.""" font_loader = self.get_font_loader() @@ -195,14 +195,14 @@ def test_load_font_system(self, mocked_islink, mocked_copy, mocked_unlink): ) # assert the call - mocked_islink.assert_not_called() + mocked_is_link.assert_not_called() mocked_unlink.assert_not_called() mocked_copy.assert_not_called() @patch.object(Path, "unlink", autospec=True) @patch.object(Path, "copy", autospec=True) - @patch.object(Path, "islink", autospec=True) - def test_load_font_user(self, mocked_islink, mocked_copy, mocked_unlink): + @patch.object(Path, "is_link", autospec=True) + def test_load_font_user(self, mocked_is_link, mocked_copy, mocked_unlink): """Test to load one font which is in user directory.""" font_loader = self.get_font_loader() @@ -230,22 +230,22 @@ def test_load_font_user(self, mocked_islink, mocked_copy, mocked_unlink): ) # assert the call - mocked_islink.assert_not_called() + mocked_is_link.assert_not_called() mocked_unlink.assert_not_called() mocked_copy.assert_not_called() @patch.object(Path, "unlink", autospec=True) @patch.object(Path, "copy", autospec=True) - @patch.object(Path, "islink", autospec=True) + @patch.object(Path, "is_link", autospec=True) def test_load_font_user_link_dead_install( self, - mocked_islink, + mocked_is_link, mocked_copy, mocked_unlink, ): """Test to load one font which is in user directory as dead link.""" # prepare the mock - mocked_islink.return_value = True + mocked_is_link.return_value = True font_loader = self.get_font_loader() @@ -272,17 +272,17 @@ def test_load_font_user_link_dead_install( ) # assert the call - mocked_islink.assert_called_with(font_path) + mocked_is_link.assert_called_with(font_path) mocked_unlink.assert_called_with(font_path) mocked_copy.assert_called_once_with("directory/font_file.ttf", font_path) @patch.object(Path, "unlink", autospec=True) @patch.object(Path, "copy", autospec=True) - @patch.object(Path, "islink", autospec=True) - def test_load_font_install(self, mocked_islink, mocked_copy, mocked_unlink): + @patch.object(Path, "is_link", autospec=True) + def test_load_font_install(self, mocked_is_link, mocked_copy, mocked_unlink): """Test to load one font which is not installed.""" # prepare the mock - mocked_islink.return_value = False + mocked_is_link.return_value = False font_loader = self.get_font_loader() @@ -311,7 +311,7 @@ def test_load_font_install(self, mocked_islink, mocked_copy, mocked_unlink): ) # assert the call - mocked_islink.assert_called_with(font_path) + mocked_is_link.assert_called_with(font_path) mocked_unlink.assert_not_called() mocked_copy.assert_called_once_with( "directory/font_file.ttf", self.user_directory / ".fonts/font_file.ttf" From c17ce98281d6e890d9978f88802ec830b1b901c3 Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:11:28 +0100 Subject: [PATCH 06/25] Fix is_link --- src/dakara_player/font.py | 2 +- tests/unit/test_font.py | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index ed9ca387..71fe297d 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -187,7 +187,7 @@ def load_font(self, font_file_path, system_font_path_list, user_font_path_list): # check if the font exists as broken link at user level # in this case remove it and continue execution font_file_user_path = self.FONT_DIR_USER.expanduser() / font_file_name - if font_file_user_path.is_link(): + if font_file_user_path.is_symlink(): logger.debug( "Dead symbolic link found for font '%s' in user directory, " "removing it", diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index b0eb37e7..904ed4e8 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -166,8 +166,8 @@ def test_load( @patch.object(Path, "unlink", autospec=True) @patch.object(Path, "copy", autospec=True) - @patch.object(Path, "is_link", autospec=True) - def test_load_font_system(self, mocked_is_link, mocked_copy, mocked_unlink): + @patch.object(Path, "is_symlink", autospec=True) + def test_load_font_system(self, mocked_is_symlink, mocked_copy, mocked_unlink): """Test to load one font which is in system directory.""" font_loader = self.get_font_loader() @@ -195,14 +195,14 @@ def test_load_font_system(self, mocked_is_link, mocked_copy, mocked_unlink): ) # assert the call - mocked_is_link.assert_not_called() + mocked_is_symlink.assert_not_called() mocked_unlink.assert_not_called() mocked_copy.assert_not_called() @patch.object(Path, "unlink", autospec=True) @patch.object(Path, "copy", autospec=True) - @patch.object(Path, "is_link", autospec=True) - def test_load_font_user(self, mocked_is_link, mocked_copy, mocked_unlink): + @patch.object(Path, "is_symlink", autospec=True) + def test_load_font_user(self, mocked_is_symlink, mocked_copy, mocked_unlink): """Test to load one font which is in user directory.""" font_loader = self.get_font_loader() @@ -230,22 +230,22 @@ def test_load_font_user(self, mocked_is_link, mocked_copy, mocked_unlink): ) # assert the call - mocked_is_link.assert_not_called() + mocked_is_symlink.assert_not_called() mocked_unlink.assert_not_called() mocked_copy.assert_not_called() @patch.object(Path, "unlink", autospec=True) @patch.object(Path, "copy", autospec=True) - @patch.object(Path, "is_link", autospec=True) + @patch.object(Path, "is_symlink", autospec=True) def test_load_font_user_link_dead_install( self, - mocked_is_link, + mocked_is_symlink, mocked_copy, mocked_unlink, ): """Test to load one font which is in user directory as dead link.""" # prepare the mock - mocked_is_link.return_value = True + mocked_is_symlink.return_value = True font_loader = self.get_font_loader() @@ -272,17 +272,17 @@ def test_load_font_user_link_dead_install( ) # assert the call - mocked_is_link.assert_called_with(font_path) + mocked_is_symlink.assert_called_with(font_path) mocked_unlink.assert_called_with(font_path) mocked_copy.assert_called_once_with("directory/font_file.ttf", font_path) @patch.object(Path, "unlink", autospec=True) @patch.object(Path, "copy", autospec=True) - @patch.object(Path, "is_link", autospec=True) - def test_load_font_install(self, mocked_is_link, mocked_copy, mocked_unlink): + @patch.object(Path, "is_symlink", autospec=True) + def test_load_font_install(self, mocked_is_symlink, mocked_copy, mocked_unlink): """Test to load one font which is not installed.""" # prepare the mock - mocked_is_link.return_value = False + mocked_is_symlink.return_value = False font_loader = self.get_font_loader() @@ -311,7 +311,7 @@ def test_load_font_install(self, mocked_is_link, mocked_copy, mocked_unlink): ) # assert the call - mocked_is_link.assert_called_with(font_path) + mocked_is_symlink.assert_called_with(font_path) mocked_unlink.assert_not_called() mocked_copy.assert_called_once_with( "directory/font_file.ttf", self.user_directory / ".fonts/font_file.ttf" From 9da2ab45a2acb7e0a5b2f106a41a9cd599ab7bda Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:18:20 +0100 Subject: [PATCH 07/25] Fix path manipulations --- src/dakara_player/font.py | 2 +- tests/unit/test_font.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index 71fe297d..f3317622 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -193,7 +193,7 @@ def load_font(self, font_file_path, system_font_path_list, user_font_path_list): "removing it", font_file_name, ) - font_file_user_path.unlink_p() + font_file_user_path.unlink(missing_ok=True) # then, if the font is not installed, load by copying it font_file_path.copy(font_file_user_path) diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index 904ed4e8..5dbf26cd 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -331,7 +331,7 @@ def test_unload(self, mocked_unlink): # assert the call # especially check that the unload function does not alter the list of # elements we are iterating on - mocked_unlink.assert_has_calls([call("font1"), call("font2")]) + mocked_unlink.assert_has_calls([call(Path("font1")), call(Path("font2"))]) @patch.object(Path, "unlink", autospec=True) def test_unload_font(self, mocked_unlink): From eed732267987d4ca451b9cc89d980c2438bf3343 Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:21:46 +0100 Subject: [PATCH 08/25] Fix missing shutil copies --- src/dakara_player/font.py | 3 ++- tests/unit/test_font.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index f3317622..e6234961 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -6,6 +6,7 @@ from abc import ABC, abstractmethod from importlib.resources import contents, path from pathlib import Path +from shutil import copy from dakara_base.exceptions import DakaraError @@ -196,7 +197,7 @@ def load_font(self, font_file_path, system_font_path_list, user_font_path_list): font_file_user_path.unlink(missing_ok=True) # then, if the font is not installed, load by copying it - font_file_path.copy(font_file_user_path) + copy(font_file_path, font_file_user_path) # register the font self.fonts_loaded[font_file_name] = font_file_user_path diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index 5dbf26cd..b9f581d9 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -165,7 +165,7 @@ def test_load( ) @patch.object(Path, "unlink", autospec=True) - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.font.copy", autospec=True) @patch.object(Path, "is_symlink", autospec=True) def test_load_font_system(self, mocked_is_symlink, mocked_copy, mocked_unlink): """Test to load one font which is in system directory.""" @@ -200,7 +200,7 @@ def test_load_font_system(self, mocked_is_symlink, mocked_copy, mocked_unlink): mocked_copy.assert_not_called() @patch.object(Path, "unlink", autospec=True) - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.font.copy", autospec=True) @patch.object(Path, "is_symlink", autospec=True) def test_load_font_user(self, mocked_is_symlink, mocked_copy, mocked_unlink): """Test to load one font which is in user directory.""" @@ -235,7 +235,7 @@ def test_load_font_user(self, mocked_is_symlink, mocked_copy, mocked_unlink): mocked_copy.assert_not_called() @patch.object(Path, "unlink", autospec=True) - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.font.copy", autospec=True) @patch.object(Path, "is_symlink", autospec=True) def test_load_font_user_link_dead_install( self, @@ -277,7 +277,7 @@ def test_load_font_user_link_dead_install( mocked_copy.assert_called_once_with("directory/font_file.ttf", font_path) @patch.object(Path, "unlink", autospec=True) - @patch.object(Path, "copy", autospec=True) + @patch("dakara_player.font.copy", autospec=True) @patch.object(Path, "is_symlink", autospec=True) def test_load_font_install(self, mocked_is_symlink, mocked_copy, mocked_unlink): """Test to load one font which is not installed.""" From 63e8467b84cba2f8942a26d946541c713d41d8f8 Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:24:41 +0100 Subject: [PATCH 09/25] Fix path manipulations --- src/dakara_player/font.py | 4 ++-- tests/unit/test_font.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index e6234961..92ff2458 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -152,7 +152,7 @@ def get_user_font_path_list(self): def load(self): """Load the fonts.""" # ensure that the user font directory exists - self.FONT_DIR_USER.expanduser().mkdir_p() + self.FONT_DIR_USER.expanduser().mkdir(parents=True, exists_ok=True) # get system and user font files system_font_path_list = self.get_system_font_path_list() @@ -173,7 +173,7 @@ def load_font(self, font_file_path, system_font_path_list, user_font_path_list): of user fonts. """ # get font file name - font_file_name = font_file_path.basename() + font_file_name = font_file_path.name # check if the font is installed at system level if any(font_file_name in path for path in system_font_path_list): diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index b9f581d9..f2423e74 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -127,10 +127,10 @@ def get_font_loader(self): @patch.object(FontLoaderLinux, "load_font", autospec=True) @patch.object(FontLoaderLinux, "get_font_path_iterator", autospec=True) @patch.object(Path, "walkfiles", autospec=True) - @patch.object(Path, "mkdir_p", autospec=True) + @patch.object(Path, "mkdir", autospec=True) def test_load( self, - mocked_mkdir_p, + mocked_mkdir, mocked_walkfiles, mocked_get_font_path_iterator, mocked_load_font, @@ -149,7 +149,9 @@ def test_load( font_loader.load() # assert the call - mocked_mkdir_p.assert_called_once_with(self.user_directory / ".fonts") + mocked_mkdir.assert_called_once_with( + self.user_directory / ".fonts", parents=True, exists_ok=True + ) mocked_walkfiles.assert_has_calls( [ call(Path("/") / "usr" / "share" / "fonts"), From f9eec074bd1595bbdb0faa12f3bd27475a5f3edc Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:33:29 +0100 Subject: [PATCH 10/25] Fix rglob use for fonts --- src/dakara_player/font.py | 4 ++-- tests/unit/test_font.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index 92ff2458..79845cbc 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -139,7 +139,7 @@ def get_system_font_path_list(self): Returns: list of pathlib.Path: List of font paths. """ - return list(self.FONT_DIR_SYSTEM.walkfiles()) + return list(self.FONT_DIR_SYSTEM.rglob("*")) def get_user_font_path_list(self): """Retrieve the list of user fonts. @@ -147,7 +147,7 @@ def get_user_font_path_list(self): Returns: list of pathlib.Path: List of font paths. """ - return list(self.FONT_DIR_USER.expanduser().walkfiles()) + return list(self.FONT_DIR_USER.expanduser().rglob("*")) def load(self): """Load the fonts.""" diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index f2423e74..e0cf7389 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -126,21 +126,21 @@ def get_font_loader(self): @patch.object(FontLoaderLinux, "load_font", autospec=True) @patch.object(FontLoaderLinux, "get_font_path_iterator", autospec=True) - @patch.object(Path, "walkfiles", autospec=True) + @patch.object(Path, "rglob", autospec=True) @patch.object(Path, "mkdir", autospec=True) def test_load( self, mocked_mkdir, - mocked_walkfiles, + mocked_rglob, mocked_get_font_path_iterator, mocked_load_font, ): """Test to load fonts.""" # prepare the mock mocked_get_font_path_iterator.return_value = (p for p in [self.font_path]) - mocked_walkfiles.side_effect = [ - (p for p in [Path("/") / "usr" / "share" / "fonts" / "font1"]), - (p for p in [self.user_directory / ".fonts" / "font2"]), + mocked_rglob.side_effect = [ + [Path("/") / "usr" / "share" / "fonts" / "font1"], + [self.user_directory / ".fonts" / "font2"], ] font_loader = self.get_font_loader() @@ -152,7 +152,7 @@ def test_load( mocked_mkdir.assert_called_once_with( self.user_directory / ".fonts", parents=True, exists_ok=True ) - mocked_walkfiles.assert_has_calls( + mocked_rglob.assert_has_calls( [ call(Path("/") / "usr" / "share" / "fonts"), call(self.user_directory / ".fonts"), @@ -275,7 +275,7 @@ def test_load_font_user_link_dead_install( # assert the call mocked_is_symlink.assert_called_with(font_path) - mocked_unlink.assert_called_with(font_path) + mocked_unlink.assert_called_with(font_path, missing_ok=True) mocked_copy.assert_called_once_with("directory/font_file.ttf", font_path) @patch.object(Path, "unlink", autospec=True) From e85b201f1cb14b0cf032abdc57e8b790e535ac5d Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:41:28 +0100 Subject: [PATCH 11/25] Fix rglob use --- src/dakara_player/font.py | 28 ++++++++++++++-------------- tests/unit/test_font.py | 6 ++++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index 79845cbc..ea8d7a64 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -133,21 +133,21 @@ def __init__(self, *args, **kwargs): # create list of fonts self.fonts_loaded = {} - def get_system_font_path_list(self): + def get_system_font_name_list(self): """Retrieve the list of system fonts. Returns: list of pathlib.Path: List of font paths. """ - return list(self.FONT_DIR_SYSTEM.rglob("*")) + return [path.name for path in self.FONT_DIR_SYSTEM.rglob("*")] - def get_user_font_path_list(self): + def get_user_font_name_list(self): """Retrieve the list of user fonts. Returns: list of pathlib.Path: List of font paths. """ - return list(self.FONT_DIR_USER.expanduser().rglob("*")) + return [path.name for path in self.FONT_DIR_USER.expanduser().rglob("*")] def load(self): """Load the fonts.""" @@ -155,33 +155,33 @@ def load(self): self.FONT_DIR_USER.expanduser().mkdir(parents=True, exists_ok=True) # get system and user font files - system_font_path_list = self.get_system_font_path_list() - user_font_path_list = self.get_user_font_path_list() + system_font_name_list = self.get_system_font_name_list() + user_font_name_list = self.get_user_font_name_list() # load fonts for font_file_path in self.get_font_path_iterator(): - self.load_font(font_file_path, system_font_path_list, user_font_path_list) + self.load_font(font_file_path, system_font_name_list, user_font_name_list) - def load_font(self, font_file_path, system_font_path_list, user_font_path_list): + def load_font(self, font_file_path, system_font_name_list, user_font_name_list): """Load the provided font. Args: font_file_path (pathlib.Path): Absolute path of the font to load. - system_font_path_list (list of pathlib.Path): List of absolute - paths of system fonts. - user_font_path_list (list of pathlib.Path): List of absolute paths - of user fonts. + system_font_name_list (list of pathlib.Path): List of system fonts + name. + user_font_name_list (list of pathlib.Path): List of user fonts + name. """ # get font file name font_file_name = font_file_path.name # check if the font is installed at system level - if any(font_file_name in path for path in system_font_path_list): + if any(font_file_name in system_font_name_list): logger.debug("Font '%s' found in system directory", font_file_name) return # check if the font is installed at user level - if any(font_file_name in path for path in user_font_path_list): + if any(font_file_name in user_font_name_list): logger.debug("Font '%s' found in user directory", font_file_name) return diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index e0cf7389..2f5fc64e 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -150,7 +150,7 @@ def test_load( # assert the call mocked_mkdir.assert_called_once_with( - self.user_directory / ".fonts", parents=True, exists_ok=True + self.user_directory / ".fonts", parents=True, exist_ok=True ) mocked_rglob.assert_has_calls( [ @@ -276,7 +276,9 @@ def test_load_font_user_link_dead_install( # assert the call mocked_is_symlink.assert_called_with(font_path) mocked_unlink.assert_called_with(font_path, missing_ok=True) - mocked_copy.assert_called_once_with("directory/font_file.ttf", font_path) + mocked_copy.assert_called_once_with( + Path("directory") / "font_file.ttf", font_path + ) @patch.object(Path, "unlink", autospec=True) @patch("dakara_player.font.copy", autospec=True) From 280d19f2914cedc7db30d70803f951c9efb66abe Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:43:34 +0100 Subject: [PATCH 12/25] Fix small errors --- src/dakara_player/font.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dakara_player/font.py b/src/dakara_player/font.py index ea8d7a64..83cf4d7d 100644 --- a/src/dakara_player/font.py +++ b/src/dakara_player/font.py @@ -152,7 +152,7 @@ def get_user_font_name_list(self): def load(self): """Load the fonts.""" # ensure that the user font directory exists - self.FONT_DIR_USER.expanduser().mkdir(parents=True, exists_ok=True) + self.FONT_DIR_USER.expanduser().mkdir(parents=True, exist_ok=True) # get system and user font files system_font_name_list = self.get_system_font_name_list() @@ -176,12 +176,12 @@ def load_font(self, font_file_path, system_font_name_list, user_font_name_list): font_file_name = font_file_path.name # check if the font is installed at system level - if any(font_file_name in system_font_name_list): + if font_file_name in system_font_name_list: logger.debug("Font '%s' found in system directory", font_file_name) return # check if the font is installed at user level - if any(font_file_name in user_font_name_list): + if font_file_name in user_font_name_list: logger.debug("Font '%s' found in user directory", font_file_name) return From 719df29dec6dfb0008e1a96168ab4b2d5cbf9690 Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:46:33 +0100 Subject: [PATCH 13/25] Other small fixes --- tests/unit/test_font.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index 2f5fc64e..a934d82b 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -154,8 +154,8 @@ def test_load( ) mocked_rglob.assert_has_calls( [ - call(Path("/") / "usr" / "share" / "fonts"), - call(self.user_directory / ".fonts"), + call(Path("/") / "usr" / "share" / "fonts", "*"), + call(self.user_directory / ".fonts", "*"), ] ) mocked_get_font_path_iterator.assert_called_once_with(font_loader) @@ -318,7 +318,8 @@ def test_load_font_install(self, mocked_is_symlink, mocked_copy, mocked_unlink): mocked_is_symlink.assert_called_with(font_path) mocked_unlink.assert_not_called() mocked_copy.assert_called_once_with( - "directory/font_file.ttf", self.user_directory / ".fonts/font_file.ttf" + Path("directory") / "font_file.ttf", + self.user_directory / ".fonts/font_file.ttf", ) @patch.object(Path, "unlink", autospec=True) From 31fea5aa21fa9869f90b9a53875d3ce8e2c6c380 Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:50:12 +0100 Subject: [PATCH 14/25] Fix tests --- tests/unit/test_font.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_font.py b/tests/unit/test_font.py index a934d82b..238976a9 100644 --- a/tests/unit/test_font.py +++ b/tests/unit/test_font.py @@ -162,8 +162,8 @@ def test_load( mocked_load_font.assert_called_once_with( font_loader, self.font_path, - [Path("/") / "usr" / "share" / "fonts" / "font1"], - [self.user_directory / ".fonts" / "font2"], + ["font1"], + ["font2"], ) @patch.object(Path, "unlink", autospec=True) @@ -180,7 +180,7 @@ def test_load_font_system(self, mocked_is_symlink, mocked_copy, mocked_unlink): with self.assertLogs("dakara_player.font", "DEBUG") as logger: font_loader.load_font( self.font_path, - [Path("/") / "usr" / "share" / "fonts" / "truetype" / "font_file.ttf"], + ["font_file.ttf"], [], ) @@ -216,7 +216,7 @@ def test_load_font_user(self, mocked_is_symlink, mocked_copy, mocked_unlink): font_loader.load_font( self.font_path, [], - [self.user_directory / "fonts" / "truetype" / "font_file.ttf"], + ["font_file.ttf"], ) # post assertions From 4ed287fb6759be632447646b10e00e4741a0d013 Mon Sep 17 00:00:00 2001 From: Neraste Date: Tue, 28 Jan 2025 01:58:20 +0100 Subject: [PATCH 15/25] Fix import --- tests/integration/test_media_player_mpv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_media_player_mpv.py b/tests/integration/test_media_player_mpv.py index 581171ea..bb14945f 100644 --- a/tests/integration/test_media_player_mpv.py +++ b/tests/integration/test_media_player_mpv.py @@ -1,4 +1,5 @@ from contextlib import ExitStack, contextmanager +from pathlib import Path from queue import Queue from tempfile import TemporaryDirectory from threading import Event @@ -8,7 +9,6 @@ from dakara_base.config import Config from func_timeout import func_set_timeout -from path import Path from dakara_player.media_player.base import ( IDLE_BG_NAME, From 7ddd74293be95bf448261202d1e4d3a2fa0c37f8 Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 8 Feb 2025 17:38:17 +0100 Subject: [PATCH 16/25] Detect player error in integration tests --- tests/integration/base.py | 21 +++++++++++++++++---- tests/integration/test_media_player_mpv.py | 11 +++++++---- tests/integration/test_media_player_vlc.py | 11 +++++++---- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/tests/integration/base.py b/tests/integration/base.py index de8a9ba8..e5deb78d 100644 --- a/tests/integration/base.py +++ b/tests/integration/base.py @@ -1,4 +1,5 @@ from pathlib import Path +from queue import Empty from shutil import copy from tempfile import TemporaryDirectory from time import sleep @@ -30,10 +31,12 @@ def wait_is_playing(cls, player, what=None, wait_extra=DELAY): wait_extra (float): Time to wait at the end of the function. """ if what: - cls.wait(lambda: player.is_playing() and player.is_playing_this(what)) + cls.wait( + player, lambda: player.is_playing() and player.is_playing_this(what) + ) else: - cls.wait(lambda: player.is_playing()) + cls.wait(player, lambda: player.is_playing()) sleep(wait_extra) @@ -48,12 +51,12 @@ def wait_is_paused(cls, player, wait_extra=DELAY): player (dakara_player.media_player.base.MediaPlayer): Player. wait_extra (float): Time to wait at the end of the function. """ - cls.wait(lambda: player.is_paused()) + cls.wait(player, lambda: player.is_paused()) sleep(wait_extra) @classmethod - def wait(cls, condition_method): + def wait(cls, player, condition_method): """Wait for a condition to be true safely. Args: @@ -61,6 +64,16 @@ def wait(cls, condition_method): structure, so as to not break the loop in cause of error. """ while True: + # handle any player error + if player.stop.is_set(): + try: + _, error, traceback = player.errors.get(5) + error.with_traceback(traceback) + raise error + + except Empty as error_empty: + raise RuntimeError("Unknown error happened") from error_empty + try: if condition_method(): return diff --git a/tests/integration/test_media_player_mpv.py b/tests/integration/test_media_player_mpv.py index bb14945f..d2f78ad7 100644 --- a/tests/integration/test_media_player_mpv.py +++ b/tests/integration/test_media_player_mpv.py @@ -211,7 +211,7 @@ def test_play_playlist_entry(self): self.assertFalse(mpv_player.is_playing_this("idle")) # wait for the media to end - self.wait(lambda: not mpv_player.is_playing()) + self.wait(mpv_player, lambda: not mpv_player.is_playing()) # assert the player is not playing anything self.assertFalse(mpv_player.is_playing_this("transition")) @@ -424,7 +424,8 @@ def test_restart_song(self): # wait a bit for the player to play self.wait( - lambda: mpv_player.player.time_pos >= REWIND_FAST_FORWARD_DURATION + mpv_player, + lambda: mpv_player.player.time_pos >= REWIND_FAST_FORWARD_DURATION, ) # request playlist entry to restart @@ -762,7 +763,8 @@ def test_rewind_song(self): # wait a bit for the player to play self.wait( - lambda: mpv_player.player.time_pos >= REWIND_FAST_FORWARD_DURATION * 2 + mpv_player, + lambda: mpv_player.player.time_pos >= REWIND_FAST_FORWARD_DURATION * 2, ) timing1 = mpv_player.player.time_pos @@ -840,7 +842,8 @@ def test_fast_forward_song(self): # wait a bit for the player to play self.wait( - lambda: mpv_player.player.time_pos >= REWIND_FAST_FORWARD_DURATION * 2 + mpv_player, + lambda: mpv_player.player.time_pos >= REWIND_FAST_FORWARD_DURATION * 2, ) timing1 = mpv_player.player.time_pos diff --git a/tests/integration/test_media_player_vlc.py b/tests/integration/test_media_player_vlc.py index 5f433ec6..9716d674 100644 --- a/tests/integration/test_media_player_vlc.py +++ b/tests/integration/test_media_player_vlc.py @@ -241,7 +241,7 @@ def test_play_playlist_entry(self): self.assertFalse(vlc_player.is_playing_this("idle")) # wait for the media to end - self.wait(lambda: not vlc_player.is_playing()) + self.wait(vlc_player, lambda: not vlc_player.is_playing()) # assert the player is not playing anything self.assertFalse(vlc_player.is_playing_this("transition")) @@ -520,8 +520,9 @@ def test_restart_song(self): # wait a bit for the player to play self.wait( + vlc_player, lambda: vlc_player.player.get_time() - >= REWIND_FAST_FORWARD_DURATION * 1000 + >= REWIND_FAST_FORWARD_DURATION * 1000, ) # request to restart media @@ -675,8 +676,9 @@ def test_rewind_song(self): # wait a bit for the player to play self.wait( + vlc_player, lambda: vlc_player.player.get_time() - >= REWIND_FAST_FORWARD_DURATION * 2 * 1000 + >= REWIND_FAST_FORWARD_DURATION * 2 * 1000, ) timing1 = vlc_player.player.get_time() / 1000 @@ -753,8 +755,9 @@ def test_fast_forward_song(self): # wait a bit for the player to play self.wait( + vlc_player, lambda: vlc_player.player.get_time() - >= REWIND_FAST_FORWARD_DURATION * 2 * 1000 + >= REWIND_FAST_FORWARD_DURATION * 2 * 1000, ) timing1 = vlc_player.player.get_time() / 1000 From b15984f87dc192847a4f03f32ec67367c75213dd Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 8 Feb 2025 18:02:21 +0100 Subject: [PATCH 17/25] Fix error when detecting audio file Add test for audio.py --- src/dakara_player/audio.py | 2 +- tests/unit/test_audio.py | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/unit/test_audio.py diff --git a/src/dakara_player/audio.py b/src/dakara_player/audio.py index 19e2c592..367edb9c 100644 --- a/src/dakara_player/audio.py +++ b/src/dakara_player/audio.py @@ -13,7 +13,7 @@ def get_audio_files(filepath): list of pathlib.Path: List of paths of audio files. """ # list files with similar stem - items = filepath.parent.glob("{}.*".format(filepath.name)) + items = filepath.parent.glob(f"{filepath.stem}.*") return [item for item in items if item != filepath and is_audio_file(item)] diff --git a/tests/unit/test_audio.py b/tests/unit/test_audio.py new file mode 100644 index 00000000..f757f530 --- /dev/null +++ b/tests/unit/test_audio.py @@ -0,0 +1,60 @@ +from importlib.resources import as_file, files +from pathlib import Path +from unittest import TestCase +from unittest.mock import patch + +from dakara_player.audio import get_audio_files, is_audio_file + + +class IsAudioFileTestCase(TestCase): + def test_mp3(self): + """Test to detect a MP3 file.""" + with as_file(files("tests.resources").joinpath("song2.mp3")) as file: + self.assertTrue(is_audio_file(file)) + + def test_ass(self): + """Test to not detect an ASS file.""" + with as_file(files("tests.resources").joinpath("song2.ass")) as file: + self.assertFalse(is_audio_file(file)) + + def test_mkv(self): + """Test to not detect a MKV file.""" + with as_file(files("tests.resources").joinpath("song2.mkv")) as file: + self.assertFalse(is_audio_file(file)) + + +@patch("dakara_player.audio.is_audio_file", autospec=True) +@patch.object(Path, "glob", autospec=True) +class GetAudioFileTestCase(TestCase): + def test_no_audio(self, mocked_glob, mocked_is_audio_file): + """Test no audio file found.""" + file_video = Path("aa") / "file.mp4" + mocked_glob.return_value = [file_video] + + self.assertEqual(len(get_audio_files(file_video)), 0) + mocked_glob.assert_called_with(Path("aa"), "file.*") + + def test_one_audio(self, mocked_glob, mocked_is_audio_file): + """Test one audio file found.""" + file_video = Path("aa") / "file.mp4" + file_audio = Path("aa") / "file.mp3" + mocked_glob.return_value = [file_video, file_audio] + mocked_is_audio_file.return_value = True + + files_audio = get_audio_files(file_video) + self.assertEqual(len(files_audio), 1) + self.assertListEqual(files_audio, [file_audio]) + mocked_glob.assert_called_with(Path("aa"), "file.*") + + def test_two_audio(self, mocked_glob, mocked_is_audio_file): + """Test one audio file found.""" + file_video = Path("aa") / "file.mp4" + file_audio1 = Path("aa") / "file.mp3" + file_audio2 = Path("aa") / "file.ogg" + mocked_glob.return_value = [file_video, file_audio1, file_audio2] + mocked_is_audio_file.return_value = True + + files_audio = get_audio_files(file_video) + self.assertEqual(len(files_audio), 2) + self.assertListEqual(files_audio, [file_audio1, file_audio2]) + mocked_glob.assert_called_with(Path("aa"), "file.*") From 54670d61cbbc880dfe3da88877a5c72c4a120de4 Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 8 Feb 2025 18:35:54 +0100 Subject: [PATCH 18/25] Fix background paths Fix MPV tests for background paths --- src/dakara_player/background.py | 10 +++---- src/dakara_player/media_player/mpv.py | 2 +- tests/integration/test_media_player_mpv.py | 34 ++++++++++++---------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/dakara_player/background.py b/src/dakara_player/background.py index eacf87d0..7a041b32 100644 --- a/src/dakara_player/background.py +++ b/src/dakara_player/background.py @@ -44,9 +44,9 @@ class BackgroundLoader: >>> loader.load() >>> loader.backgrounds { - "idle": "/destination/idle.png", - "transition": "/destination/transition.png", - "other": "/destination/something.png" + "idle": Path("/destination/idle.png"), + "transition": Path("/destination/transition.png"), + "other": Path("/destination/something.png") } Args: @@ -102,7 +102,7 @@ def get_background_path(self, background_name, file_name): logger.debug( "Loading custom %s background file '%s'", background_name, file_name ) - return copy(file_path, self.destination) + return Path(copy(file_path, self.destination)) # trying to load from package by default try: @@ -113,7 +113,7 @@ def get_background_path(self, background_name, file_name): file_name, ) file_path = Path(file) - return copy(file_path, self.destination) + return Path(copy(file_path, self.destination)) except FileNotFoundError as error: raise BackgroundNotFoundError( diff --git a/src/dakara_player/media_player/mpv.py b/src/dakara_player/media_player/mpv.py index 735af0df..edd2be17 100644 --- a/src/dakara_player/media_player/mpv.py +++ b/src/dakara_player/media_player/mpv.py @@ -379,7 +379,7 @@ def play(self, what): return self.generate_text("idle") - self.player.play(self.background_loader.backgrounds["idle"]) + self.player.play(str(self.background_loader.backgrounds["idle"])) self.player.sub_files = str(self.text_paths["idle"]) return diff --git a/tests/integration/test_media_player_mpv.py b/tests/integration/test_media_player_mpv.py index d2f78ad7..e2096fe5 100644 --- a/tests/integration/test_media_player_mpv.py +++ b/tests/integration/test_media_player_mpv.py @@ -130,8 +130,10 @@ def test_play_idle(self): # post assertions self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, temp / IDLE_BG_NAME) - self.assertListEqual(mpv_player.player.sub_files, [temp / IDLE_TEXT_NAME]) + self.assertEqual(mpv_player.player.path, str(temp / IDLE_BG_NAME)) + self.assertListEqual( + mpv_player.player.sub_files, [str(temp / IDLE_TEXT_NAME)] + ) @func_set_timeout(TIMEOUT) def test_play_playlist_entry(self): @@ -175,14 +177,14 @@ def test_play_playlist_entry(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, temp / TRANSITION_BG_NAME) + self.assertEqual(mpv_player.player.path, str(temp / TRANSITION_BG_NAME)) # check there is no audio track self.assertFalse(mpv_player.player.audio) # check which subtitle file is read self.assertListEqual( - mpv_player.player.sub_files, [temp / TRANSITION_TEXT_NAME] + mpv_player.player.sub_files, [str(temp / TRANSITION_TEXT_NAME)] ) # assert the started transition callback has been called @@ -195,7 +197,7 @@ def test_play_playlist_entry(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song1_path) + self.assertEqual(mpv_player.player.path, str(self.song1_path)) # check audio track self.assertEqual(mpv_player.player.audio, 1) @@ -244,7 +246,7 @@ def test_play_playlist_entry_instrumental_track(self): # check media exists self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song1_path) + self.assertEqual(mpv_player.player.path, str(self.song1_path)) # check audio track self.assertEqual(mpv_player.player.audio, 2) @@ -278,7 +280,7 @@ def test_play_playlist_entry_instrumental_file(self): # check media exists self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song2_path) + self.assertEqual(mpv_player.player.path, str(self.song2_path)) # check audio track self.assertEqual(mpv_player.player.audio, 3) @@ -467,7 +469,7 @@ def test_skip_song(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song1_path) + self.assertEqual(mpv_player.player.path, str(self.song1_path)) # request first playlist entry to stop mpv_player.skip() @@ -492,7 +494,7 @@ def test_skip_song(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song2_path) + self.assertEqual(mpv_player.player.path, str(self.song2_path)) # check skip flag self.assertFalse(mpv_player.player_data["skip"]) @@ -521,7 +523,7 @@ def test_skip_last_song(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song1_path) + self.assertEqual(mpv_player.player.path, str(self.song1_path)) # request first playlist entry to stop mpv_player.skip() @@ -570,7 +572,7 @@ def test_skip_song_pause(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song1_path) + self.assertEqual(mpv_player.player.path, str(self.song1_path)) # pause song mpv_player.pause() @@ -601,7 +603,7 @@ def test_skip_song_pause(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song2_path) + self.assertEqual(mpv_player.player.path, str(self.song2_path)) # check skip flag self.assertFalse(mpv_player.player_data["skip"]) @@ -635,7 +637,7 @@ def test_skip_last_song_pause(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song1_path) + self.assertEqual(mpv_player.player.path, str(self.song1_path)) # pause song mpv_player.pause() @@ -696,7 +698,7 @@ def test_skip_transition(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song2_path) + self.assertEqual(mpv_player.player.path, str(self.song2_path)) @func_set_timeout(TIMEOUT) def test_skip_idle(self): @@ -731,7 +733,7 @@ def test_skip_idle(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song1_path) + self.assertEqual(mpv_player.player.path, str(self.song1_path)) @func_set_timeout(TIMEOUT) def test_rewind_song(self): @@ -920,7 +922,7 @@ def test_play_idle_after_playlist_paused(self): # check media self.assertIsNotNone(mpv_player.player.path) - self.assertEqual(mpv_player.player.path, self.song1_path) + self.assertEqual(mpv_player.player.path, str(self.song1_path)) # pause song mpv_player.pause() From a7c42d2234feea0052aabfee7801563f62522f30 Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 8 Feb 2025 18:41:34 +0100 Subject: [PATCH 19/25] Use old path syntax --- tests/unit/test_audio.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_audio.py b/tests/unit/test_audio.py index f757f530..a9016225 100644 --- a/tests/unit/test_audio.py +++ b/tests/unit/test_audio.py @@ -1,4 +1,4 @@ -from importlib.resources import as_file, files +from importlib.resources import path from pathlib import Path from unittest import TestCase from unittest.mock import patch @@ -9,17 +9,17 @@ class IsAudioFileTestCase(TestCase): def test_mp3(self): """Test to detect a MP3 file.""" - with as_file(files("tests.resources").joinpath("song2.mp3")) as file: + with path("tests.resources", "song2.mp3") as file: self.assertTrue(is_audio_file(file)) def test_ass(self): """Test to not detect an ASS file.""" - with as_file(files("tests.resources").joinpath("song2.ass")) as file: + with path("tests.resources", "song2.ass") as file: self.assertFalse(is_audio_file(file)) def test_mkv(self): """Test to not detect a MKV file.""" - with as_file(files("tests.resources").joinpath("song2.mkv")) as file: + with path("tests.resources", "song2.mkv") as file: self.assertFalse(is_audio_file(file)) From acfd006367e7bfd96bb3557b27fead8713f4667c Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 8 Feb 2025 22:10:26 +0100 Subject: [PATCH 20/25] Try to fix path errors --- tests/unit/test_media_player_vlc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_media_player_vlc.py b/tests/unit/test_media_player_vlc.py index c15bd871..14a54ba0 100644 --- a/tests/unit/test_media_player_vlc.py +++ b/tests/unit/test_media_player_vlc.py @@ -991,7 +991,7 @@ def test_handle_playing_song(self, mocked_is_playing_this): [ "DEBUG:dakara_player.media_player.vlc:Playing callback called", "INFO:dakara_player.media_player.vlc:Now playing 'Song title' " - "('{}')".format(Path(gettempdir()) / self.song_file_path), + "('{}')".format(Path(gettempdir()).resolve() / self.song_file_path), ], ) From 6b40e1d87c49c2ac5adaa89a25777143eea3e5f2 Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 8 Feb 2025 22:27:25 +0100 Subject: [PATCH 21/25] Fix DOS short paths problem --- tests/integration/base.py | 3 ++- tests/unit/test_media_player_mpv.py | 26 ++++++++++------------ tests/unit/test_media_player_vlc.py | 34 +++++++++++++++-------------- tests/utils.py | 17 +++++++++++++++ 4 files changed, 48 insertions(+), 32 deletions(-) create mode 100644 tests/utils.py diff --git a/tests/integration/base.py b/tests/integration/base.py index e5deb78d..b487890a 100644 --- a/tests/integration/base.py +++ b/tests/integration/base.py @@ -90,7 +90,8 @@ class TestCaseKara(TestCase): def setUp(self): # create kara folder self.kara_folder = TemporaryDirectory() - self.kara_folder_path = Path(self.kara_folder.name) + # resolve to prevent DOS short paths on Windows CI + self.kara_folder_path = Path(self.kara_folder.name).resolve() # create subtitle with path("tests.resources", "song1.ass") as file: diff --git a/tests/unit/test_media_player_mpv.py b/tests/unit/test_media_player_mpv.py index 6ea4cbe0..255cd4a5 100644 --- a/tests/unit/test_media_player_mpv.py +++ b/tests/unit/test_media_player_mpv.py @@ -1,7 +1,6 @@ from contextlib import ExitStack from pathlib import Path from queue import Queue -from tempfile import gettempdir from threading import Event from unittest import TestCase from unittest.mock import MagicMock, patch @@ -20,6 +19,7 @@ MediaPlayerMpvPost0340, MpvTooOldError, ) +from tests.utils import get_temp_dir class MediaPlayerMpvTestCase(TestCase): @@ -211,7 +211,7 @@ def get_instance( unittest.mock.MagicMock: BackgroundLoader class. unittest.mock.MagicMock: TextGenerator class. """ - config = config or {"kara_folder": gettempdir()} + config = config or {"kara_folder": get_temp_dir()} with ExitStack() as stack: mocked_instance_class = stack.enter_context( @@ -251,7 +251,7 @@ def set_playlist_entry(self, mpv_player, started=True): # create mocked transition mpv_player.playlist_entry_data["transition"].path = ( - Path(gettempdir()) / "transition.png" + get_temp_dir() / "transition.png" ) # create mocked song @@ -413,9 +413,7 @@ def test_handle_end_file_transition(self, mocked_play, mocked_clear_playlist_ent mpv_player, (mocked_player, _, _), _ = self.get_instance() mpv_player.set_callback("finished", MagicMock()) self.set_playlist_entry(mpv_player) - mocked_player.playlist[0]["filename"] = str( - Path(gettempdir()) / "transition.png" - ) + mocked_player.playlist[0]["filename"] = str(get_temp_dir() / "transition.png") # call the method with self.assertLogs("dakara_player.media_player.mpv", "DEBUG") as logger: @@ -427,7 +425,7 @@ def test_handle_end_file_transition(self, mocked_play, mocked_clear_playlist_ent [ "DEBUG:dakara_player.media_player.mpv:File end callback called", "DEBUG:dakara_player.media_player.mpv:Will play '{}'".format( - Path(gettempdir()) / self.song_file_path + get_temp_dir() / self.song_file_path ), ], ) @@ -492,7 +490,7 @@ def test_handle_end_file_other(self, mocked_play, mocked_clear_playlist_entry): mpv_player, (mocked_player, _, _), _ = self.get_instance() mpv_player.set_callback("finished", MagicMock()) self.set_playlist_entry(mpv_player) - mocked_player.playlist[0]["filename"] = str(Path(gettempdir()) / "other") + mocked_player.playlist[0]["filename"] = str(get_temp_dir() / "other") self.assertFalse(mpv_player.stop.is_set()) @@ -532,7 +530,7 @@ def test_handle_log_message(self, mocked_skip): [ "DEBUG:dakara_player.media_player.mpv:Log message callback called", "ERROR:dakara_player.media_player.mpv:Unable to play '{}'".format( - Path(gettempdir()) / self.song_file_path + get_temp_dir() / self.song_file_path ), ], ) @@ -600,7 +598,7 @@ def test_handle_start_file_song(self, mocked_is_playing_this): [ "DEBUG:dakara_player.media_player.mpv:Start file callback called", "INFO:dakara_player.media_player.mpv:Now playing 'Song title' " - "('{}')".format(Path(gettempdir()) / self.song_file_path), + "('{}')".format(get_temp_dir() / self.song_file_path), ], ) @@ -770,9 +768,7 @@ def test_handle_end_file_transition(self, mocked_play, mocked_clear_playlist_ent mpv_player, (mocked_player, _, _), _ = self.get_instance() mpv_player.set_callback("finished", MagicMock()) self.set_playlist_entry(mpv_player) - mocked_player.playlist[0]["filename"] = str( - Path(gettempdir()) / "transition.png" - ) + mocked_player.playlist[0]["filename"] = str(get_temp_dir() / "transition.png") # call the method with self.assertLogs("dakara_player.media_player.mpv", "DEBUG") as logger: @@ -786,7 +782,7 @@ def test_handle_end_file_transition(self, mocked_play, mocked_clear_playlist_ent [ "DEBUG:dakara_player.media_player.mpv:File end callback called", "DEBUG:dakara_player.media_player.mpv:Will play '{}'".format( - Path(gettempdir()) / self.song_file_path + get_temp_dir() / self.song_file_path ), ], ) @@ -855,7 +851,7 @@ def test_handle_end_file_other(self, mocked_play, mocked_clear_playlist_entry): mpv_player, (mocked_player, _, _), _ = self.get_instance() mpv_player.set_callback("finished", MagicMock()) self.set_playlist_entry(mpv_player) - mocked_player.playlist[0]["filename"] = str(Path(gettempdir()) / "other") + mocked_player.playlist[0]["filename"] = str(get_temp_dir() / "other") self.assertFalse(mpv_player.stop.is_set()) diff --git a/tests/unit/test_media_player_vlc.py b/tests/unit/test_media_player_vlc.py index 14a54ba0..330bcee7 100644 --- a/tests/unit/test_media_player_vlc.py +++ b/tests/unit/test_media_player_vlc.py @@ -2,7 +2,6 @@ from contextlib import ExitStack, contextmanager from pathlib import Path from queue import Queue -from tempfile import gettempdir from threading import Event from time import sleep from unittest import TestCase, skipIf @@ -32,6 +31,7 @@ from dakara_player.mrl import path_to_mrl from dakara_player.text import TextGenerator from dakara_player.window import DummyWindowManager, WindowManager +from tests.utils import get_temp_dir class BaseTestCase(TestCase): @@ -76,7 +76,7 @@ def get_instance( unittest.mock.MagicMock: BackgroundLoader class. unittest.mock.MagicMock: TextGenerator class. """ - config = config or {"kara_folder": gettempdir()} + config = config or {"kara_folder": str(get_temp_dir())} with ExitStack() as stack: if vlc is None: @@ -152,7 +152,7 @@ def test_init_window(self): """Test to use default or custom window.""" # default window with self.get_instance( - {"kara_folder": gettempdir(), "vlc": {"use_default_window": True}} + {"kara_folder": str(get_temp_dir()), "vlc": {"use_default_window": True}} ) as (vlc_player, _, _): self.assertIsInstance(vlc_player.window, DummyWindowManager) @@ -342,7 +342,9 @@ def test_check_kara_folder_path_does_not_exist(self, mocked_exists): # call the method with self.assertRaisesRegex( KaraFolderNotFound, - 'Karaoke folder "{}" does not exist'.format(re.escape(gettempdir())), + 'Karaoke folder "{}" does not exist'.format( + re.escape(str(get_temp_dir())) + ), ): vlc_player.check_kara_folder_path() @@ -421,7 +423,7 @@ def test_set_playlist_entry_error_file(self, mocked_is_file): logger.output, [ "ERROR:dakara_player.media_player.base:File not found '{}'".format( - Path(gettempdir()) / self.song_file_path + get_temp_dir() / self.song_file_path ) ], ) @@ -446,7 +448,7 @@ def test_set_playlist_entry( # setup mocks mocked_is_file.return_value = True mocked_background_loader.backgrounds = { - "transition": Path(gettempdir()) / "transition.png" + "transition": get_temp_dir() / "transition.png" } # mock the callbacks @@ -484,8 +486,8 @@ def test_manage_instrumental_file( ): """Test to add instrumental file.""" with self.get_instance() as (vlc_player, (mocked_instance, _, _), _): - video_path = Path(gettempdir()) / "video" - audio_path = Path(gettempdir()) / "audio" + video_path = get_temp_dir() / "video" + audio_path = get_temp_dir() / "audio" # pre assertions self.assertIsNone(vlc_player.playlist_entry_data["song"].audio_track_id) @@ -528,8 +530,8 @@ def test_manage_instrumental_file_error_slaves_add( ): """Test to be unable to add instrumental file.""" with self.get_instance() as (vlc_player, (mocked_instance, _, _), _): - video_path = Path(gettempdir()) / "video" - audio_path = Path(gettempdir()) / "audio" + video_path = get_temp_dir() / "video" + audio_path = get_temp_dir() / "audio" # pre assertions self.assertIsNone(vlc_player.playlist_entry_data["song"].audio_track_id) @@ -584,7 +586,7 @@ def test_manage_instrumental_track( ), _, ): - video_path = Path(gettempdir()) / "video" + video_path = get_temp_dir() / "video" # pre assertions self.assertIsNone(vlc_player.playlist_entry_data["song"].audio_track_id) @@ -625,7 +627,7 @@ def test_manage_instrumental_no_instrumental_found( ): """Test to cannot find instrumental.""" with self.get_instance() as (vlc_player, (mocked_instance, _, _), _): - video_path = Path(gettempdir()) / "video" + video_path = get_temp_dir() / "video" # pre assertions self.assertIsNone(vlc_player.playlist_entry_data["song"].audio_track_id) @@ -755,7 +757,7 @@ def test_handle_end_reached_transition( [ "DEBUG:dakara_player.media_player.vlc:End reached callback called", "DEBUG:dakara_player.media_player.vlc:Will play '{}'".format( - Path(gettempdir()) / self.song_file_path + get_temp_dir() / self.song_file_path ), ], ) @@ -889,7 +891,7 @@ def test_handle_encountered_error(self, mocked_is_playing_this, mocked_skip): [ "DEBUG:dakara_player.media_player.vlc:Error callback called", "ERROR:dakara_player.media_player.vlc:Unable to play '{}'".format( - Path(gettempdir()) / self.song_file_path + get_temp_dir() / self.song_file_path ), ], ) @@ -991,7 +993,7 @@ def test_handle_playing_song(self, mocked_is_playing_this): [ "DEBUG:dakara_player.media_player.vlc:Playing callback called", "INFO:dakara_player.media_player.vlc:Now playing 'Song title' " - "('{}')".format(Path(gettempdir()).resolve() / self.song_file_path), + "('{}')".format(get_temp_dir() / self.song_file_path), ], ) @@ -1027,7 +1029,7 @@ def test_handle_playing_media_starts_track_id(self, mocked_is_playing_this): "DEBUG:dakara_player.media_player.vlc:Requesting to play audio " "track 99", "INFO:dakara_player.media_player.vlc:Now playing 'Song title' " - "('{}')".format(Path(gettempdir()) / self.song_file_path), + "('{}')".format(get_temp_dir() / self.song_file_path), ], ) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 00000000..bbb38ffb --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,17 @@ +from pathlib import Path +from tempfile import gettempdir + + +def get_temp_dir() -> Path: + """Return the default temporary directory. + + This function fixes the problem on Windows CI where `tempfile.gettempdir` + would return a DOS short path, not a Windows long path. + + See: + https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#short-vs-long-names + + Returns: + pathlib.Path: Long path to the default temporary directory. + """ + return Path(gettempdir()).resolve() From f82de10aa9aabd8b2a927bf337a36ae301ace2c8 Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 8 Feb 2025 23:00:04 +0100 Subject: [PATCH 22/25] Fix DOS short paths problems for integration tests --- tests/integration/test_media_player_mpv.py | 2 +- tests/integration/test_media_player_vlc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_media_player_mpv.py b/tests/integration/test_media_player_mpv.py index e2096fe5..6204d80b 100644 --- a/tests/integration/test_media_player_mpv.py +++ b/tests/integration/test_media_player_mpv.py @@ -68,7 +68,7 @@ def get_instance(self, config=None, check_error=True): config_full.update(config) with TemporaryDirectory() as temp_str: - temp = Path(temp_str) + temp = Path(temp_str).resolve() try: with ExitStack() as stack: mpv_player = stack.enter_context( diff --git a/tests/integration/test_media_player_vlc.py b/tests/integration/test_media_player_vlc.py index 9716d674..5236171f 100644 --- a/tests/integration/test_media_player_vlc.py +++ b/tests/integration/test_media_player_vlc.py @@ -86,7 +86,7 @@ def get_instance(self, config=None, check_error=True): config_full.update(config) with ExitStack() as stack: - temp = Path(stack.enter_context(TemporaryDirectory())) + temp = Path(stack.enter_context(TemporaryDirectory())).resolve() vlc_player = stack.enter_context( MediaPlayerVlc( Event(), From 78ad00e01936aa51a1a3ac2dab204d0cf0002d43 Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 8 Feb 2025 23:38:19 +0100 Subject: [PATCH 23/25] Refactor path manipulation in is_playing_this for mpv --- src/dakara_player/media_player/mpv.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/dakara_player/media_player/mpv.py b/src/dakara_player/media_player/mpv.py index edd2be17..ce47715c 100644 --- a/src/dakara_player/media_player/mpv.py +++ b/src/dakara_player/media_player/mpv.py @@ -344,15 +344,17 @@ def is_playing_this(self, what): assert len(playlist) == 1, "Too many entries in mpv internal playlist" - media = playlist[0] - media_path = Path(media.get("filename") or "") + media_raw = playlist[0].get("filename") + + if media_raw is None: + return False + + media_path = Path(media_raw) if what == "idle": return media_path == self.background_loader.backgrounds["idle"] - return (media_path == self.playlist_entry_data[what].path) and ( - media_path != Path("") - ) + return media_path == self.playlist_entry_data[what].path def play(self, what): """Request mpv to play something. @@ -843,25 +845,28 @@ def was_playing_this(self, what, id): return self.is_playing_this(what, entries[0]["filename"]) - def is_playing_this(self, what, media_path=None): + def is_playing_this(self, what, current_media_path=None): """Query if mpv is playing the requested media type. Args: what (str): Tell if mpv current track is of the requested type, but not if it is actually playing it (it can be in pause). - media_path (pathlib.Path): Optional media path. + current_media_path (pathlib.Path): Optional current media path. Returns: bool: `True` if mpv is playing the requested type. """ - media_path = Path(media_path or self.player.path or "") + media_raw = current_media_path or self.player.path + + if media_raw is None: + return False + + media_path = Path(media_raw) if what == "idle": return media_path == self.background_loader.backgrounds["idle"] - return (media_path == self.playlist_entry_data[what].path) and ( - media_path != Path("") - ) + return media_path == self.playlist_entry_data[what].path @safe def handle_end_file(self, event): From ff106576594fc36240fa011d13ad600a5bf6f7e2 Mon Sep 17 00:00:00 2001 From: Neraste Date: Sat, 15 Feb 2025 21:49:32 +0100 Subject: [PATCH 24/25] Add commend to remove custom URI parsing For Python 3.13. --- src/dakara_player/mrl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dakara_player/mrl.py b/src/dakara_player/mrl.py index 109d97c9..b0968034 100644 --- a/src/dakara_player/mrl.py +++ b/src/dakara_player/mrl.py @@ -16,6 +16,7 @@ def mrl_to_path(file_mrl): Returns: pathlib.Path: Path to the resource. """ + # TODO Replace by pathlib.Path.from_uri() available in Python 3.13 path_string = unquote(urlparse(file_mrl).path) # remove first '/' if a colon character is found like in '/C:/a/b' From de912a1b9b64feb65053adcdc29da74cda4d394f Mon Sep 17 00:00:00 2001 From: Neraste Date: Sun, 16 Feb 2025 00:48:15 +0100 Subject: [PATCH 25/25] [skip ci] Fix error message --- tests/integration/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/base.py b/tests/integration/base.py index b487890a..3bfea051 100644 --- a/tests/integration/base.py +++ b/tests/integration/base.py @@ -72,7 +72,7 @@ def wait(cls, player, condition_method): raise error except Empty as error_empty: - raise RuntimeError("Unknown error happened") from error_empty + raise RuntimeError("Unexpected error happened") from error_empty try: if condition_method():