From 2c12e4f6bf9c384ad748680a4c77953f52900968 Mon Sep 17 00:00:00 2001 From: Draper <27962761+Drapersniper@users.noreply.github.com> Date: Fri, 17 Jan 2020 22:07:49 +0000 Subject: [PATCH] [Audio] Show symbolic link folders (#3376) * Fixes Bump play * Fixed #3332 * Revert "Fixed #3332" This reverts commit d76d3acb * Revert "Fixes Bump play" This reverts commit 3839bdaf * *sigh* * *sigh* * *sigh* * use iglob + async iterator Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * black Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * + fixes Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> --- redbot/cogs/audio/audio.py | 14 ++- redbot/cogs/audio/audio_dataclasses.py | 154 +++++++++++++++---------- 2 files changed, 105 insertions(+), 63 deletions(-) diff --git a/redbot/cogs/audio/audio.py b/redbot/cogs/audio/audio.py index 259d60e62..99714daf6 100644 --- a/redbot/cogs/audio/audio.py +++ b/redbot/cogs/audio/audio.py @@ -2452,7 +2452,11 @@ class Audio(commands.Cog): if not await self._localtracks_check(ctx): return - return audio_data.subfolders_in_tree() if search_subfolders else audio_data.subfolders() + return ( + await audio_data.subfolders_in_tree() + if search_subfolders + else await audio_data.subfolders() + ) async def _folder_list( self, ctx: commands.Context, query: audio_dataclasses.Query @@ -2463,9 +2467,9 @@ class Audio(commands.Cog): if not query.track.exists(): return return ( - query.track.tracks_in_tree() + await query.track.tracks_in_tree() if query.search_subfolders - else query.track.tracks_in_folder() + else await query.track.tracks_in_folder() ) async def _folder_tracks( @@ -2504,9 +2508,9 @@ class Audio(commands.Cog): return return ( - query.track.tracks_in_tree() + await query.track.tracks_in_tree() if query.search_subfolders - else query.track.tracks_in_folder() + else await query.track.tracks_in_folder() ) async def _localtracks_check(self, ctx: commands.Context) -> bool: diff --git a/redbot/cogs/audio/audio_dataclasses.py b/redbot/cogs/audio/audio_dataclasses.py index 32eb3ef2b..21c79e14f 100644 --- a/redbot/cogs/audio/audio_dataclasses.py +++ b/redbot/cogs/audio/audio_dataclasses.py @@ -1,9 +1,12 @@ +import asyncio +import contextlib +import glob import ntpath import os import posixpath import re from pathlib import Path, PosixPath, WindowsPath -from typing import List, Optional, Union, MutableMapping +from typing import List, Optional, Union, MutableMapping, Iterator, AsyncIterator from urllib.parse import urlparse import lavalink @@ -167,29 +170,48 @@ class LocalPath: modified.path = modified.path.joinpath(*args) return modified - def multiglob(self, *patterns): - paths = [] + def rglob(self, pattern, folder=False) -> Iterator[str]: + if folder: + return glob.iglob(f"{self.path}{os.sep}**{os.sep}", recursive=True) + else: + return glob.iglob(f"{self.path}{os.sep}**{os.sep}{pattern}", recursive=True) + + def glob(self, pattern, folder=False) -> Iterator[str]: + if folder: + return glob.iglob(f"{self.path}{os.sep}*{os.sep}", recursive=False) + else: + return glob.iglob(f"{self.path}{os.sep}*{pattern}", recursive=False) + + async def multiglob(self, *patterns, folder=False) -> AsyncIterator["LocalPath"]: for p in patterns: - paths.extend(list(self.path.glob(p))) - for p in self._filtered(paths): - yield p + for rp in self.glob(p): + rp = LocalPath(rp) + if folder and rp.is_dir() and rp.exists(): + yield rp + await asyncio.sleep(0) + else: + if rp.suffix in self._all_music_ext and rp.is_file() and rp.exists(): + yield rp + await asyncio.sleep(0) - def multirglob(self, *patterns): - paths = [] + async def multirglob(self, *patterns, folder=False) -> AsyncIterator["LocalPath"]: for p in patterns: - paths.extend(list(self.path.rglob(p))) - - for p in self._filtered(paths): - yield p - - def _filtered(self, paths: List[Path]): - for p in paths: - if p.suffix in self._all_music_ext: - yield p + for rp in self.rglob(p): + rp = LocalPath(rp) + if folder and rp.is_dir() and rp.exists(): + yield rp + await asyncio.sleep(0) + else: + if rp.suffix in self._all_music_ext and rp.is_file() and rp.exists(): + yield rp + await asyncio.sleep(0) def __str__(self): return self.to_string() + def __repr__(self): + return str(self) + def to_string(self): try: return str(self.path.absolute()) @@ -209,48 +231,56 @@ class LocalPath: string = f"...{os.sep}{string}" return string - def tracks_in_tree(self): + async def tracks_in_tree(self): tracks = [] - for track in self.multirglob(*[f"{ext}" for ext in self._all_music_ext]): - if track.exists() and track.is_file() and track.parent != self.localtrack_folder: - tracks.append(Query.process_input(LocalPath(str(track.absolute())))) + async for track in self.multirglob(*[f"{ext}" for ext in self._all_music_ext]): + with contextlib.suppress(ValueError): + if track.path.parent != self.localtrack_folder and track.path.relative_to( + self.path + ): + tracks.append(Query.process_input(track)) return sorted(tracks, key=lambda x: x.to_string_user().lower()) - def subfolders_in_tree(self): - files = list(self.multirglob(*[f"*{ext}" for ext in self._all_music_ext])) - folders = [] - for f in files: - if f.exists() and f.parent not in folders and f.parent != self.localtrack_folder: - folders.append(f.parent) + async def subfolders_in_tree(self): return_folders = [] - for folder in folders: - if folder.exists() and folder.is_dir(): - return_folders.append(LocalPath(str(folder.absolute()))) + async for f in self.multirglob("", folder=True): + with contextlib.suppress(ValueError): + if ( + f not in return_folders + and f.path != self.localtrack_folder + and f.path.relative_to(self.path) + ): + return_folders.append(f) return sorted(return_folders, key=lambda x: x.to_string_user().lower()) - def tracks_in_folder(self): + async def tracks_in_folder(self): tracks = [] - for track in self.multiglob(*[f"*{ext}" for ext in self._all_music_ext]): - if track.exists() and track.is_file() and track.parent != self.localtrack_folder: - tracks.append(Query.process_input(LocalPath(str(track.absolute())))) + async for track in self.multiglob(*[f"{ext}" for ext in self._all_music_ext]): + with contextlib.suppress(ValueError): + if track.path.parent != self.localtrack_folder and track.path.relative_to( + self.path + ): + tracks.append(Query.process_input(track)) return sorted(tracks, key=lambda x: x.to_string_user().lower()) - def subfolders(self): - files = list(self.multiglob(*[f"*{ext}" for ext in self._all_music_ext])) - folders = [] - for f in files: - if f.exists() and f.parent not in folders and f.parent != self.localtrack_folder: - folders.append(f.parent) + async def subfolders(self): return_folders = [] - for folder in folders: - if folder.exists() and folder.is_dir(): - return_folders.append(LocalPath(str(folder.absolute()))) + async for f in self.multiglob("", folder=True): + with contextlib.suppress(ValueError): + if ( + f not in return_folders + and f.path != self.localtrack_folder + and f.path.relative_to(self.path) + ): + return_folders.append(f) return sorted(return_folders, key=lambda x: x.to_string_user().lower()) def __eq__(self, other): - if not isinstance(other, LocalPath): - return NotImplemented - return self.path._cparts == other.path._cparts + if isinstance(other, LocalPath): + return self.path._cparts == other.path._cparts + elif isinstance(other, Path): + return self.path._cparts == other._cpart + return NotImplemented def __hash__(self): try: @@ -260,24 +290,32 @@ class LocalPath: return self._hash def __lt__(self, other): - if not isinstance(other, LocalPath): - return NotImplemented - return self.path._cparts < other.path._cparts + if isinstance(other, LocalPath): + return self.path._cparts < other.path._cparts + elif isinstance(other, Path): + return self.path._cparts < other._cpart + return NotImplemented def __le__(self, other): - if not isinstance(other, LocalPath): - return NotImplemented - return self.path._cparts <= other.path._cparts + if isinstance(other, LocalPath): + return self.path._cparts <= other.path._cparts + elif isinstance(other, Path): + return self.path._cparts <= other._cpart + return NotImplemented def __gt__(self, other): - if not isinstance(other, LocalPath): - return NotImplemented - return self.path._cparts > other.path._cparts + if isinstance(other, LocalPath): + return self.path._cparts > other.path._cparts + elif isinstance(other, Path): + return self.path._cparts > other._cpart + return NotImplemented def __ge__(self, other): - if not isinstance(other, LocalPath): - return NotImplemented - return self.path._cparts >= other.path._cparts + if isinstance(other, LocalPath): + return self.path._cparts >= other.path._cparts + elif isinstance(other, Path): + return self.path._cparts >= other._cpart + return NotImplemented class Query: