[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>
This commit is contained in:
Draper 2020-01-17 22:07:49 +00:00 committed by Michael H
parent b88bd5d44d
commit 2c12e4f6bf
2 changed files with 105 additions and 63 deletions

View File

@ -2452,7 +2452,11 @@ class Audio(commands.Cog):
if not await self._localtracks_check(ctx): if not await self._localtracks_check(ctx):
return 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( async def _folder_list(
self, ctx: commands.Context, query: audio_dataclasses.Query self, ctx: commands.Context, query: audio_dataclasses.Query
@ -2463,9 +2467,9 @@ class Audio(commands.Cog):
if not query.track.exists(): if not query.track.exists():
return return
return ( return (
query.track.tracks_in_tree() await query.track.tracks_in_tree()
if query.search_subfolders if query.search_subfolders
else query.track.tracks_in_folder() else await query.track.tracks_in_folder()
) )
async def _folder_tracks( async def _folder_tracks(
@ -2504,9 +2508,9 @@ class Audio(commands.Cog):
return return
return ( return (
query.track.tracks_in_tree() await query.track.tracks_in_tree()
if query.search_subfolders 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: async def _localtracks_check(self, ctx: commands.Context) -> bool:

View File

@ -1,9 +1,12 @@
import asyncio
import contextlib
import glob
import ntpath import ntpath
import os import os
import posixpath import posixpath
import re import re
from pathlib import Path, PosixPath, WindowsPath 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 from urllib.parse import urlparse
import lavalink import lavalink
@ -167,29 +170,48 @@ class LocalPath:
modified.path = modified.path.joinpath(*args) modified.path = modified.path.joinpath(*args)
return modified return modified
def multiglob(self, *patterns): def rglob(self, pattern, folder=False) -> Iterator[str]:
paths = [] 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: for p in patterns:
paths.extend(list(self.path.glob(p))) for rp in self.glob(p):
for p in self._filtered(paths): rp = LocalPath(rp)
yield p 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): async def multirglob(self, *patterns, folder=False) -> AsyncIterator["LocalPath"]:
paths = []
for p in patterns: for p in patterns:
paths.extend(list(self.path.rglob(p))) for rp in self.rglob(p):
rp = LocalPath(rp)
for p in self._filtered(paths): if folder and rp.is_dir() and rp.exists():
yield p yield rp
await asyncio.sleep(0)
def _filtered(self, paths: List[Path]): else:
for p in paths: if rp.suffix in self._all_music_ext and rp.is_file() and rp.exists():
if p.suffix in self._all_music_ext: yield rp
yield p await asyncio.sleep(0)
def __str__(self): def __str__(self):
return self.to_string() return self.to_string()
def __repr__(self):
return str(self)
def to_string(self): def to_string(self):
try: try:
return str(self.path.absolute()) return str(self.path.absolute())
@ -209,48 +231,56 @@ class LocalPath:
string = f"...{os.sep}{string}" string = f"...{os.sep}{string}"
return string return string
def tracks_in_tree(self): async def tracks_in_tree(self):
tracks = [] tracks = []
for track in self.multirglob(*[f"{ext}" for ext in self._all_music_ext]): async 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: with contextlib.suppress(ValueError):
tracks.append(Query.process_input(LocalPath(str(track.absolute())))) 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()) return sorted(tracks, key=lambda x: x.to_string_user().lower())
def subfolders_in_tree(self): async 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)
return_folders = [] return_folders = []
for folder in folders: async for f in self.multirglob("", folder=True):
if folder.exists() and folder.is_dir(): with contextlib.suppress(ValueError):
return_folders.append(LocalPath(str(folder.absolute()))) 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()) return sorted(return_folders, key=lambda x: x.to_string_user().lower())
def tracks_in_folder(self): async def tracks_in_folder(self):
tracks = [] tracks = []
for track in self.multiglob(*[f"*{ext}" for ext in self._all_music_ext]): async 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: with contextlib.suppress(ValueError):
tracks.append(Query.process_input(LocalPath(str(track.absolute())))) 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()) return sorted(tracks, key=lambda x: x.to_string_user().lower())
def subfolders(self): async 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)
return_folders = [] return_folders = []
for folder in folders: async for f in self.multiglob("", folder=True):
if folder.exists() and folder.is_dir(): with contextlib.suppress(ValueError):
return_folders.append(LocalPath(str(folder.absolute()))) 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()) return sorted(return_folders, key=lambda x: x.to_string_user().lower())
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, LocalPath): if isinstance(other, LocalPath):
return NotImplemented return self.path._cparts == other.path._cparts
return self.path._cparts == other.path._cparts elif isinstance(other, Path):
return self.path._cparts == other._cpart
return NotImplemented
def __hash__(self): def __hash__(self):
try: try:
@ -260,24 +290,32 @@ class LocalPath:
return self._hash return self._hash
def __lt__(self, other): def __lt__(self, other):
if not isinstance(other, LocalPath): if isinstance(other, LocalPath):
return NotImplemented return self.path._cparts < other.path._cparts
return self.path._cparts < other.path._cparts elif isinstance(other, Path):
return self.path._cparts < other._cpart
return NotImplemented
def __le__(self, other): def __le__(self, other):
if not isinstance(other, LocalPath): if isinstance(other, LocalPath):
return NotImplemented return self.path._cparts <= other.path._cparts
return self.path._cparts <= other.path._cparts elif isinstance(other, Path):
return self.path._cparts <= other._cpart
return NotImplemented
def __gt__(self, other): def __gt__(self, other):
if not isinstance(other, LocalPath): if isinstance(other, LocalPath):
return NotImplemented return self.path._cparts > other.path._cparts
return self.path._cparts > other.path._cparts elif isinstance(other, Path):
return self.path._cparts > other._cpart
return NotImplemented
def __ge__(self, other): def __ge__(self, other):
if not isinstance(other, LocalPath): if isinstance(other, LocalPath):
return NotImplemented return self.path._cparts >= other.path._cparts
return self.path._cparts >= other.path._cparts elif isinstance(other, Path):
return self.path._cparts >= other._cpart
return NotImplemented
class Query: class Query: