56 lines
1.4 KiB
TypeScript
56 lines
1.4 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
|
|
const IPTV_ORG_URL = 'https://iptv-org.github.io/api/channels.json'
|
|
|
|
interface IptvChannel {
|
|
id: string
|
|
name: string
|
|
country: string
|
|
categories: string[]
|
|
is_nsfw: boolean
|
|
is_closed: boolean
|
|
}
|
|
|
|
interface SlimChannel {
|
|
id: string
|
|
name: string
|
|
country: string
|
|
categories: string[]
|
|
}
|
|
|
|
let cache: { data: SlimChannel[]; fetchedAt: number } | null = null
|
|
const CACHE_TTL = 60 * 60 * 1000 // 1 hour
|
|
|
|
export async function GET() {
|
|
try {
|
|
if (cache && Date.now() - cache.fetchedAt < CACHE_TTL) {
|
|
return NextResponse.json(cache.data)
|
|
}
|
|
|
|
const res = await fetch(IPTV_ORG_URL, { next: { revalidate: 3600 } })
|
|
if (!res.ok) {
|
|
throw new Error(`iptv-org API returned ${res.status}`)
|
|
}
|
|
|
|
const channels: IptvChannel[] = await res.json()
|
|
|
|
const filtered: SlimChannel[] = channels
|
|
.filter((ch) => !ch.is_nsfw && !ch.is_closed)
|
|
.map(({ id, name, country, categories }) => ({ id, name, country, categories }))
|
|
|
|
cache = { data: filtered, fetchedAt: Date.now() }
|
|
|
|
return NextResponse.json(filtered)
|
|
} catch (error) {
|
|
console.error('Failed to fetch channels:', error)
|
|
// Return cached data even if stale on error
|
|
if (cache) {
|
|
return NextResponse.json(cache.data)
|
|
}
|
|
return NextResponse.json(
|
|
{ error: 'Failed to load channel list' },
|
|
{ status: 502 }
|
|
)
|
|
}
|
|
}
|