This commit is contained in:
Neil.X.Zhang
2025-07-02 09:09:35 +08:00
parent 3b79d06b7d
commit 05ef835d5d
86 changed files with 2440 additions and 8770 deletions

View File

@@ -1,4 +1,3 @@
// MoonTV API 服务
export interface DoubanItem {
title: string;
poster: string;
@@ -32,7 +31,7 @@ export interface VideoDetail {
}
export interface SearchResult {
id: string;
id: number;
title: string;
poster: string;
episodes: string[];
@@ -57,17 +56,8 @@ export interface PlayRecord {
user_id: number; // User ID, always 0 in this version
}
export class MoonTVAPI {
private baseURL: string;
constructor(baseURL: string) {
if (!baseURL) {
console.warn(
"MoonTVAPI base URL not set. Please configure it for your network."
);
}
this.baseURL = baseURL;
}
export class API {
private baseURL: string = "https://orion-tv.vercel.app";
/**
* 生成图片代理 URL
@@ -160,7 +150,7 @@ export class MoonTVAPI {
}
// 默认实例
export const moonTVApi = new MoonTVAPI();
export const moonTVApi = new API();
// 生成模拟数据的辅助函数
export const generateMockDoubanData = (count: number = 20): DoubanItem[] => {
@@ -202,7 +192,7 @@ export const generateMockSearchResults = (
count: number = 20
): SearchResult[] => {
return Array.from({ length: count }, (_, index) => ({
id: `${index + 1}`,
id: index + 1,
title: `搜索结果:${query} ${index + 1}`,
poster: `https://picsum.photos/160/240?random=${index + 100}`,
episodes: [`第1集`, `第2集`, `第3集`],

55
services/m3u8.ts Normal file
View File

@@ -0,0 +1,55 @@
interface CacheEntry {
resolution: string | null;
timestamp: number;
}
const resolutionCache: { [url: string]: CacheEntry } = {};
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
export const getResolutionFromM3U8 = async (
url: string
): Promise<string | null> => {
// 1. Check cache first
const cachedEntry = resolutionCache[url];
if (cachedEntry && Date.now() - cachedEntry.timestamp < CACHE_DURATION) {
return cachedEntry.resolution;
}
if (!url.toLowerCase().endsWith(".m3u8")) {
return null;
}
try {
const response = await fetch(url);
if (!response.ok) {
return null;
}
const playlist = await response.text();
const lines = playlist.split("\n");
let highestResolution = 0;
let resolutionString: string | null = null;
for (const line of lines) {
if (line.startsWith("#EXT-X-STREAM-INF")) {
const resolutionMatch = line.match(/RESOLUTION=(\d+)x(\d+)/);
if (resolutionMatch) {
const height = parseInt(resolutionMatch[2], 10);
if (height > highestResolution) {
highestResolution = height;
resolutionString = `${height}p`;
}
}
}
}
// 2. Store result in cache
resolutionCache[url] = {
resolution: resolutionString,
timestamp: Date.now(),
};
return resolutionString;
} catch (error) {
return null;
}
};