diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx
index 4080fae..21b4d3c 100644
--- a/src/app/admin/page.tsx
+++ b/src/app/admin/page.tsx
@@ -3430,6 +3430,7 @@ const EmbyConfigComponent = ({
removeEmbyPrefix: false,
appendMediaSourceId: false,
transcodeMp4: false,
+ proxyPlay: false,
});
// 从配置加载源列表
@@ -3468,6 +3469,7 @@ const EmbyConfigComponent = ({
removeEmbyPrefix: false,
appendMediaSourceId: false,
transcodeMp4: false,
+ proxyPlay: false,
});
setEditingSource(null);
setShowAddForm(false);
@@ -3901,25 +3903,6 @@ const EmbyConfigComponent = ({
- {/* 默认源开关 */}
-
-
-
-
-
{/* 高级选项 */}
@@ -3997,6 +3980,30 @@ const EmbyConfigComponent = ({
/>
+
+ {/* 视频播放代理开关 */}
+
+
+
+ 视频播放代理
+
+
+ 启用后视频播放将通过服务器代理
+
+
+
+
{/* 操作按钮 */}
diff --git a/src/app/api/emby/play/[token]/[filename]/route.ts b/src/app/api/emby/play/[token]/[filename]/route.ts
index e0c8362..7518487 100644
--- a/src/app/api/emby/play/[token]/[filename]/route.ts
+++ b/src/app/api/emby/play/[token]/[filename]/route.ts
@@ -60,8 +60,8 @@ export async function GET(
// 获取 Emby 客户端
let client = await getEmbyClient(embyKey);
- // 构建 Emby 原始播放链接
- let embyStreamUrl = await client.getStreamUrl(itemId);
+ // 构建 Emby 原始播放链接(强制获取直接URL,避免代理循环)
+ let embyStreamUrl = await client.getStreamUrl(itemId, true, true);
// 构建请求头,转发 Range 请求
const requestHeaders: HeadersInit = {};
@@ -81,7 +81,7 @@ export async function GET(
const { embyManager } = await import('@/lib/emby-manager');
embyManager.clearCache();
client = await getEmbyClient(embyKey);
- embyStreamUrl = await client.getStreamUrl(itemId);
+ embyStreamUrl = await client.getStreamUrl(itemId, true, true);
videoResponse = await fetch(embyStreamUrl, {
headers: requestHeaders,
});
diff --git a/src/lib/admin.types.ts b/src/lib/admin.types.ts
index e901fd7..6e2c32f 100644
--- a/src/lib/admin.types.ts
+++ b/src/lib/admin.types.ts
@@ -178,6 +178,7 @@ export interface AdminConfig {
removeEmbyPrefix?: boolean; // 播放链接移除/emby前缀
appendMediaSourceId?: boolean; // 拼接MediaSourceId参数
transcodeMp4?: boolean; // 转码mp4
+ proxyPlay?: boolean; // 视频播放代理开关
}>;
// 旧格式:单源配置(向后兼容)
Enabled?: boolean;
diff --git a/src/lib/emby-manager.ts b/src/lib/emby-manager.ts
index f00df99..548a140 100644
--- a/src/lib/emby-manager.ts
+++ b/src/lib/emby-manager.ts
@@ -22,6 +22,7 @@ interface EmbySourceConfig {
removeEmbyPrefix?: boolean;
appendMediaSourceId?: boolean;
transcodeMp4?: boolean;
+ proxyPlay?: boolean; // 视频播放代理开关
}
class EmbyManager {
@@ -182,6 +183,7 @@ export function migrateEmbyConfig(config: AdminConfig): AdminConfig {
removeEmbyPrefix: false,
appendMediaSourceId: false,
transcodeMp4: false,
+ proxyPlay: false,
}],
};
}
diff --git a/src/lib/emby.client.ts b/src/lib/emby.client.ts
index 52e4f22..dbde767 100644
--- a/src/lib/emby.client.ts
+++ b/src/lib/emby.client.ts
@@ -11,6 +11,8 @@ interface EmbyConfig {
removeEmbyPrefix?: boolean;
appendMediaSourceId?: boolean;
transcodeMp4?: boolean;
+ proxyPlay?: boolean; // 视频播放代理开关
+ key?: string; // Emby源的唯一标识
}
interface EmbyItem {
@@ -71,6 +73,8 @@ export class EmbyClient {
private removeEmbyPrefix: boolean;
private appendMediaSourceId: boolean;
private transcodeMp4: boolean;
+ private proxyPlay: boolean;
+ private embyKey?: string;
constructor(config: EmbyConfig) {
let serverUrl = config.ServerURL.replace(/\/$/, '');
@@ -79,6 +83,8 @@ export class EmbyClient {
this.removeEmbyPrefix = config.removeEmbyPrefix || false;
this.appendMediaSourceId = config.appendMediaSourceId || false;
this.transcodeMp4 = config.transcodeMp4 || false;
+ this.proxyPlay = config.proxyPlay || false;
+ this.embyKey = config.key;
// 如果 URL 不包含 /emby 路径,自动添加(除非启用了 removeEmbyPrefix)
if (!serverUrl.endsWith('/emby') && !this.removeEmbyPrefix) {
@@ -461,8 +467,27 @@ export class EmbyClient {
}
}
- async getStreamUrl(itemId: string, direct: boolean = true): Promise {
+ async getStreamUrl(itemId: string, direct: boolean = true, forceDirectUrl: boolean = false): Promise {
const token = this.apiKey || this.authToken;
+
+ // 如果启用了代理播放且不是强制获取直接URL,返回代理URL
+ if (this.proxyPlay && !forceDirectUrl) {
+ // 使用固定的token占位符,实际验证在服务端进行
+ const subscribeToken = 'proxy';
+ const filename = this.transcodeMp4 ? 'video.mp4' : 'video';
+
+ // 构建代理URL(相对路径)
+ let proxyUrl = `/api/emby/play/${subscribeToken}/${filename}?itemId=${itemId}`;
+
+ // 如果有embyKey,添加到查询参数
+ if (this.embyKey) {
+ proxyUrl += `&embyKey=${this.embyKey}`;
+ }
+
+ return proxyUrl;
+ }
+
+ // 原有的直接播放逻辑
let url: string;
if (direct) {