emby支持无密码登录

This commit is contained in:
mtvpls
2026-03-07 10:19:38 +08:00
parent 82e6a33b00
commit 047691d1a5
3 changed files with 136 additions and 61 deletions
+129 -54
View File
@@ -3514,6 +3514,7 @@ const EmbyConfigComponent = ({
proxyPlay: false, proxyPlay: false,
customUserAgent: '', customUserAgent: '',
}); });
const [authMode, setAuthMode] = useState<'apikey' | 'password'>('apikey');
// 从配置加载源列表 // 从配置加载源列表
useEffect(() => { useEffect(() => {
@@ -3554,6 +3555,7 @@ const EmbyConfigComponent = ({
proxyPlay: false, proxyPlay: false,
customUserAgent: '', customUserAgent: '',
}); });
setAuthMode('apikey');
setEditingSource(null); setEditingSource(null);
setShowAddForm(false); setShowAddForm(false);
}; };
@@ -3561,6 +3563,14 @@ const EmbyConfigComponent = ({
// 开始编辑 // 开始编辑
const handleEdit = (source: any) => { const handleEdit = (source: any) => {
setFormData({ ...source }); setFormData({ ...source });
// 根据现有配置判断认证方式
if (source.ApiKey) {
setAuthMode('apikey');
} else if (source.Username) {
setAuthMode('password');
} else {
setAuthMode('apikey');
}
setEditingSource(source); setEditingSource(source);
setShowAddForm(false); setShowAddForm(false);
}; };
@@ -3579,6 +3589,19 @@ const EmbyConfigComponent = ({
return; return;
} }
// 根据认证方式验证必填字段
if (authMode === 'apikey') {
if (!formData.ApiKey || !formData.UserId) {
showError('使用密钥认证时,API Key 和用户 ID 为必填项', showAlert);
return;
}
} else if (authMode === 'password') {
if (!formData.Username) {
showError('使用账号认证时,用户名为必填项', showAlert);
return;
}
}
// 验证key唯一性 // 验证key唯一性
if (!editingSource && sources.some(s => s.key === formData.key)) { if (!editingSource && sources.some(s => s.key === formData.key)) {
showError('标识符已存在,请使用其他标识符', showAlert); showError('标识符已存在,请使用其他标识符', showAlert);
@@ -4088,67 +4111,119 @@ const EmbyConfigComponent = ({
/> />
</div> </div>
{/* API Key */} {/* 认证方式切换卡 */}
<div> <div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'> <label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
API Key *
</label> </label>
<input <div className='flex gap-2 mb-4'>
type='password' <button
value={formData.ApiKey} type='button'
onChange={(e) => setFormData({ ...formData, ApiKey: e.target.value })} onClick={() => {
placeholder='输入 Emby API Key' setAuthMode('apikey');
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' // 切换到密钥认证时,清空用户名密码
/> setFormData({ ...formData, Username: '', Password: '' });
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'> }}
使 API Key 使 API Key className={`flex-1 px-4 py-2 rounded-lg font-medium transition-colors ${
</p> authMode === 'apikey'
? 'bg-blue-600 text-white'
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
}`}
>
</button>
<button
type='button'
onClick={() => {
setAuthMode('password');
// 切换到账号认证时,清空 API Key 和 UserId
setFormData({ ...formData, ApiKey: '', UserId: '' });
}}
className={`flex-1 px-4 py-2 rounded-lg font-medium transition-colors ${
authMode === 'password'
? 'bg-blue-600 text-white'
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
}`}
>
</button>
</div>
</div> </div>
{/* 用户名 */} {/* 密钥认证模式 */}
<div> {authMode === 'apikey' && (
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'> <>
{/* API Key */}
</label> <div>
<input <label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
type='text' API Key *
value={formData.Username} </label>
onChange={(e) => setFormData({ ...formData, Username: e.target.value })} <input
placeholder='Emby 用户名' type='password'
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' value={formData.ApiKey}
/> onChange={(e) => setFormData({ ...formData, ApiKey: e.target.value })}
</div> placeholder='输入 Emby API Key'
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
/>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
Emby API
</p>
</div>
{/* 密码 */} {/* 用户 ID */}
<div> <div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'> <label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
ID *
</label> </label>
<input <input
type='password' type='text'
value={formData.Password} value={formData.UserId}
onChange={(e) => setFormData({ ...formData, Password: e.target.value })} onChange={(e) => setFormData({ ...formData, UserId: e.target.value })}
placeholder='Emby 密码' placeholder='aab507c58e874de6a9bd12388d72f4d2'
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
/> />
</div> <p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
Emby ID URL /Users/[userId]/...
</p>
</div>
</>
)}
{/* 用户 ID */} {/* 账号认证模式 */}
<div> {authMode === 'password' && (
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'> <>
ID使 API Key {/* 用户名 */}
</label> <div>
<input <label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
type='text' *
value={formData.UserId} </label>
onChange={(e) => setFormData({ ...formData, UserId: e.target.value })} <input
placeholder='aab507c58e874de6a9bd12388d72f4d2' type='text'
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100' value={formData.Username}
/> onChange={(e) => setFormData({ ...formData, Username: e.target.value })}
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'> placeholder='Emby 用户名'
Emby ID URL /Users/[userId]/... className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
</p> />
</div> </div>
{/* 密码 */}
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
</label>
<input
type='password'
value={formData.Password}
onChange={(e) => setFormData({ ...formData, Password: e.target.value })}
placeholder='Emby 密码(如果账号没有密码可留空)'
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100'
/>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
Emby
</p>
</div>
</>
)}
{/* 启用开关 */} {/* 启用开关 */}
<div className='flex items-center justify-between'> <div className='flex items-center justify-between'>
+4 -4
View File
@@ -52,9 +52,9 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: '请填写 Emby 服务器地址' }, { status: 400 }); return NextResponse.json({ error: '请填写 Emby 服务器地址' }, { status: 400 });
} }
if (!ApiKey && (!Username || !Password)) { if (!ApiKey && !Username) {
return NextResponse.json( return NextResponse.json(
{ error: '请填写 API Key 或用户名密码' }, { error: '请填写 API Key 或用户名' },
{ status: 400 } { status: 400 }
); );
} }
@@ -69,9 +69,9 @@ export async function POST(request: NextRequest) {
const client = new EmbyClient(testConfig); const client = new EmbyClient(testConfig);
// 如果使用用户名密码,先认证 // 如果使用用户名密码,先认证
if (!ApiKey && Username && Password) { if (!ApiKey && Username) {
try { try {
await client.authenticate(Username, Password); await client.authenticate(Username, Password || '');
} catch (error) { } catch (error) {
return NextResponse.json( return NextResponse.json(
{ success: false, message: 'Emby 认证失败: ' + (error as Error).message }, { success: false, message: 'Emby 认证失败: ' + (error as Error).message },
+3 -3
View File
@@ -115,9 +115,9 @@ export class EmbyClient {
// 如果有 AuthToken,假设它是有效的 // 如果有 AuthToken,假设它是有效的
if (this.authToken) return; if (this.authToken) return;
// 如果有用户名和密码,自动认证 // 如果有用户名,自动认证(密码可选)
if (this.username && this.password) { if (this.username) {
const authResult = await this.authenticate(this.username, this.password); const authResult = await this.authenticate(this.username, this.password || '');
this.authToken = authResult.AccessToken; this.authToken = authResult.AccessToken;
this.userId = authResult.User.Id; this.userId = authResult.User.Id;
} }