updated plugin refactor and added FTP and SCP plugins , also hydrusnetwork plugin migration
This commit is contained in:
@@ -12,10 +12,18 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from ProviderCore.base import Provider, SearchResult
|
||||
from SYS.logger import log, debug
|
||||
from SYS.logger import log, debug, debug_panel
|
||||
from SYS.models import ProgressBar
|
||||
|
||||
_SOULSEEK_NOISE_SUBSTRINGS = (
|
||||
"unhandled exception on loop",
|
||||
"Task exception was never retrieved",
|
||||
"future: <Task finished",
|
||||
"ConnectionFailedError",
|
||||
"PeerConnectionError",
|
||||
"indirect connection failed",
|
||||
"indirect connection timed out",
|
||||
"failed to connect",
|
||||
"search reply ticket does not match any search request",
|
||||
"failed to receive transfer ticket on file connection",
|
||||
"aioslsk.exceptions.ConnectionReadError",
|
||||
@@ -59,10 +67,10 @@ async def _suppress_aioslsk_asyncio_task_noise() -> Any:
|
||||
if msg == "Task exception was never retrieved":
|
||||
cls = getattr(exc, "__class__", None)
|
||||
name = getattr(cls, "__name__", "")
|
||||
mod = getattr(cls, "__module__", "")
|
||||
exc_text = str(exc or "").lower()
|
||||
|
||||
# Suppress ConnectionFailedError from aioslsk
|
||||
if name == "ConnectionFailedError" and str(mod).startswith("aioslsk"):
|
||||
# Suppress expected peer direct-connect failures from aioslsk.
|
||||
if name == "ConnectionFailedError" or "failed to connect" in exc_text:
|
||||
return
|
||||
except Exception:
|
||||
# If our filter logic fails, fall through to default handling.
|
||||
@@ -117,6 +125,9 @@ class _LineFilterStream(io.TextIOBase):
|
||||
self._in_tb = False
|
||||
self._tb_lines: list[str] = []
|
||||
self._tb_suppress = False
|
||||
self._in_task_block = False
|
||||
self._task_lines: list[str] = []
|
||||
self._task_suppress = False
|
||||
|
||||
def writable(self) -> bool: # pragma: no cover
|
||||
return True
|
||||
@@ -137,6 +148,19 @@ class _LineFilterStream(io.TextIOBase):
|
||||
self._tb_suppress = False
|
||||
self._in_tb = False
|
||||
|
||||
def _flush_task_block(self) -> None:
|
||||
if not self._task_lines:
|
||||
return
|
||||
if not self._task_suppress:
|
||||
for l in self._task_lines:
|
||||
try:
|
||||
self._underlying.write(l + "\n")
|
||||
except Exception:
|
||||
pass
|
||||
self._task_lines = []
|
||||
self._task_suppress = False
|
||||
self._in_task_block = False
|
||||
|
||||
def write(self, s: str) -> int:
|
||||
self._buf += str(s)
|
||||
while "\n" in self._buf:
|
||||
@@ -145,6 +169,29 @@ class _LineFilterStream(io.TextIOBase):
|
||||
return len(s)
|
||||
|
||||
def _handle_line(self, line: str) -> None:
|
||||
if not self._in_task_block and self._should_suppress_line(line) and (
|
||||
line.startswith("Task exception was never retrieved")
|
||||
or line.startswith("future: <Task finished")
|
||||
or line.startswith("unhandled exception on loop")
|
||||
):
|
||||
self._in_task_block = True
|
||||
self._task_lines = [line]
|
||||
self._task_suppress = True
|
||||
return
|
||||
|
||||
if self._in_task_block:
|
||||
if line.startswith("Traceback (most recent call last):"):
|
||||
self._in_tb = True
|
||||
self._tb_lines = [line]
|
||||
self._tb_suppress = True
|
||||
return
|
||||
self._task_lines.append(line)
|
||||
if self._should_suppress_line(line):
|
||||
self._task_suppress = True
|
||||
if line.strip() == "":
|
||||
self._flush_task_block()
|
||||
return
|
||||
|
||||
# Start capturing tracebacks so we can suppress the whole block if it matches.
|
||||
if not self._in_tb and line.startswith("Traceback (most recent call last):"):
|
||||
self._in_tb = True
|
||||
@@ -159,6 +206,8 @@ class _LineFilterStream(io.TextIOBase):
|
||||
# End traceback block on blank line.
|
||||
if line.strip() == "":
|
||||
self._flush_tb()
|
||||
if self._in_task_block:
|
||||
self._flush_task_block()
|
||||
return
|
||||
|
||||
# Non-traceback line
|
||||
@@ -174,6 +223,8 @@ class _LineFilterStream(io.TextIOBase):
|
||||
if self._in_tb:
|
||||
# If the traceback ends without a trailing blank line, decide here.
|
||||
self._flush_tb()
|
||||
if self._in_task_block:
|
||||
self._flush_task_block()
|
||||
if self._buf:
|
||||
line = self._buf
|
||||
self._buf = ""
|
||||
@@ -422,14 +473,14 @@ class Soulseek(Provider):
|
||||
|
||||
try:
|
||||
search_request = await client.searches.search(query)
|
||||
await self._collect_results(search_request, timeout=timeout)
|
||||
return self._flatten_results(search_request)[:limit]
|
||||
summary = await self._collect_results(search_request, timeout=timeout)
|
||||
return self._flatten_results(search_request)[:limit], summary
|
||||
except Exception as exc:
|
||||
log(
|
||||
f"[soulseek] Search error: {type(exc).__name__}: {exc}",
|
||||
file=sys.stderr
|
||||
)
|
||||
return []
|
||||
return [], {}
|
||||
finally:
|
||||
# Best-effort: try to cancel/close the search request before stopping
|
||||
# the client to reduce stray reply spam.
|
||||
@@ -477,16 +528,24 @@ class Soulseek(Provider):
|
||||
self,
|
||||
search_request: Any,
|
||||
timeout: float = 75.0
|
||||
) -> None:
|
||||
end = time.time() + timeout
|
||||
) -> Dict[str, Any]:
|
||||
start = time.time()
|
||||
end = start + timeout
|
||||
last_count = 0
|
||||
update_count = 0
|
||||
while time.time() < end:
|
||||
current_count = len(getattr(search_request, "results", []))
|
||||
if current_count > last_count:
|
||||
debug(f"[soulseek] Got {current_count} result(s)...")
|
||||
last_count = current_count
|
||||
update_count += 1
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
return {
|
||||
"peer_hits": last_count,
|
||||
"count_updates": update_count,
|
||||
"elapsed_seconds": round(max(0.0, time.time() - start), 1),
|
||||
}
|
||||
|
||||
def search(
|
||||
self,
|
||||
query: str,
|
||||
@@ -503,7 +562,7 @@ class Soulseek(Provider):
|
||||
base_tmp.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
flat_results = asyncio.run(
|
||||
flat_results, search_summary = asyncio.run(
|
||||
self.perform_search(query,
|
||||
timeout=9.0,
|
||||
limit=limit)
|
||||
@@ -636,6 +695,23 @@ class Soulseek(Provider):
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
debug_panel(
|
||||
"soulseek search",
|
||||
[
|
||||
("query", query),
|
||||
("peer_hits", search_summary.get("peer_hits", 0)),
|
||||
("file_hits", len(flat_results)),
|
||||
("audio_hits", len(music_results)),
|
||||
("results", len(results)),
|
||||
("poll_updates", search_summary.get("count_updates", 0)),
|
||||
("elapsed_s", search_summary.get("elapsed_seconds", 0.0)),
|
||||
],
|
||||
border_style="magenta",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return results
|
||||
|
||||
except Exception as exc:
|
||||
|
||||
Reference in New Issue
Block a user