Spaces:
Paused
Paused
File size: 3,420 Bytes
e24a52f 94f96fe c22439c 4170f15 e24a52f 94f96fe e24a52f c22439c 94f96fe e24a52f 7102b00 e24a52f 94f96fe e24a52f c22439c 94f96fe 20f1d9d c22439c 6e879ea c22439c 6e879ea c22439c e24a52f c22439c e24a52f 94f96fe e24a52f 94f96fe c22439c 7d6a166 94f96fe 78a24ef c22439c 78a24ef c22439c 78a24ef c22439c 78a24ef c22439c 4170f15 78a24ef | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | import type { Browser, BrowserContext, Page } from 'playwright'
import { chromium } from 'playwright-extra'
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
import { store } from './storage.js'
import { logMemory } from './utils.js'
chromium.use(StealthPlugin())
const createContext = async (browser: Browser) => {
const context = await browser.newContext({
locale: 'en-US',
timezoneId: 'America/New_York',
bypassCSP: true,
deviceScaleFactor: 1,
})
await context.addCookies([
{
name: 'sp_dc',
value: process.env.SP_DC!,
domain: '.spotify.com',
path: '/',
httpOnly: true,
secure: true,
sameSite: 'None',
},
{
name: 'sp_key',
value: process.env.SP_KEY!,
domain: '.spotify.com',
path: '/',
httpOnly: false,
secure: true,
sameSite: 'None',
},
])
return context
}
export const createPageFromBrowser = async (browser: Browser) => {
const context = await createContext(browser)
const page = await context.newPage()
await page.route('**/*.{png,jpg,jpeg,gif,woff,woff2,sentry}', (r) => r.abort())
await page.route('**/*{onetrust,i.scdn.co/image/,mosaic.scdn.co/,encore.scdn.co/fonts}*', (r) => r.abort())
return page
}
let browserPromise: Promise<Browser> | null = null
export async function ensureBrowser(): Promise<Browser> {
if (browserPromise) {
const b = await browserPromise.catch(() => null)
if (!b || !b.isConnected()) {
browserPromise = null
store.browser = null
}
}
if (!browserPromise) {
browserPromise = (async () => {
const b = await chromium.launch({
headless: true,
args: ['--disable-dev-shm-usage', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu'],
})
b.on('disconnected', () => {
console.warn('Browser disconnected')
browserPromise = null
store.browser = null
})
return b
})()
}
const browser = await browserPromise
store.browser = browser
return browser
}
export async function restartBrowser() {
if (store.browser) {
await store.browser.close()
store.browser = null
}
return ensureBrowser()
}
export function handleError(error: unknown) {
const details = error instanceof Error ? error.message : 'Unknown error'
console.error('💥 Error:', details)
return details
}
export async function killBrowser(browser: Browser | null) {
console.log(`<- Closing browser`)
if (browser) await browser.close().catch(() => {})
}
export async function closeContexts(browser: Browser | null) {
console.log(`<- Closing contexts`)
if (browser) {
const contexts = browser.contexts()
for (const context of contexts) {
await context.close().catch(() => {})
}
}
}
export async function withPage<T>(fn: (page: Page) => Promise<T>): Promise<T | null> {
let page: Page | null = null
try {
const browser = await ensureBrowser()
page = await createPageFromBrowser(browser)
return await fn(page)
} catch (err) {
handleError(err)
return null
} finally {
if (page) {
await page
.context()
.close()
.catch(() => {})
}
logMemory('Context closed')
}
}
|