Fix empty proxy sources (empty pool), first-run bat, elite hint

- load_settings/sanitize_settings: treat sources=[] as invalid; restore defaults (all([]) was true)
- _build_pool: defensive default sources + clearer empty-fetch log; elite-only empty fetch message
- FirstRun_Build_And_Install.bat: Python check + setup_and_build.ps1
- README + test for empty sources sanitize

Made-with: Cursor
This commit is contained in:
Dr Jones
2026-04-16 00:44:42 -07:00
parent 214d2d2ff7
commit c9c45fdfec
5 changed files with 72 additions and 5 deletions

View File

@@ -217,6 +217,10 @@ def sanitize_settings(s: Settings) -> tuple[Settings, bool]:
except (TypeError, ValueError):
s.validation_timeout_seconds = 12.0
changed = True
# Empty list survives "all(isinstance…)" — would skip all fetches and yield an empty pool.
if not isinstance(s.sources, list) or not all(isinstance(x, str) for x in s.sources) or len(s.sources) == 0:
s.sources = list(Settings().sources)
changed = True
return s, changed
@@ -240,8 +244,12 @@ def load_settings() -> Settings:
for k, v in raw.items():
if hasattr(s, k):
setattr(s, k, v)
if not isinstance(s.sources, list) or not all(isinstance(x, str) for x in s.sources):
s.sources = Settings().sources
if (
not isinstance(s.sources, list)
or not all(isinstance(x, str) for x in s.sources)
or len(s.sources) == 0
):
s.sources = list(Settings().sources)
if s.obfuscation_mode not in OBFUSCATION_MODES:
s.obfuscation_mode = "auto"
if not isinstance(s.pinned_chain, list):

View File

@@ -469,6 +469,14 @@ class ChainService:
timeout=55.0,
)
entries = normalize_entries(rows, s.prefer_elite)
if s.prefer_elite and rows and not entries:
self._notify({
"type": "log",
"text": (
f"Source returned {len(rows)} proxies but none marked “elite” — all skipped. "
"Turn off “Elite proxies only” in Chain Builder if every fetch is empty."
),
})
self._notify({"type": "log", "text": f"Fetched {len(entries)} from source."})
return entries
except asyncio.TimeoutError:
@@ -478,13 +486,26 @@ class ChainService:
self._notify({"type": "log", "text": f"Fetch error: {e!s}"})
return []
results = await asyncio.gather(*(_fetch_one(u) for u in s.sources))
src_urls = list(s.sources) if s.sources else list(Settings().sources)
if not s.sources:
self._notify({
"type": "log",
"text": "Settings had no proxy sources — using built-in defaults (save Settings to persist).",
})
if not src_urls:
self._notify({"type": "log", "text": "No proxy source URLs configured — cannot build a pool."})
return []
results = await asyncio.gather(*(_fetch_one(u) for u in src_urls))
raw_urls: list[str] = []
for chunk in results:
raw_urls.extend(chunk)
if not raw_urls:
self._notify({"type": "log", "text": "No proxies fetched from any source!"})
self._notify({
"type": "log",
"text": "No proxies fetched from any source (check network, URLs, or turn off “Elite only” if every list is filtered empty).",
})
return []
# Deduplicate, remove blacklisted, shuffle before capping