pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Revert "trakt stuff"

This reverts commit f6e9f2be67182e28acb2e76027fc4ed6eb280c17.

Pas 054612b9 f6e9f2be

+11 -218
-1
example.env
··· 1 1 VITE_TMDB_READ_API_KEY=... 2 2 VITE_OPENSEARCH_ENABLED=false 3 - VITE_ENABLE_TRAKT=false 4 3 5 4 # make sure the cors proxy url does NOT have a slash at the end 6 5 VITE_CORS_PROXY_URL=...
+11 -213
src/backend/metadata/traktApi.ts
··· 1 - import { conf } from "@/setup/config"; 2 1 import { SimpleCache } from "@/utils/cache"; 3 - import { getTurnstileToken } from "@/utils/turnstile"; 4 2 5 3 import { getMediaDetails } from "./tmdb"; 6 4 import { TMDBContentTypes, TMDBMovieData } from "./types/tmdb"; ··· 51 49 traktCache.setCompare((a, b) => a.endpoint === b.endpoint); 52 50 traktCache.initialize(); 53 51 54 - // Authentication state - only track concurrent requests 55 - let isAuthenticating = false; 56 - let authToken: string | null = null; 57 - let tokenExpiry: Date | null = null; 58 - 59 - /** 60 - * Clears the authentication token 61 - */ 62 - function clearAuthToken(): void { 63 - authToken = null; 64 - tokenExpiry = null; 65 - localStorage.removeItem("trakt_auth_token"); 66 - localStorage.removeItem("trakt_token_expiry"); 67 - } 68 - 69 - /** 70 - * Stores the authentication token in memory and localStorage 71 - */ 72 - function storeAuthToken(token: string, expiresAt: string): void { 73 - const expiryDate = new Date(expiresAt); 74 - if (Number.isNaN(expiryDate.getTime())) { 75 - console.error("Invalid expiry date format:", expiresAt); 76 - return; 77 - } 78 - 79 - authToken = token; 80 - tokenExpiry = expiryDate; 81 - 82 - // Store in localStorage for persistence 83 - localStorage.setItem("trakt_auth_token", token); 84 - localStorage.setItem("trakt_token_expiry", expiresAt); 85 - } 86 - 87 - /** 88 - * Checks if user is authenticated by checking token validity 89 - */ 90 - function isAuthenticated(): boolean { 91 - // Check memory first 92 - if (authToken && tokenExpiry && tokenExpiry > new Date()) { 93 - return true; 94 - } 95 - 96 - // Check localStorage 97 - const storedToken = localStorage.getItem("trakt_auth_token"); 98 - const storedExpiry = localStorage.getItem("trakt_token_expiry"); 99 - 100 - if (storedToken && storedExpiry) { 101 - const expiryDate = new Date(storedExpiry); 102 - if (expiryDate > new Date()) { 103 - authToken = storedToken; 104 - tokenExpiry = expiryDate; 105 - return true; 106 - } 107 - // Token expired, clear it 108 - clearAuthToken(); 109 - } 110 - 111 - return false; 112 - } 113 - 114 - /** 115 - * Authenticates with the Trakt API using Cloudflare Turnstile 116 - * Stores the auth token for use in API requests 117 - */ 118 - async function authenticateWithTurnstile(): Promise<void> { 119 - // Prevent concurrent authentication attempts 120 - if (isAuthenticating) { 121 - // Wait for existing authentication to complete 122 - await new Promise<void>((resolve) => { 123 - const checkAuth = () => { 124 - if (!isAuthenticating) { 125 - resolve(); 126 - } else { 127 - setTimeout(checkAuth, 100); 128 - } 129 - }; 130 - checkAuth(); 131 - }); 132 - return; 133 - } 134 - 135 - isAuthenticating = true; 136 - 137 - try { 138 - const turnstileToken = await getTurnstileToken("0x4AAAAAAB6ocCCpurfWRZyC"); 139 - 140 - // Authenticate with the API 141 - const response = await fetch(`${TRAKT_BASE_URL}/auth`, { 142 - method: "POST", 143 - headers: { 144 - "Content-Type": "application/json", 145 - }, 146 - body: JSON.stringify({ 147 - token: turnstileToken, 148 - }), 149 - }); 150 - 151 - if (!response.ok) { 152 - throw new Error(`Authentication failed: ${response.statusText}`); 153 - } 154 - 155 - const result = await response.json(); 156 - 157 - if (!result.success) { 158 - throw new Error(result.message || "Authentication failed"); 159 - } 160 - 161 - // Store the auth token 162 - storeAuthToken(result.auth_token, result.expires_at); 163 - } finally { 164 - isAuthenticating = false; 165 - } 166 - } 167 - 168 52 // Base function to fetch from Trakt API 169 53 async function fetchFromTrakt<T = TraktListResponse>( 170 54 endpoint: string, 171 55 ): Promise<T> { 172 - // Check if Trakt is enabled 173 - if (!conf().ENABLE_TRAKT) { 174 - throw new Error("Trakt API is not enabled, using tmdb lists instead."); 175 - } 176 - 177 56 // Check cache first 178 57 const cacheKey: TraktCacheKey = { endpoint }; 179 58 const cachedResult = traktCache.get(cacheKey); ··· 181 60 return cachedResult as T; 182 61 } 183 62 184 - // Ensure we're authenticated 185 - if (!isAuthenticated()) { 186 - await authenticateWithTurnstile(); 187 - } 188 - 189 - // Make the API request with authorization header 190 - const headers: Record<string, string> = {}; 191 - if (authToken) { 192 - headers.Authorization = `Bearer ${authToken}`; 193 - } 194 - 195 - let response = await fetch(`${TRAKT_BASE_URL}${endpoint}`, { 196 - headers, 197 - }); 198 - 199 - // If request fails, try re-authenticating and retry once 63 + // Make the API request 64 + const response = await fetch(`${TRAKT_BASE_URL}${endpoint}`); 200 65 if (!response.ok) { 201 - // If 401, clear token and re-authenticate 202 - if (response.status === 401) { 203 - clearAuthToken(); 204 - } 205 - 206 - // Re-authenticate and retry 207 - await authenticateWithTurnstile(); 208 - 209 - // Rebuild headers after re-authentication 210 - const retryHeaders: Record<string, string> = {}; 211 - if (authToken) { 212 - retryHeaders.Authorization = `Bearer ${authToken}`; 213 - } 214 - 215 - response = await fetch(`${TRAKT_BASE_URL}${endpoint}`, { 216 - headers: retryHeaders, 217 - }); 218 - 219 - // If retry also fails, throw error 220 - if (!response.ok) { 221 - throw new Error( 222 - `Failed to fetch from ${endpoint}: ${response.statusText}`, 223 - ); 224 - } 66 + throw new Error(`Failed to fetch from ${endpoint}: ${response.statusText}`); 225 67 } 226 - 227 68 const result = await response.json(); 228 69 229 70 // Cache the result for 1 hour (3600 seconds) ··· 243 84 url += `/${season}/${episode}`; 244 85 } 245 86 246 - // Check if Trakt is enabled 247 - if (!conf().ENABLE_TRAKT) { 248 - throw new Error("Trakt API is not enabled"); 249 - } 250 - 251 87 // Check cache first 252 88 const cacheKey: TraktCacheKey = { endpoint: url }; 253 89 const cachedResult = traktCache.get(cacheKey); ··· 255 91 return cachedResult as TraktReleaseResponse; 256 92 } 257 93 258 - // Ensure we're authenticated 259 - if (!isAuthenticated()) { 260 - await authenticateWithTurnstile(); 261 - } 262 - 263 - // Make the API request with authorization header 264 - const headers: Record<string, string> = {}; 265 - if (authToken) { 266 - headers.Authorization = `Bearer ${authToken}`; 267 - } 268 - 269 - let response = await fetch(`${TRAKT_BASE_URL}${url}`, { 270 - headers, 271 - }); 272 - 273 - // If request fails, try re-authenticating and retry once 94 + // Make the API request 95 + const response = await fetch(`${TRAKT_BASE_URL}${url}`); 274 96 if (!response.ok) { 275 - // If 401, clear token and re-authenticate 276 - if (response.status === 401) { 277 - clearAuthToken(); 278 - } 279 - 280 - // Re-authenticate and retry 281 - await authenticateWithTurnstile(); 282 - 283 - // Rebuild headers after re-authentication 284 - const retryHeaders: Record<string, string> = {}; 285 - if (authToken) { 286 - retryHeaders.Authorization = `Bearer ${authToken}`; 287 - } 288 - 289 - response = await fetch(`${TRAKT_BASE_URL}${url}`, { 290 - headers: retryHeaders, 291 - }); 292 - 293 - // If retry also fails, throw error 294 - if (!response.ok) { 295 - throw new Error( 296 - `Failed to fetch release details: ${response.statusText}`, 297 - ); 298 - } 97 + throw new Error(`Failed to fetch release details: ${response.statusText}`); 299 98 } 300 - 301 99 const result = await response.json(); 302 100 303 101 // Cache the result for 1 hour (3600 seconds) ··· 402 200 403 201 const lists: CuratedMovieList[] = []; 404 202 405 - for (const listConfig of listConfigs) { 203 + for (const config of listConfigs) { 406 204 try { 407 - const response = await fetchFromTrakt(listConfig.endpoint); 205 + const response = await fetchFromTrakt(config.endpoint); 408 206 lists.push({ 409 - listName: listConfig.name, 410 - listSlug: listConfig.slug, 207 + listName: config.name, 208 + listSlug: config.slug, 411 209 tmdbIds: response.movie_tmdb_ids.slice(0, 30), // Limit to first 30 items 412 210 count: Math.min(response.movie_tmdb_ids.length, 30), // Update count to reflect the limit 413 211 }); 414 212 } catch (error) { 415 - console.error(`Failed to fetch ${listConfig.name}:`, error); 213 + console.error(`Failed to fetch ${config.name}:`, error); 416 214 } 417 215 } 418 216
-4
src/setup/config.ts
··· 19 19 BACKEND_URL: string; 20 20 DISALLOWED_IDS: string; 21 21 TURNSTILE_KEY: string; 22 - ENABLE_TRAKT: string; 23 22 CDN_REPLACEMENTS: string; 24 23 HAS_ONBOARDING: string; 25 24 ONBOARDING_CHROME_EXTENSION_INSTALL_LINK: string; ··· 49 48 BACKEND_URL: string | null; 50 49 DISALLOWED_IDS: string[]; 51 50 TURNSTILE_KEY: string | null; 52 - ENABLE_TRAKT: boolean; 53 51 CDN_REPLACEMENTS: Array<string[]>; 54 52 HAS_ONBOARDING: boolean; 55 53 ALLOW_AUTOPLAY: boolean; ··· 83 81 BACKEND_URL: import.meta.env.VITE_BACKEND_URL, 84 82 DISALLOWED_IDS: import.meta.env.VITE_DISALLOWED_IDS, 85 83 TURNSTILE_KEY: import.meta.env.VITE_TURNSTILE_KEY, 86 - ENABLE_TRAKT: import.meta.env.VITE_ENABLE_TRAKT, 87 84 CDN_REPLACEMENTS: import.meta.env.VITE_CDN_REPLACEMENTS, 88 85 HAS_ONBOARDING: import.meta.env.VITE_HAS_ONBOARDING, 89 86 ALLOW_AUTOPLAY: import.meta.env.VITE_ALLOW_AUTOPLAY, ··· 145 142 HAS_ONBOARDING: getKey("HAS_ONBOARDING", "false") === "true", 146 143 ALLOW_AUTOPLAY: getKey("ALLOW_AUTOPLAY", "false") === "true", 147 144 TURNSTILE_KEY: getKey("TURNSTILE_KEY"), 148 - ENABLE_TRAKT: getKey("ENABLE_TRAKT", "false") === "true", 149 145 DISALLOWED_IDS: getKey("DISALLOWED_IDS", "") 150 146 .split(",") 151 147 .map((v) => v.trim())