Add YAPF style + ignore, and format tracked Python files
This commit is contained in:
@@ -15,7 +15,6 @@ from ProviderCore.base import Provider, SearchResult
|
||||
from SYS.logger import log, debug
|
||||
from models import ProgressBar
|
||||
|
||||
|
||||
_SOULSEEK_NOISE_SUBSTRINGS = (
|
||||
"search reply ticket does not match any search request",
|
||||
"failed to receive transfer ticket on file connection",
|
||||
@@ -82,11 +81,11 @@ def _configure_aioslsk_logging() -> None:
|
||||
aioslsk to ERROR and stop propagation so it doesn't spam the CLI.
|
||||
"""
|
||||
for name in (
|
||||
"aioslsk",
|
||||
"aioslsk.network",
|
||||
"aioslsk.search",
|
||||
"aioslsk.transfer",
|
||||
"aioslsk.transfer.manager",
|
||||
"aioslsk",
|
||||
"aioslsk.network",
|
||||
"aioslsk.search",
|
||||
"aioslsk.transfer",
|
||||
"aioslsk.transfer.manager",
|
||||
):
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.ERROR)
|
||||
@@ -237,7 +236,7 @@ class Soulseek(Provider):
|
||||
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
||||
super().__init__(config)
|
||||
try:
|
||||
from config import get_soulseek_username, get_soulseek_password
|
||||
from SYS.config import get_soulseek_username, get_soulseek_password
|
||||
|
||||
user = get_soulseek_username(self.config)
|
||||
pwd = get_soulseek_password(self.config)
|
||||
@@ -257,7 +256,10 @@ class Soulseek(Provider):
|
||||
filename = full_metadata.get("filename") or result.path
|
||||
|
||||
if not username or not filename:
|
||||
log(f"[soulseek] Missing metadata for download: {result.title}", file=sys.stderr)
|
||||
log(
|
||||
f"[soulseek] Missing metadata for download: {result.title}",
|
||||
file=sys.stderr
|
||||
)
|
||||
return None
|
||||
|
||||
# This cmdlet stack is synchronous; use asyncio.run for clarity.
|
||||
@@ -294,9 +296,11 @@ class Soulseek(Provider):
|
||||
log(f"[soulseek] Download error: {exc}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
async def perform_search(
|
||||
self, query: str, timeout: float = 9.0, limit: int = 50
|
||||
) -> List[Dict[str, Any]]:
|
||||
async def perform_search(self,
|
||||
query: str,
|
||||
timeout: float = 9.0,
|
||||
limit: int = 50) -> List[Dict[str,
|
||||
Any]]:
|
||||
"""Perform async Soulseek search."""
|
||||
|
||||
from aioslsk.client import SoulSeekClient
|
||||
@@ -305,7 +309,10 @@ class Soulseek(Provider):
|
||||
os.makedirs(self.DOWNLOAD_DIR, exist_ok=True)
|
||||
|
||||
settings = Settings(
|
||||
credentials=CredentialsSettings(username=self.USERNAME, password=self.PASSWORD)
|
||||
credentials=CredentialsSettings(
|
||||
username=self.USERNAME,
|
||||
password=self.PASSWORD
|
||||
)
|
||||
)
|
||||
client = SoulSeekClient(settings)
|
||||
|
||||
@@ -315,7 +322,10 @@ class Soulseek(Provider):
|
||||
await client.start()
|
||||
await client.login()
|
||||
except Exception as exc:
|
||||
log(f"[soulseek] Login failed: {type(exc).__name__}: {exc}", file=sys.stderr)
|
||||
log(
|
||||
f"[soulseek] Login failed: {type(exc).__name__}: {exc}",
|
||||
file=sys.stderr
|
||||
)
|
||||
return []
|
||||
|
||||
try:
|
||||
@@ -323,7 +333,10 @@ class Soulseek(Provider):
|
||||
await self._collect_results(search_request, timeout=timeout)
|
||||
return self._flatten_results(search_request)[:limit]
|
||||
except Exception as exc:
|
||||
log(f"[soulseek] Search error: {type(exc).__name__}: {exc}", file=sys.stderr)
|
||||
log(
|
||||
f"[soulseek] Search error: {type(exc).__name__}: {exc}",
|
||||
file=sys.stderr
|
||||
)
|
||||
return []
|
||||
finally:
|
||||
# Best-effort: try to cancel/close the search request before stopping
|
||||
@@ -356,8 +369,12 @@ class Soulseek(Provider):
|
||||
{
|
||||
"file": file_data,
|
||||
"username": username,
|
||||
"filename": getattr(file_data, "filename", "?"),
|
||||
"size": getattr(file_data, "filesize", 0),
|
||||
"filename": getattr(file_data,
|
||||
"filename",
|
||||
"?"),
|
||||
"size": getattr(file_data,
|
||||
"filesize",
|
||||
0),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -366,14 +383,22 @@ class Soulseek(Provider):
|
||||
{
|
||||
"file": file_data,
|
||||
"username": username,
|
||||
"filename": getattr(file_data, "filename", "?"),
|
||||
"size": getattr(file_data, "filesize", 0),
|
||||
"filename": getattr(file_data,
|
||||
"filename",
|
||||
"?"),
|
||||
"size": getattr(file_data,
|
||||
"filesize",
|
||||
0),
|
||||
}
|
||||
)
|
||||
|
||||
return flat
|
||||
|
||||
async def _collect_results(self, search_request: Any, timeout: float = 75.0) -> None:
|
||||
async def _collect_results(
|
||||
self,
|
||||
search_request: Any,
|
||||
timeout: float = 75.0
|
||||
) -> None:
|
||||
end = time.time() + timeout
|
||||
last_count = 0
|
||||
while time.time() < end:
|
||||
@@ -387,20 +412,28 @@ class Soulseek(Provider):
|
||||
self,
|
||||
query: str,
|
||||
limit: int = 50,
|
||||
filters: Optional[Dict[str, Any]] = None,
|
||||
filters: Optional[Dict[str,
|
||||
Any]] = None,
|
||||
**kwargs: Any,
|
||||
) -> List[SearchResult]:
|
||||
filters = filters or {}
|
||||
|
||||
try:
|
||||
flat_results = asyncio.run(self.perform_search(query, timeout=9.0, limit=limit))
|
||||
flat_results = asyncio.run(
|
||||
self.perform_search(query,
|
||||
timeout=9.0,
|
||||
limit=limit)
|
||||
)
|
||||
if not flat_results:
|
||||
return []
|
||||
|
||||
music_results: List[dict] = []
|
||||
for item in flat_results:
|
||||
filename = item["filename"]
|
||||
ext = ("." + filename.rsplit(".", 1)[-1].lower()) if "." in filename else ""
|
||||
ext = (
|
||||
"." + filename.rsplit(".",
|
||||
1)[-1].lower()
|
||||
) if "." in filename else ""
|
||||
if ext in self.MUSIC_EXTENSIONS:
|
||||
music_results.append(item)
|
||||
|
||||
@@ -410,18 +443,23 @@ class Soulseek(Provider):
|
||||
enriched_results: List[dict] = []
|
||||
for item in music_results:
|
||||
filename = item["filename"]
|
||||
ext = ("." + filename.rsplit(".", 1)[-1].lower()) if "." in filename else ""
|
||||
ext = (
|
||||
"." + filename.rsplit(".",
|
||||
1)[-1].lower()
|
||||
) if "." in filename else ""
|
||||
|
||||
display_name = filename.replace("\\", "/").split("/")[-1]
|
||||
path_parts = filename.replace("\\", "/").split("/")
|
||||
artist = path_parts[-3] if len(path_parts) >= 3 else ""
|
||||
album = (
|
||||
path_parts[-2]
|
||||
if len(path_parts) >= 3
|
||||
else (path_parts[-2] if len(path_parts) == 2 else "")
|
||||
path_parts[-2] if len(path_parts) >= 3 else
|
||||
(path_parts[-2] if len(path_parts) == 2 else "")
|
||||
)
|
||||
|
||||
base_name = display_name.rsplit(".", 1)[0] if "." in display_name else display_name
|
||||
base_name = display_name.rsplit(
|
||||
".",
|
||||
1
|
||||
)[0] if "." in display_name else display_name
|
||||
track_num = ""
|
||||
title = base_name
|
||||
filename_artist = ""
|
||||
@@ -457,7 +495,8 @@ class Soulseek(Provider):
|
||||
if artist_filter or album_filter or track_filter:
|
||||
filtered: List[dict] = []
|
||||
for item in enriched_results:
|
||||
if artist_filter and artist_filter not in item["artist"].lower():
|
||||
if artist_filter and artist_filter not in item["artist"].lower(
|
||||
):
|
||||
continue
|
||||
if album_filter and album_filter not in item["album"].lower():
|
||||
continue
|
||||
@@ -466,7 +505,9 @@ class Soulseek(Provider):
|
||||
filtered.append(item)
|
||||
enriched_results = filtered
|
||||
|
||||
enriched_results.sort(key=lambda item: (item["ext"].lower() != ".flac", -item["size"]))
|
||||
enriched_results.sort(
|
||||
key=lambda item: (item["ext"].lower() != ".flac", -item["size"])
|
||||
)
|
||||
|
||||
results: List[SearchResult] = []
|
||||
for item in enriched_results:
|
||||
@@ -475,11 +516,16 @@ class Soulseek(Provider):
|
||||
size_mb = int(item["size"] / 1024 / 1024)
|
||||
|
||||
columns = [
|
||||
("Track", item["track_num"] or "?"),
|
||||
("Title", item["title"][:40]),
|
||||
("Artist", artist_display[:32]),
|
||||
("Album", album_display[:32]),
|
||||
("Size", f"{size_mb} MB"),
|
||||
("Track",
|
||||
item["track_num"] or "?"),
|
||||
("Title",
|
||||
item["title"][:40]),
|
||||
("Artist",
|
||||
artist_display[:32]),
|
||||
("Album",
|
||||
album_display[:32]),
|
||||
("Size",
|
||||
f"{size_mb} MB"),
|
||||
]
|
||||
|
||||
results.append(
|
||||
@@ -488,7 +534,8 @@ class Soulseek(Provider):
|
||||
title=item["title"],
|
||||
path=item["filename"],
|
||||
detail=f"{artist_display} - {album_display}",
|
||||
annotations=[f"{size_mb} MB", item["ext"].lstrip(".").upper()],
|
||||
annotations=[f"{size_mb} MB",
|
||||
item["ext"].lstrip(".").upper()],
|
||||
media_kind="audio",
|
||||
size_bytes=item["size"],
|
||||
columns=columns,
|
||||
@@ -515,7 +562,7 @@ class Soulseek(Provider):
|
||||
|
||||
# Require configured credentials.
|
||||
try:
|
||||
from config import get_soulseek_username, get_soulseek_password
|
||||
from SYS.config import get_soulseek_username, get_soulseek_password
|
||||
|
||||
user = get_soulseek_username(self.config)
|
||||
pwd = get_soulseek_password(self.config)
|
||||
@@ -570,10 +617,16 @@ async def download_soulseek_file(
|
||||
)
|
||||
|
||||
settings = Settings(
|
||||
credentials=CredentialsSettings(username=login_user, password=login_pass)
|
||||
credentials=CredentialsSettings(username=login_user,
|
||||
password=login_pass)
|
||||
)
|
||||
|
||||
async def _attempt_once(attempt_num: int) -> tuple[Optional[Path], Any, int, float]:
|
||||
async def _attempt_once(
|
||||
attempt_num: int
|
||||
) -> tuple[Optional[Path],
|
||||
Any,
|
||||
int,
|
||||
float]:
|
||||
client = SoulSeekClient(settings)
|
||||
with _suppress_aioslsk_noise():
|
||||
async with _suppress_aioslsk_asyncio_task_noise():
|
||||
@@ -586,10 +639,14 @@ async def download_soulseek_file(
|
||||
f"[soulseek] Download attempt {attempt_num}: {username} :: {local_filename}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
debug(f"[soulseek] Requesting download from {username}: {filename}")
|
||||
debug(
|
||||
f"[soulseek] Requesting download from {username}: {filename}"
|
||||
)
|
||||
|
||||
transfer = await client.transfers.add(
|
||||
Transfer(username, filename, TransferDirection.DOWNLOAD)
|
||||
Transfer(username,
|
||||
filename,
|
||||
TransferDirection.DOWNLOAD)
|
||||
)
|
||||
transfer.local_path = str(output_path)
|
||||
await client.transfers.queue(transfer)
|
||||
@@ -602,14 +659,29 @@ async def download_soulseek_file(
|
||||
elapsed = time.time() - start_time
|
||||
if elapsed > timeout:
|
||||
log(
|
||||
f"[soulseek] Download timeout after {timeout}s", file=sys.stderr
|
||||
f"[soulseek] Download timeout after {timeout}s",
|
||||
file=sys.stderr
|
||||
)
|
||||
bytes_done = int(
|
||||
getattr(transfer,
|
||||
"bytes_transfered",
|
||||
0) or 0
|
||||
)
|
||||
state_val = getattr(
|
||||
getattr(transfer,
|
||||
"state",
|
||||
None),
|
||||
"VALUE",
|
||||
None
|
||||
)
|
||||
bytes_done = int(getattr(transfer, "bytes_transfered", 0) or 0)
|
||||
state_val = getattr(getattr(transfer, "state", None), "VALUE", None)
|
||||
progress_bar.finish()
|
||||
return None, state_val, bytes_done, elapsed
|
||||
|
||||
bytes_done = int(getattr(transfer, "bytes_transfered", 0) or 0)
|
||||
bytes_done = int(
|
||||
getattr(transfer,
|
||||
"bytes_transfered",
|
||||
0) or 0
|
||||
)
|
||||
total_bytes = int(getattr(transfer, "filesize", 0) or 0)
|
||||
now = time.time()
|
||||
if now - last_progress_time >= 0.5:
|
||||
@@ -623,11 +695,18 @@ async def download_soulseek_file(
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
final_state = getattr(getattr(transfer, "state", None), "VALUE", None)
|
||||
final_state = getattr(
|
||||
getattr(transfer,
|
||||
"state",
|
||||
None),
|
||||
"VALUE",
|
||||
None
|
||||
)
|
||||
downloaded_path = (
|
||||
Path(transfer.local_path)
|
||||
if getattr(transfer, "local_path", None)
|
||||
else output_path
|
||||
if getattr(transfer,
|
||||
"local_path",
|
||||
None) else output_path
|
||||
)
|
||||
final_elapsed = time.time() - start_time
|
||||
|
||||
@@ -636,7 +715,8 @@ async def download_soulseek_file(
|
||||
|
||||
# If a file was written, treat it as success even if state is odd.
|
||||
try:
|
||||
if downloaded_path.exists() and downloaded_path.stat().st_size > 0:
|
||||
if downloaded_path.exists() and downloaded_path.stat(
|
||||
).st_size > 0:
|
||||
if final_state != TransferState.COMPLETE:
|
||||
log(
|
||||
f"[soulseek] Transfer finalized as {final_state}, but file exists ({downloaded_path.stat().st_size} bytes). Keeping file.",
|
||||
@@ -651,7 +731,8 @@ async def download_soulseek_file(
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if final_state == TransferState.COMPLETE and downloaded_path.exists():
|
||||
if final_state == TransferState.COMPLETE and downloaded_path.exists(
|
||||
):
|
||||
debug(f"[soulseek] Download complete: {downloaded_path}")
|
||||
return (
|
||||
downloaded_path,
|
||||
@@ -670,7 +751,8 @@ async def download_soulseek_file(
|
||||
|
||||
# Clean up 0-byte placeholder.
|
||||
try:
|
||||
if downloaded_path.exists() and downloaded_path.stat().st_size == 0:
|
||||
if downloaded_path.exists() and downloaded_path.stat(
|
||||
).st_size == 0:
|
||||
downloaded_path.unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -696,7 +778,8 @@ async def download_soulseek_file(
|
||||
should_retry = (bytes_done == 0) and (elapsed < 15.0)
|
||||
if attempt < max_attempts and should_retry:
|
||||
log(
|
||||
f"[soulseek] Retrying after fast failure (state={final_state})", file=sys.stderr
|
||||
f"[soulseek] Retrying after fast failure (state={final_state})",
|
||||
file=sys.stderr
|
||||
)
|
||||
await asyncio.sleep(2)
|
||||
continue
|
||||
@@ -704,7 +787,10 @@ async def download_soulseek_file(
|
||||
return None
|
||||
|
||||
except ImportError:
|
||||
log("[soulseek] aioslsk not installed. Install with: pip install aioslsk", file=sys.stderr)
|
||||
log(
|
||||
"[soulseek] aioslsk not installed. Install with: pip install aioslsk",
|
||||
file=sys.stderr
|
||||
)
|
||||
return None
|
||||
except Exception as exc:
|
||||
log(f"[soulseek] Download failed: {type(exc).__name__}: {exc}", file=sys.stderr)
|
||||
|
||||
Reference in New Issue
Block a user