diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index e27b628..3f9373a 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -5278,6 +5278,7 @@ const EmbyConfigComponent = ({ transcodeMp4: false, proxyPlay: false, customUserAgent: '', + embyAuthorizationHeader: '', }); const [authMode, setAuthMode] = useState<'apikey' | 'password'>('apikey'); @@ -5297,6 +5298,7 @@ const EmbyConfigComponent = ({ Username: config.EmbyConfig.Username, Password: config.EmbyConfig.Password, UserId: config.EmbyConfig.UserId, + embyAuthorizationHeader: config.EmbyConfig.embyAuthorizationHeader, isDefault: true, }, ]); @@ -5321,6 +5323,7 @@ const EmbyConfigComponent = ({ transcodeMp4: false, proxyPlay: false, customUserAgent: '', + embyAuthorizationHeader: '', }); setAuthMode('apikey'); setEditingSource(null); @@ -5499,6 +5502,7 @@ const EmbyConfigComponent = ({ ApiKey: source.ApiKey, Username: source.Username, Password: source.Password, + embyAuthorizationHeader: source.embyAuthorizationHeader, }), }); @@ -6225,6 +6229,28 @@ const EmbyConfigComponent = ({ 用于登录、获取影片和代理视频时的User-Agent,留空则使用默认浏览器UA

+ + {/* 自定义 X-Emby-Authorization */} +
+ + + setFormData({ + ...formData, + embyAuthorizationHeader: e.target.value, + }) + } + placeholder='留空使用默认 moontvplus 认证头' + className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white text-sm' + /> +

+ 仅用于账号认证登录请求,示例:MediaBrowser Client="moontvplus", Device="Web", DeviceId="moontvplus-web", Version="1.0.0" +

+
{/* 操作按钮 */} diff --git a/src/app/api/admin/emby/route.ts b/src/app/api/admin/emby/route.ts index 614207e..facdd02 100644 --- a/src/app/api/admin/emby/route.ts +++ b/src/app/api/admin/emby/route.ts @@ -27,7 +27,7 @@ export async function POST(request: NextRequest) { try { const body = await request.json(); - const { action, ServerURL, ApiKey, Username, Password } = body; + const { action, ServerURL, ApiKey, Username, Password, embyAuthorizationHeader } = body; const authInfo = getAuthInfoFromCookie(request); if (!authInfo || !authInfo.username) { @@ -64,6 +64,7 @@ export async function POST(request: NextRequest) { ApiKey, Username, Password, + embyAuthorizationHeader, }; const client = new EmbyClient(testConfig); diff --git a/src/lib/admin.types.ts b/src/lib/admin.types.ts index 32a38f6..8411234 100644 --- a/src/lib/admin.types.ts +++ b/src/lib/admin.types.ts @@ -262,6 +262,7 @@ export interface AdminConfig { transcodeMp4?: boolean; // 转码mp4 proxyPlay?: boolean; // 视频播放代理开关 customUserAgent?: string; // 自定义User-Agent + embyAuthorizationHeader?: string; // 自定义 X-Emby-Authorization 请求头 }>; // 旧格式:单源配置(向后兼容) Enabled?: boolean; @@ -272,6 +273,7 @@ export interface AdminConfig { UserId?: string; AuthToken?: string; Libraries?: string[]; + embyAuthorizationHeader?: string; LastSyncTime?: number; ItemCount?: number; }; diff --git a/src/lib/config.ts b/src/lib/config.ts index 0433ae2..e2d295d 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -707,6 +707,7 @@ export function configSelfCheck(adminConfig: AdminConfig): AdminConfig { UserId: oldConfig.UserId, AuthToken: oldConfig.AuthToken, Libraries: oldConfig.Libraries, + embyAuthorizationHeader: oldConfig.embyAuthorizationHeader, LastSyncTime: oldConfig.LastSyncTime, ItemCount: oldConfig.ItemCount, isDefault: true, diff --git a/src/lib/emby-manager.ts b/src/lib/emby-manager.ts index 311efea..c18d193 100644 --- a/src/lib/emby-manager.ts +++ b/src/lib/emby-manager.ts @@ -23,6 +23,8 @@ interface EmbySourceConfig { appendMediaSourceId?: boolean; transcodeMp4?: boolean; proxyPlay?: boolean; // 视频播放代理开关 + customUserAgent?: string; // 自定义User-Agent + embyAuthorizationHeader?: string; // 自定义 X-Emby-Authorization 请求头 } class EmbyManager { @@ -62,6 +64,7 @@ class EmbyManager { UserId: config.EmbyConfig.UserId, AuthToken: config.EmbyConfig.AuthToken, Libraries: config.EmbyConfig.Libraries, + embyAuthorizationHeader: config.EmbyConfig.embyAuthorizationHeader, LastSyncTime: config.EmbyConfig.LastSyncTime, ItemCount: config.EmbyConfig.ItemCount, isDefault: true, @@ -176,6 +179,7 @@ export function migrateEmbyConfig(config: AdminConfig): AdminConfig { UserId: oldConfig.UserId, AuthToken: oldConfig.AuthToken, Libraries: oldConfig.Libraries, + embyAuthorizationHeader: oldConfig.embyAuthorizationHeader, LastSyncTime: oldConfig.LastSyncTime, ItemCount: oldConfig.ItemCount, isDefault: true, diff --git a/src/lib/emby.client.ts b/src/lib/emby.client.ts index 8a04ab8..d69cab2 100644 --- a/src/lib/emby.client.ts +++ b/src/lib/emby.client.ts @@ -13,6 +13,7 @@ interface EmbyConfig { transcodeMp4?: boolean; proxyPlay?: boolean; // 视频播放代理开关 customUserAgent?: string; // 自定义User-Agent + embyAuthorizationHeader?: string; // 自定义 X-Emby-Authorization 请求头 key?: string; // Emby源的唯一标识 } @@ -77,6 +78,8 @@ interface EmbyView { CollectionType?: string; } +const DEFAULT_EMBY_AUTHORIZATION_HEADER = 'MediaBrowser Client="moontvplus", Device="Web", DeviceId="moontvplus-web", Version="1.0.0"'; + export class EmbyClient { private serverUrl: string; private apiKey?: string; @@ -90,6 +93,7 @@ export class EmbyClient { private proxyPlay: boolean; private embyKey?: string; private customUserAgent: string; + private embyAuthorizationHeader: string; constructor(config: EmbyConfig) { let serverUrl = config.ServerURL.replace(/\/$/, ''); @@ -102,6 +106,7 @@ export class EmbyClient { this.embyKey = config.key; // 设置自定义UA,如果没有设置则使用默认浏览器UA this.customUserAgent = config.customUserAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; + this.embyAuthorizationHeader = config.embyAuthorizationHeader?.trim() || DEFAULT_EMBY_AUTHORIZATION_HEADER; // 如果 URL 不包含 /emby 路径,自动添加(除非启用了 removeEmbyPrefix) if (!serverUrl.endsWith('/emby') && !this.removeEmbyPrefix) { @@ -167,7 +172,7 @@ export class EmbyClient { method: 'POST', headers: { 'Content-Type': 'application/json', - 'X-Emby-Authorization': 'MediaBrowser Client="LunaTV", Device="Web", DeviceId="lunatv-web", Version="1.0.0"', + 'X-Emby-Authorization': this.embyAuthorizationHeader, 'User-Agent': this.customUserAgent, }, body: body,