From dbe543b433c4350da47a643e5a958d82f3a152db Mon Sep 17 00:00:00 2001 From: "bairon.dev" Date: Thu, 2 Jul 2026 01:01:51 -0400 Subject: [PATCH] fix: drop SolidTorrents so games stop showing under TV (#33) --- README.md | 2 +- src/sources/registry.ts | 2 -- src/sources/solidtorrents.ts | 60 --------------------------------- src/sources/types.ts | 1 - src/ui/components/Downloads.tsx | 6 ++-- src/ui/components/Results.tsx | 6 ++-- src/ui/components/Seeding.tsx | 4 +-- src/ui/theme.ts | 9 ++++- 8 files changed, 17 insertions(+), 73 deletions(-) delete mode 100644 src/sources/solidtorrents.ts diff --git a/README.md b/README.md index 1e7e5e1..e84038f 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ A short, hand-picked list of trusted sources: | --- | --- | | Games | FitGirl | | Movies | YTS, The Pirate Bay, 1337x | -| TV | EZTV, SolidTorrents, The Pirate Bay, 1337x | +| TV | EZTV, The Pirate Bay, 1337x | | Anime | Nyaa, SubsPlease | Games are the only category that can run code, so they come from FitGirl alone, a repacker with a long, trusted track record; everything else is plain video and subtitles. If a source is down, the search carries on without it, and torlink tells you which one is offline. diff --git a/src/sources/registry.ts b/src/sources/registry.ts index 67c2abb..c77b81e 100644 --- a/src/sources/registry.ts +++ b/src/sources/registry.ts @@ -2,7 +2,6 @@ import { eztv } from "./eztv"; import { fitgirl } from "./fitgirl"; import { nyaa } from "./nyaa"; import { subsplease } from "./subsplease"; -import { solid } from "./solidtorrents"; import { tpbMovies, tpbTv } from "./piratebay"; import { x1337Movies, x1337Tv } from "./x1337"; import { yts } from "./yts"; @@ -14,7 +13,6 @@ export const SOURCES: readonly Source[] = [ tpbMovies, x1337Movies, eztv, - solid, tpbTv, x1337Tv, nyaa, diff --git a/src/sources/solidtorrents.ts b/src/sources/solidtorrents.ts deleted file mode 100644 index 2e01a25..0000000 --- a/src/sources/solidtorrents.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { fetchResilient, HttpError, USER_AGENT } from "../util/net"; -import { buildMagnet } from "./magnet"; -import type { SearchOptions, Source, TorrentResult } from "./types"; - -interface SolidResult { - infohash?: string; - title?: string; - size?: number; - seeders?: number; - leechers?: number; - updatedAt?: string; -} - -interface SolidResponse { - success?: boolean; - results?: SolidResult[]; -} - -async function search(query: string, opts: SearchOptions = {}): Promise { - const q = query.trim() || "tv show"; - const params = new URLSearchParams({ q }); - const url = `https://solidtorrents.net/api/v1/search?${params.toString()}`; - - const res = await fetchResilient(url, { - headers: { "User-Agent": USER_AGENT }, - signal: opts.signal, - retries: 1, - }); - - if (!res.ok) throw new HttpError(res.status, `SolidTorrents returned ${res.status}`); - const json = (await res.json()) as SolidResponse; - - const out: TorrentResult[] = []; - for (const item of json.results ?? []) { - if (!item.infohash) continue; - const infoHash = item.infohash.toLowerCase(); - const name = item.title || "Unknown"; - const added = item.updatedAt ? Math.floor(new Date(item.updatedAt).getTime() / 1000) : undefined; - - out.push({ - infoHash, - name, - sizeBytes: item.size ?? 0, - seeders: item.seeders ?? 0, - leechers: item.leechers ?? 0, - source: "solid", - magnet: buildMagnet(infoHash, name), - added, - }); - } - return out; -} - -export const solid: Source = { - id: "solid", - label: "Solid", - group: "TV", - homepage: "https://solidtorrents.net", - search, -}; diff --git a/src/sources/types.ts b/src/sources/types.ts index 753e38d..db4ac44 100644 --- a/src/sources/types.ts +++ b/src/sources/types.ts @@ -4,7 +4,6 @@ export type SourceId = | "eztv" | "nyaa" | "subsplease" - | "solid" | "tpb-movies" | "tpb-tv" | "x1337-movies" diff --git a/src/ui/components/Downloads.tsx b/src/ui/components/Downloads.tsx index 99bc965..a7dbab4 100644 --- a/src/ui/components/Downloads.tsx +++ b/src/ui/components/Downloads.tsx @@ -4,7 +4,7 @@ import { useStore, useQueueItems, useQueueHistory, type DownloadFocus } from ".. import { Panel } from "./Panel"; import { ProgressBar } from "./ProgressBar"; import { wrapStep, windowStart } from "../move"; -import { COLOR, GUTTER, ICON, SOURCE_STYLE } from "../theme"; +import { COLOR, GUTTER, ICON, sourceStyle } from "../theme"; import { cleanText, formatBytes, @@ -146,7 +146,7 @@ export function Downloads() { {activeVisible.map((it, i) => { const here = activeStart + i === clamped && focused && inActive; const sc = statusColor(it.status); - const ss = SOURCE_STYLE[it.source ?? "fitgirl"]; + const ss = sourceStyle(it.source); return ( @@ -201,7 +201,7 @@ export function Downloads() { {recentVisible.map((h: HistoryItem, i) => { const here = recentStart + i === recentCursor && focused && !inActive; - const ss = SOURCE_STYLE[h.source ?? "fitgirl"]; + const ss = sourceStyle(h.source); const when = formatRelative(h.completedAt / 1000); return ( diff --git a/src/ui/components/Results.tsx b/src/ui/components/Results.tsx index 7e13349..51c8186 100644 --- a/src/ui/components/Results.tsx +++ b/src/ui/components/Results.tsx @@ -9,7 +9,7 @@ import { useConcurrentSearch } from "../hooks/useConcurrentSearch"; import { getSource, SOURCES } from "../../sources/registry"; import { wrapStep, windowStart, resultsPanelOuter } from "../move"; import { sortResults, nextSort, sortLabel, sortArrow, type Sort, type SortField } from "../sort"; -import { COLOR, GUTTER, ICON, SOURCE_STYLE } from "../theme"; +import { COLOR, GUTTER, ICON, sourceStyle } from "../theme"; import { cleanText, formatBytes, formatRelative, truncate } from "../../util/format"; import type { Source, TorrentResult } from "../../sources/types"; @@ -29,7 +29,7 @@ function DetailRow({ label, value }: { label: string; value: ReactNode }) { } function Detail({ r, width }: { r: TorrentResult; width: number }) { - const ss = SOURCE_STYLE[r.source]; + const ss = sourceStyle(r.source); const date = formatRelative(r.added); const health = r.seeders || r.leechers ? ( @@ -363,7 +363,7 @@ export function Results() { {visible.map((r, i) => { const index = start + i; const here = index === clamped && focused && mode === "list"; - const ss = SOURCE_STYLE[r.source]; + const ss = sourceStyle(r.source); return ( diff --git a/src/ui/components/Seeding.tsx b/src/ui/components/Seeding.tsx index 258b74a..00c4ee0 100644 --- a/src/ui/components/Seeding.tsx +++ b/src/ui/components/Seeding.tsx @@ -3,7 +3,7 @@ import { Box, Text, useInput } from "ink"; import { useStore, useQueueHistory, useSeeds, type SeedFocus } from "../store"; import { Panel } from "./Panel"; import { wrapStep, windowStart } from "../move"; -import { COLOR, GUTTER, ICON, SOURCE_STYLE } from "../theme"; +import { COLOR, GUTTER, ICON, sourceStyle } from "../theme"; import { cleanText, formatBytes, formatBytesPerSec, truncate } from "../../util/format"; import type { SeedItem } from "../../download/types"; @@ -134,7 +134,7 @@ export function Seeding() { const seed = seeds.get(h.id); const g = glyph(seed); const st = statusCell(seed); - const ss = SOURCE_STYLE[h.source ?? "fitgirl"]; + const ss = sourceStyle(h.source); return ( diff --git a/src/ui/theme.ts b/src/ui/theme.ts index 73bef0e..36e6096 100644 --- a/src/ui/theme.ts +++ b/src/ui/theme.ts @@ -34,13 +34,20 @@ export const SOURCE_STYLE: Record = { eztv: { tag: "EZTV", color: COLOR.warn }, nyaa: { tag: "NYAA", color: COLOR.bright }, subsplease: { tag: "SUB", color: "#b9a7e6" }, - solid: { tag: "SLD", color: "#60a5fa" }, "tpb-movies": { tag: "TPB", color: "#5fd0c5" }, "tpb-tv": { tag: "TPB", color: "#5fd0c5" }, "x1337-movies": { tag: "1337", color: "#f6a55c" }, "x1337-tv": { tag: "1337", color: "#f6a55c" }, }; +// Tolerant lookup: a source id may be absent (a pasted magnet / bare infohash) or +// no longer exist (a removed source persisted in old history/seeds). Fall back to a +// neutral tag rather than indexing SOURCE_STYLE and crashing on `undefined`. +export function sourceStyle(id?: SourceId): { tag: string; color: string } { + const s = id ? (SOURCE_STYLE as Record)[id] : undefined; + return s ?? { tag: "•", color: COLOR.alt }; +} + function rgb(hex: string): [number, number, number] { const n = parseInt(hex.slice(1), 16); return [(n >> 16) & 255, (n >> 8) & 255, n & 255];