| | |
| | |
| | |
| | |
| | |
| |
|
| | import { fetchUrbanProxy } from "./urbanProxy.ts"; |
| |
|
| | |
| | const API_BASE = "https://antpeak.com"; |
| | const USER_AGENT = |
| | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"; |
| | const APP_VERSION = "3.7.8"; |
| | const YOUTUBE_TEST_URL = "https://www.youtube.com/watch?v=bzbsJGMVHxQ"; |
| | const CUSTOM_PROXY_URL = "https://ytdlp-api-gbdn.onrender.com/proxies"; |
| |
|
| | |
| | interface DeviceInfo { |
| | udid: string; |
| | appVersion: string; |
| | platform: string; |
| | platformVersion: string; |
| | timeZone: string; |
| | deviceName: string; |
| | } |
| |
|
| | interface Location { |
| | id: string; |
| | region: string; |
| | name: string; |
| | countryCode: string; |
| | type: number; |
| | proxyType: number; |
| | } |
| |
|
| | interface ProxyServer { |
| | addresses: string[]; |
| | protocol: string; |
| | port: number; |
| | username?: string; |
| | password?: string; |
| | } |
| |
|
| | |
| | let currentProxyUrl: string | null = null; |
| | let accessToken: string | null = null; |
| | let freeLocations: Location[] = []; |
| | let isInitialized = false; |
| | let initializationPromise: Promise<void> | null = null; |
| | let vpnSource = 1; |
| |
|
| | |
| |
|
| | async function fetchJson( |
| | endpoint: string, |
| | method: string, |
| | body?: unknown, |
| | token?: string, |
| | ): Promise<unknown> { |
| | const url = `${API_BASE}${endpoint}`; |
| | const headers: Record<string, string> = { |
| | "User-Agent": USER_AGENT, |
| | "Content-Type": "application/json", |
| | "Accept": "application/json", |
| | }; |
| |
|
| | if (token) { |
| | headers["Authorization"] = `Bearer ${token}`; |
| | } |
| |
|
| | const response = await fetch(url, { |
| | method, |
| | headers, |
| | body: body ? JSON.stringify(body) : undefined, |
| | }); |
| |
|
| | if (!response.ok) { |
| | const text = await response.text(); |
| | throw new Error(`API Error ${response.status}: ${text}`); |
| | } |
| |
|
| | return await response.json(); |
| | } |
| |
|
| | async function testProxyAgainstYouTube(proxyUrl: string): Promise<number | boolean> { |
| | try { |
| | const proxyUrlObj = new URL(proxyUrl); |
| | const clientOptions: Deno.CreateHttpClientOptions = {}; |
| |
|
| | if (proxyUrlObj.username && proxyUrlObj.password) { |
| | clientOptions.proxy = { |
| | url: `${proxyUrlObj.protocol}//${proxyUrlObj.host}`, |
| | basicAuth: { |
| | username: decodeURIComponent(proxyUrlObj.username), |
| | password: decodeURIComponent(proxyUrlObj.password), |
| | }, |
| | }; |
| | } else { |
| | clientOptions.proxy = { |
| | url: proxyUrl, |
| | }; |
| | } |
| |
|
| | const client = Deno.createHttpClient(clientOptions); |
| |
|
| | const response = await fetch(YOUTUBE_TEST_URL, { |
| | client, |
| | signal: AbortSignal.timeout(15000), |
| | headers: { |
| | "User-Agent": USER_AGENT, |
| | }, |
| | }); |
| |
|
| | client.close(); |
| |
|
| | |
| | if (response.ok || (response.status >= 300 && response.status < 400)) { |
| | return true; |
| | } |
| | return response.status; |
| | } catch (err) { |
| | |
| | return false; |
| | } |
| | } |
| |
|
| | async function registerDevice(): Promise<string> { |
| | const deviceInfo: DeviceInfo = { |
| | udid: crypto.randomUUID(), |
| | appVersion: APP_VERSION, |
| | platform: "chrome", |
| | platformVersion: USER_AGENT, |
| | timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC", |
| | deviceName: "Chrome 120.0.0.0", |
| | }; |
| |
|
| | const launchResponse = await fetchJson( |
| | "/api/launch/", |
| | "POST", |
| | deviceInfo, |
| | ) as { |
| | success: boolean; |
| | data?: { accessToken: string }; |
| | }; |
| |
|
| | if (!launchResponse.success || !launchResponse.data?.accessToken) { |
| | throw new Error("Failed to register device with antpeak.com"); |
| | } |
| |
|
| | return launchResponse.data.accessToken; |
| | } |
| |
|
| | async function fetchLocations(token: string): Promise<Location[]> { |
| | const locationsResponse = await fetchJson( |
| | "/api/location/list/", |
| | "POST", |
| | undefined, |
| | token, |
| | ) as { |
| | success: boolean; |
| | data?: { locations: Location[] }; |
| | }; |
| |
|
| | if (!locationsResponse.success || !locationsResponse.data?.locations) { |
| | throw new Error("Failed to fetch locations from antpeak.com"); |
| | } |
| |
|
| | |
| | return locationsResponse.data.locations.filter((l) => l.proxyType === 0); |
| | } |
| |
|
| | async function fetchProxyServer( |
| | token: string, |
| | location: Location, |
| | ): Promise<string | null> { |
| | const serverPayload = { |
| | protocol: "https", |
| | region: location.region, |
| | type: location.type, |
| | }; |
| |
|
| | const serverResponse = await fetchJson( |
| | "/api/server/list/", |
| | "POST", |
| | serverPayload, |
| | token, |
| | ) as { |
| | success: boolean; |
| | data?: ProxyServer[]; |
| | }; |
| |
|
| | if ( |
| | !serverResponse.success || |
| | !Array.isArray(serverResponse.data) || |
| | serverResponse.data.length === 0 |
| | ) { |
| | return null; |
| | } |
| |
|
| | const server = serverResponse.data[0]; |
| | const ip = server.addresses[0]; |
| | const port = server.port; |
| | const username = server.username || ""; |
| | const password = server.password || ""; |
| |
|
| | if (!username) { |
| | return `https://${ip}:${port}`; |
| | } else { |
| | return `https://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${ip}:${port}`; |
| | } |
| | } |
| |
|
| | |
| |
|
| | |
| | |
| | |
| | |
| | export async function initProxyManager(source: number = 1): Promise<void> { |
| | vpnSource = source; |
| | if (isInitialized) return; |
| |
|
| | if (initializationPromise) { |
| | return initializationPromise; |
| | } |
| |
|
| | initializationPromise = (async () => { |
| | console.log("[ProxyManager] Initializing automatic proxy manager..."); |
| |
|
| | try { |
| | if (vpnSource === 1) { |
| | accessToken = await registerDevice(); |
| | console.log("[ProxyManager] β
Registered with antpeak.com"); |
| |
|
| | freeLocations = await fetchLocations(accessToken); |
| | console.log( |
| | `[ProxyManager] β
Found ${freeLocations.length} free locations`, |
| | ); |
| |
|
| | if (freeLocations.length === 0) { |
| | throw new Error("No free proxy locations available"); |
| | } |
| | } else if (vpnSource === 2) { |
| | console.log("[ProxyManager] Using Urban VPN source"); |
| | } else if (vpnSource === 3) { |
| | console.log("[ProxyManager] Using Custom Proxy API source"); |
| | } |
| |
|
| | |
| | await rotateProxy(); |
| |
|
| | isInitialized = true; |
| | console.log("[ProxyManager] β
Initialization complete"); |
| | } catch (err) { |
| | console.error("[ProxyManager] β Initialization failed:", err); |
| | throw err; |
| | } |
| | })(); |
| |
|
| | return initializationPromise; |
| | } |
| |
|
| | |
| | |
| | |
| | export function getCurrentProxy(): string | null { |
| | return currentProxyUrl; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | export async function rotateProxy(): Promise<string | null> { |
| | console.log(`[ProxyManager] Rotation requested. Source: ${vpnSource}`); |
| |
|
| | if (vpnSource === 2) { |
| | |
| | try { |
| | const urbanResult = await fetchUrbanProxy(); |
| | if (urbanResult) { |
| | console.log(`[ProxyManager] Testing Urban proxy against YouTube...`); |
| | const result = await testProxyAgainstYouTube(urbanResult.url); |
| | if (result === true) { |
| | currentProxyUrl = urbanResult.url; |
| | console.log(`[ProxyManager] β
New Urban proxy active: ${urbanResult.host}`); |
| | return currentProxyUrl; |
| | } else { |
| | console.log(`[ProxyManager] β Urban proxy failed YouTube test`); |
| | } |
| | } |
| | } catch (err) { |
| | console.error("[ProxyManager] Failed to fetch/test Urban proxy", err); |
| | } |
| | console.error("[ProxyManager] β Could not find a working Urban proxy"); |
| | currentProxyUrl = null; |
| | return null; |
| | } |
| |
|
| | if (vpnSource === 3) { |
| | |
| | console.log("[ProxyManager] Fetching proxies from custom API..."); |
| |
|
| | let attempts = 0; |
| | const maxAttempts = 10; |
| |
|
| | while (attempts < maxAttempts) { |
| | try { |
| | const response = await fetch(CUSTOM_PROXY_URL); |
| | if (!response.ok) { |
| | throw new Error(`Failed to fetch proxies: ${response.statusText}`); |
| | } |
| | const data = await response.json() as { proxies: string[] }; |
| |
|
| | if (!data.proxies || !Array.isArray(data.proxies) || data.proxies.length === 0) { |
| | console.log("[ProxyManager] No proxies returned from API, retrying..."); |
| | attempts++; |
| | continue; |
| | } |
| |
|
| | console.log(`[ProxyManager] Got ${data.proxies.length} proxies from API. Testing...`); |
| |
|
| | for (const proxy of data.proxies) { |
| | console.log(`[ProxyManager] Testing ${proxy}...`); |
| | const result = await testProxyAgainstYouTube(proxy); |
| |
|
| | if (result === true) { |
| | currentProxyUrl = proxy; |
| | console.log(`[ProxyManager] β
New custom proxy active: ${proxy}`); |
| | return currentProxyUrl; |
| | } else if (typeof result === 'number') { |
| | console.log(`[ProxyManager] β Proxy returned status ${result}, trying next...`); |
| | } else { |
| | console.log(`[ProxyManager] β Proxy unreachable, trying next...`); |
| | } |
| | } |
| |
|
| | console.log("[ProxyManager] All proxies from this batch failed. Refetching..."); |
| | attempts++; |
| |
|
| | } catch (err) { |
| | console.error("[ProxyManager] Error fetching custom proxies:", err); |
| | attempts++; |
| | |
| | await new Promise(resolve => setTimeout(resolve, 2000)); |
| | } |
| | } |
| |
|
| | console.error("[ProxyManager] β Failed to find a working custom proxy after multiple attempts."); |
| | currentProxyUrl = null; |
| | return null; |
| | } |
| |
|
| | if (!accessToken || freeLocations.length === 0) { |
| | console.error( |
| | "[ProxyManager] Not initialized or no locations available", |
| | ); |
| | return null; |
| | } |
| |
|
| | |
| | if (!accessToken || freeLocations.length === 0) { |
| | console.error( |
| | "[ProxyManager] Not initialized or no locations available", |
| | ); |
| | return null; |
| | } |
| |
|
| | console.log("[ProxyManager] Rotating to new proxy (AntPeak)..."); |
| |
|
| | |
| | const shuffledLocations = [...freeLocations].sort(() => |
| | Math.random() - 0.5 |
| | ); |
| |
|
| | for (const location of shuffledLocations) { |
| | try { |
| | console.log( |
| | `[ProxyManager] Trying location: ${location.region} (${location.countryCode})`, |
| | ); |
| |
|
| | const proxyUrl = await fetchProxyServer(accessToken, location); |
| | if (!proxyUrl) { |
| | console.log( |
| | `[ProxyManager] No server available for ${location.region}`, |
| | ); |
| | continue; |
| | } |
| |
|
| | |
| | console.log(`[ProxyManager] Testing proxy against YouTube...`); |
| | const result = await testProxyAgainstYouTube(proxyUrl); |
| |
|
| | if (result === true) { |
| | currentProxyUrl = proxyUrl; |
| | |
| | const sanitizedUrl = proxyUrl.replace( |
| | /:\/\/[^@]+@/, |
| | "://***:***@", |
| | ); |
| | console.log( |
| | `[ProxyManager] β
New proxy active: ${sanitizedUrl}`, |
| | ); |
| | return currentProxyUrl; |
| | } else { |
| | console.log( |
| | `[ProxyManager] β Proxy failed YouTube test, trying next...`, |
| | ); |
| | } |
| | } catch (err) { |
| | console.error( |
| | `[ProxyManager] Error with location ${location.region}:`, |
| | err, |
| | ); |
| | } |
| | } |
| |
|
| | console.error("[ProxyManager] β Could not find a working proxy"); |
| | currentProxyUrl = null; |
| | return null; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | export async function markProxyFailed(): Promise<string | null> { |
| | console.log("[ProxyManager] Current proxy marked as failed, rotating..."); |
| | return await rotateProxy(); |
| | } |
| |
|
| | |
| | |
| | |
| | export function isProxyManagerReady(): boolean { |
| | return isInitialized && currentProxyUrl !== null; |
| | } |
| |
|
| | |
| | |
| | |
| | export async function refreshRegistration(): Promise<void> { |
| | console.log("[ProxyManager] Refreshing registration..."); |
| | try { |
| | accessToken = await registerDevice(); |
| | freeLocations = await fetchLocations(accessToken); |
| | console.log("[ProxyManager] β
Registration refreshed"); |
| | } catch (err) { |
| | console.error("[ProxyManager] β Failed to refresh registration:", err); |
| | throw err; |
| | } |
| | } |
| |
|