mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
chore(ai): remove dead code (agent)
Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
38006c52b6
commit
f80bbc7dae
7 files changed
+20
-1685
No files matched your search
+3
-3
@@ -21,10 +21,10 @@ import { buildModerationBadgeText } from "./ui/moderationCopy.js";
|
||||
import { formatShortNpub } from "./utils/formatters.js";
|
||||
import { attachFeedInfoPopover } from "./ui/components/FeedInfoPopover.js";
|
||||
|
||||
export const WATCH_HISTORY_EMPTY_COPY =
|
||||
const WATCH_HISTORY_EMPTY_COPY =
|
||||
"Your watch history is empty. Watch some videos to populate this list.";
|
||||
|
||||
export const WATCH_HISTORY_DISABLED_COPY =
|
||||
const WATCH_HISTORY_DISABLED_COPY =
|
||||
"Watch history sync is unavailable. Connect a NIP-07 extension or log in to enable syncing.";
|
||||
|
||||
const DEFAULT_WATCH_HISTORY_BATCH_SIZE = 12;
|
||||
@@ -2494,7 +2494,7 @@ export function createWatchHistoryRenderer(config = {}) {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
export const watchHistoryRenderer = createWatchHistoryRenderer();
|
||||
const watchHistoryRenderer = createWatchHistoryRenderer();
|
||||
|
||||
export async function initHistoryView() {
|
||||
const infoTrigger = document.getElementById("historyInfoTrigger");
|
||||
|
||||
+3
-19
@@ -36,7 +36,7 @@ export function safeDecodeMagnet(value) {
|
||||
return decoded;
|
||||
}
|
||||
|
||||
export function normalizeForComparison(value) {
|
||||
function normalizeForComparison(value) {
|
||||
if (typeof value !== "string") {
|
||||
return "";
|
||||
}
|
||||
@@ -258,7 +258,7 @@ export function buildMagnetUri(normalizedScheme, params, fragment) {
|
||||
return `${normalizedScheme}${queryString ? `?${queryString}` : ""}${fragment || ""}`;
|
||||
}
|
||||
|
||||
export function appendUniqueParam(params, key, value) {
|
||||
function appendUniqueParam(params, key, value) {
|
||||
if (!Array.isArray(params)) {
|
||||
return false;
|
||||
}
|
||||
@@ -305,7 +305,7 @@ export function ensureTrackers(params, trackers = WSS_TRACKERS) {
|
||||
return didMutate;
|
||||
}
|
||||
|
||||
export function sanitizeHttpUrl(candidate) {
|
||||
function sanitizeHttpUrl(candidate) {
|
||||
if (typeof candidate !== "string") {
|
||||
return "";
|
||||
}
|
||||
@@ -403,22 +403,6 @@ export function ensureWebSeeds(
|
||||
return didMutate;
|
||||
}
|
||||
|
||||
export function formatAbsoluteUrl(candidate) {
|
||||
if (typeof candidate !== "string") {
|
||||
return "";
|
||||
}
|
||||
const trimmed = candidate.trim();
|
||||
if (!trimmed) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
const parsed = new URL(trimmed);
|
||||
return parsed.toString();
|
||||
} catch (err) {
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
export function extractMagnetHints(rawValue) {
|
||||
const { params } = normalizeMagnetInput(rawValue);
|
||||
const hints = { ws: "", xs: "" };
|
||||
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
import { multipartUpload, ensureBucketExists, ensureBucketCors } from "./storage/s3-multipart.js";
|
||||
import { makeS3Client } from "./storage/s3-client.js";
|
||||
import { userLogger } from "./utils/logger.js";
|
||||
|
||||
const STORAGE_KEY = "bitvid:quickS3Settings";
|
||||
|
||||
export function loadLocalSettings() {
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) return null;
|
||||
return JSON.parse(raw);
|
||||
} catch (err) {
|
||||
userLogger.warn("Failed to load quick S3 settings:", err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function saveLocalSettings(settings) {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
||||
} catch (err) {
|
||||
userLogger.warn("Failed to save quick S3 settings:", err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadWithKeys({
|
||||
file,
|
||||
key,
|
||||
settings, // { endpoint, region, bucket, accessKeyId, secretAccessKey, forcePathStyle, createBucketIfMissing }
|
||||
onProgress
|
||||
}) {
|
||||
if (!settings.endpoint || !settings.bucket || !settings.accessKeyId || !settings.secretAccessKey) {
|
||||
throw new Error("Missing S3 credentials.");
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
throw new Error("Target object key is required.");
|
||||
}
|
||||
|
||||
const s3 = makeS3Client({
|
||||
endpoint: settings.endpoint,
|
||||
region: settings.region,
|
||||
credentials: {
|
||||
accessKeyId: settings.accessKeyId,
|
||||
secretAccessKey: settings.secretAccessKey
|
||||
},
|
||||
forcePathStyle: settings.forcePathStyle
|
||||
});
|
||||
|
||||
if (settings.createBucketIfMissing) {
|
||||
await ensureBucketExists({ s3, bucket: settings.bucket, region: settings.region });
|
||||
}
|
||||
|
||||
// Ensure CORS only if we have keys
|
||||
try {
|
||||
const origins = [];
|
||||
if (typeof window !== "undefined" && window.location) {
|
||||
if (window.location.origin && window.location.origin !== "null") {
|
||||
origins.push(window.location.origin);
|
||||
}
|
||||
}
|
||||
// Add standard localdev origins just in case
|
||||
origins.push('http://localhost:8000');
|
||||
origins.push('http://127.0.0.1:8000');
|
||||
|
||||
const uniqueOrigins = [...new Set(origins)];
|
||||
|
||||
await ensureBucketCors({
|
||||
s3,
|
||||
bucket: settings.bucket,
|
||||
origins: uniqueOrigins,
|
||||
region: settings.region
|
||||
});
|
||||
} catch (err) {
|
||||
userLogger.warn("Failed to ensure CORS (ignoring):", err);
|
||||
}
|
||||
|
||||
return multipartUpload({
|
||||
s3,
|
||||
bucket: settings.bucket,
|
||||
key,
|
||||
file,
|
||||
contentType: file.type,
|
||||
onProgress,
|
||||
createBucketIfMissing: false // Already handled
|
||||
});
|
||||
}
|
||||
|
||||
export async function uploadWithManifest({
|
||||
file,
|
||||
manifest,
|
||||
onProgress
|
||||
}) {
|
||||
// manifest: {
|
||||
// bucket: string,
|
||||
// key: string,
|
||||
// uploadId: string,
|
||||
// parts: { partNumber: number, url: string }[],
|
||||
// completeUrl?: string,
|
||||
// partSize?: number
|
||||
// }
|
||||
if (!manifest || !Array.isArray(manifest.parts) || manifest.parts.length === 0) {
|
||||
throw new Error("Invalid manifest: 'parts' array required.");
|
||||
}
|
||||
|
||||
// Ensure parts are sorted
|
||||
const parts = [...manifest.parts].sort((a, b) => a.partNumber - b.partNumber);
|
||||
|
||||
const fileSize = file.size;
|
||||
const partCount = parts.length;
|
||||
|
||||
// Determine part size
|
||||
// If not provided, assume standard S3 splitting (equal parts except last)
|
||||
const partSize = manifest.partSize || Math.ceil(fileSize / partCount);
|
||||
|
||||
const etags = [];
|
||||
let uploadedBytes = 0;
|
||||
|
||||
for (let i = 0; i < partCount; i++) {
|
||||
const part = parts[i];
|
||||
const partNum = part.partNumber;
|
||||
const url = part.url;
|
||||
|
||||
// Calculate byte range
|
||||
const index = i;
|
||||
const start = index * partSize;
|
||||
if (start >= fileSize) break;
|
||||
|
||||
const end = Math.min(start + partSize, fileSize);
|
||||
const chunk = file.slice(start, end);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
body: chunk
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to upload part ${partNum}: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const etag = response.headers.get('ETag');
|
||||
if (!etag) {
|
||||
userLogger.warn(`Part ${partNum} uploaded but no ETag header received.`);
|
||||
}
|
||||
|
||||
etags.push({ PartNumber: partNum, ETag: etag });
|
||||
uploadedBytes += chunk.size;
|
||||
|
||||
if (onProgress) {
|
||||
onProgress(uploadedBytes / fileSize);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Network error uploading part ${partNum}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (manifest.completeUrl) {
|
||||
const compRes = await fetch(manifest.completeUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ Parts: etags })
|
||||
});
|
||||
if (!compRes.ok) {
|
||||
throw new Error(`Failed to complete upload: ${compRes.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
return etags;
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { WSS_TRACKERS as DEFAULT_WSS_TRACKERS } from "./constants.js";
|
||||
|
||||
export const TRACKER_TIMEOUT_MS = 6000;
|
||||
export const TRACKER_PER_MAGNET = 10;
|
||||
const TRACKER_PER_MAGNET = 10;
|
||||
export const HEALTH_TTL_MS = 2 * 60 * 1000;
|
||||
export const CONCURRENCY = 6;
|
||||
export const TRACKER_ERROR_COOLDOWN_MS = 60 * 1000;
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
import "./bufferPolyfill.js";
|
||||
import Client from "https://esm.sh/[email protected]/client?bundle";
|
||||
import { infoHashFromMagnet, trackersFromMagnet } from "./magnets.js";
|
||||
import {
|
||||
resolveTrackerList,
|
||||
TRACKER_TIMEOUT_MS,
|
||||
TRACKER_ERROR_COOLDOWN_MS,
|
||||
} from "./trackerConfig.js";
|
||||
|
||||
const trackerState = new Map();
|
||||
|
||||
function now() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
function randomPeerId() {
|
||||
const bytes = new Uint8Array(20);
|
||||
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
||||
crypto.getRandomValues(bytes);
|
||||
} else {
|
||||
for (let i = 0; i < bytes.length; i += 1) {
|
||||
bytes[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function getDefaultHealth() {
|
||||
return {
|
||||
ok: false,
|
||||
seeders: 0,
|
||||
leechers: 0,
|
||||
responded: false,
|
||||
from: [],
|
||||
};
|
||||
}
|
||||
|
||||
function getTrackerEntry(url) {
|
||||
const existing = trackerState.get(url);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
const entry = {
|
||||
consecutiveErrors: 0,
|
||||
lastErrorAt: 0,
|
||||
cooldownUntil: 0,
|
||||
};
|
||||
trackerState.set(url, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
function markTrackerSuccess(url) {
|
||||
const entry = getTrackerEntry(url);
|
||||
entry.consecutiveErrors = 0;
|
||||
entry.cooldownUntil = 0;
|
||||
}
|
||||
|
||||
function markTrackerError(url) {
|
||||
const entry = getTrackerEntry(url);
|
||||
const nowTs = now();
|
||||
if (entry.lastErrorAt && nowTs - entry.lastErrorAt < TRACKER_ERROR_COOLDOWN_MS) {
|
||||
entry.consecutiveErrors += 1;
|
||||
} else {
|
||||
entry.consecutiveErrors = 1;
|
||||
}
|
||||
entry.lastErrorAt = nowTs;
|
||||
if (entry.consecutiveErrors >= 2) {
|
||||
entry.cooldownUntil = nowTs + TRACKER_ERROR_COOLDOWN_MS;
|
||||
}
|
||||
}
|
||||
|
||||
function isTrackerUsable(url) {
|
||||
const entry = getTrackerEntry(url);
|
||||
return entry.cooldownUntil === 0 || entry.cooldownUntil <= now();
|
||||
}
|
||||
|
||||
export async function trackerPing(magnet, trackers) {
|
||||
const infoHash = infoHashFromMagnet(magnet);
|
||||
if (!infoHash) {
|
||||
return getDefaultHealth();
|
||||
}
|
||||
|
||||
const magnetTrackers = trackers || trackersFromMagnet(magnet);
|
||||
const announceList = resolveTrackerList({ magnetTrackers });
|
||||
const usable = announceList.filter(isTrackerUsable);
|
||||
const announces = usable.length ? usable : announceList;
|
||||
|
||||
if (!announces.length) {
|
||||
return getDefaultHealth();
|
||||
}
|
||||
|
||||
const peerId = randomPeerId();
|
||||
const result = getDefaultHealth();
|
||||
const clients = new Set();
|
||||
let settled = false;
|
||||
let timeoutId = null;
|
||||
|
||||
const finalize = () => {
|
||||
if (settled) {
|
||||
return;
|
||||
}
|
||||
settled = true;
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
clients.forEach((client) => {
|
||||
try {
|
||||
client.destroy();
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
clients.clear();
|
||||
};
|
||||
|
||||
return new Promise((resolve) => {
|
||||
if (Number.isFinite(TRACKER_TIMEOUT_MS) && TRACKER_TIMEOUT_MS > 0) {
|
||||
timeoutId = setTimeout(() => {
|
||||
finalize();
|
||||
resolve(result);
|
||||
}, TRACKER_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
let remaining = announces.length;
|
||||
|
||||
const handleComplete = (client, url) => {
|
||||
if (clients.has(client)) {
|
||||
clients.delete(client);
|
||||
}
|
||||
if (url && result.from.includes(url) && result.ok) {
|
||||
markTrackerSuccess(url);
|
||||
}
|
||||
remaining -= 1;
|
||||
if (remaining <= 0 && !settled) {
|
||||
finalize();
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResult = (url, data) => {
|
||||
result.responded = true;
|
||||
if (url && !result.from.includes(url)) {
|
||||
result.from.push(url);
|
||||
}
|
||||
const seeders = Number.isFinite(data?.complete)
|
||||
? Number(data.complete)
|
||||
: 0;
|
||||
const leechers = Number.isFinite(data?.incomplete)
|
||||
? Number(data.incomplete)
|
||||
: 0;
|
||||
if (seeders > result.seeders) {
|
||||
result.seeders = seeders;
|
||||
}
|
||||
if (leechers > result.leechers) {
|
||||
result.leechers = leechers;
|
||||
}
|
||||
if (seeders > 0) {
|
||||
result.ok = true;
|
||||
}
|
||||
};
|
||||
|
||||
announces.forEach((url) => {
|
||||
let client;
|
||||
try {
|
||||
client = new Client({
|
||||
infoHash,
|
||||
peerId,
|
||||
announce: [url],
|
||||
});
|
||||
} catch (err) {
|
||||
markTrackerError(url);
|
||||
remaining -= 1;
|
||||
if (remaining <= 0 && !settled) {
|
||||
finalize();
|
||||
resolve(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
clients.add(client);
|
||||
|
||||
const cleanupAndResolve = () => {
|
||||
if (settled) {
|
||||
return;
|
||||
}
|
||||
finalize();
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
client.once("update", (data) => {
|
||||
handleResult(url, data);
|
||||
markTrackerSuccess(url);
|
||||
if (result.ok) {
|
||||
cleanupAndResolve();
|
||||
return;
|
||||
}
|
||||
handleComplete(client, url);
|
||||
});
|
||||
|
||||
client.once("error", () => {
|
||||
markTrackerError(url);
|
||||
handleComplete(client, url);
|
||||
});
|
||||
|
||||
client.once("warning", () => {
|
||||
handleComplete(client, url);
|
||||
});
|
||||
|
||||
try {
|
||||
client.start();
|
||||
} catch (err) {
|
||||
markTrackerError(url);
|
||||
handleComplete(client, url);
|
||||
}
|
||||
});
|
||||
|
||||
if (announces.length === 0) {
|
||||
finalize();
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Generated
+13
-1265
File diff suppressed because it is too large.
Load diff
@@ -36,11 +36,9 @@
|
||||
"@playwright/test": "^1.58.0",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"autoprefixer": "^10.4.23",
|
||||
"c8": "^10.1.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"esbuild": "^0.25.12",
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
"http-server": "^14.1.1",
|
||||
"nostr-tools": "^2.19.4",
|
||||
"pixelmatch": "^5.3.0",
|
||||
"playwright": "^1.58.0",
|
||||
@@ -56,9 +54,7 @@
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.4",
|
||||
"@noble/hashes": "^2.0.1",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jsdom": "^27.4.0",
|
||||
"marked": "^17.0.1",
|
||||
"ws": "^8.19.0"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user