|
8 | 8 | import os
|
9 | 9 | import os.path
|
10 | 10 | import time
|
| 11 | +import urllib.parse |
11 | 12 | from collections.abc import AsyncGenerator, Iterator, Sequence
|
| 13 | +from pathlib import Path |
12 | 14 | from typing import TYPE_CHECKING, Any, cast
|
13 | 15 |
|
14 | 16 | import aiofiles
|
@@ -673,6 +675,8 @@ async def get_playlist_tracks(self, prov_playlist_id: str, page: int = 0) -> lis
|
673 | 675 | playlist_lines = parse_pls(playlist_data)
|
674 | 676 |
|
675 | 677 | for idx, playlist_line in enumerate(playlist_lines, 1):
|
| 678 | + if "#EXT" in playlist_line.path: |
| 679 | + continue |
676 | 680 | if track := await self._parse_playlist_line(
|
677 | 681 | playlist_line.path, os.path.dirname(prov_playlist_id)
|
678 | 682 | ):
|
@@ -718,24 +722,30 @@ async def _process_podcast_episode(item: FileSystemItem) -> None:
|
718 | 722 | async def _parse_playlist_line(self, line: str, playlist_path: str) -> Track | None:
|
719 | 723 | """Try to parse a track from a playlist line."""
|
720 | 724 | try:
|
721 |
| - # if a relative path was given in an upper level from the playlist, |
722 |
| - # try to resolve it |
723 |
| - for parentpart in ("../", "..\\"): |
724 |
| - while line.startswith(parentpart): |
725 |
| - if len(playlist_path) < 3: |
726 |
| - break # guard |
727 |
| - playlist_path = parentpart[:-3] |
728 |
| - line = line[3:] |
729 |
| - |
730 |
| - # try to resolve the filename |
731 |
| - for filename in (line, os.path.join(playlist_path, line)): |
732 |
| - with contextlib.suppress(FileNotFoundError): |
733 |
| - file_item = await self.resolve(filename) |
734 |
| - tags = await async_parse_tags(file_item.absolute_path, file_item.file_size) |
735 |
| - return await self._parse_track(file_item, tags) |
| 725 | + line = line.replace("file://", "").strip() |
| 726 | + # try to resolve the filename (both normal and url decoded): |
| 727 | + # - as an absolute path |
| 728 | + # - relative to the playlist path |
| 729 | + # - relative to our base path |
| 730 | + # - relative to the playlist path with a leading slash |
| 731 | + for _line in (line, urllib.parse.unquote(line)): |
| 732 | + for filename in ( |
| 733 | + # try to resolve the line as an absolute path |
| 734 | + _line, |
| 735 | + # try to resolve the line as a relative path to the playlist |
| 736 | + os.path.join(playlist_path, _line.removeprefix("/")), |
| 737 | + # try to resolve the line by resolving it against the absolute playlist path |
| 738 | + (Path(self.get_absolute_path(playlist_path)) / _line).resolve().as_posix(), |
| 739 | + ): |
| 740 | + with contextlib.suppress(FileNotFoundError): |
| 741 | + file_item = await self.resolve(filename) |
| 742 | + tags = await async_parse_tags(file_item.absolute_path, file_item.file_size) |
| 743 | + return await self._parse_track(file_item, tags) |
| 744 | + # all attempts failed |
| 745 | + raise MediaNotFoundError("Invalid path/uri") |
736 | 746 |
|
737 | 747 | except MusicAssistantError as err:
|
738 |
| - self.logger.warning("Could not parse uri/file %s to track: %s", line, str(err)) |
| 748 | + self.logger.warning("Could not parse %s to track: %s", line, str(err)) |
739 | 749 |
|
740 | 750 | return None
|
741 | 751 |
|
|
0 commit comments