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

View File

@@ -3514,6 +3514,7 @@ const EmbyConfigComponent = ({
proxyPlay: false,
customUserAgent: '',
});
const [authMode, setAuthMode] = useState<'apikey' | 'password'>('apikey');
// 从配置加载源列表
useEffect(() => {
@@ -3554,6 +3555,7 @@ const EmbyConfigComponent = ({
proxyPlay: false,
customUserAgent: '',
});
setAuthMode('apikey');
setEditingSource(null);
setShowAddForm(false);
};
@@ -3561,6 +3563,14 @@ const EmbyConfigComponent = ({
// 开始编辑
const handleEdit = (source: any) => {
setFormData({ ...source });
// 根据现有配置判断认证方式
if (source.ApiKey) {
setAuthMode('apikey');
} else if (source.Username) {
setAuthMode('password');
} else {
setAuthMode('apikey');
}
setEditingSource(source);
setShowAddForm(false);
};
@@ -3579,6 +3589,19 @@ const EmbyConfigComponent = ({
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唯一性
if (!editingSource && sources.some(s => s.key === formData.key)) {
showError('标识符已存在,请使用其他标识符', showAlert);
@@ -4088,67 +4111,119 @@ const EmbyConfigComponent = ({
/>
</div>
{/* API Key */}
{/* 认证方式切换卡 */}
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
API Key
*
</label>
<input
type='password'
value={formData.ApiKey}
onChange={(e) => setFormData({ ...formData, ApiKey: e.target.value })}
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'>
使 API Key 使 API Key
</p>
<div className='flex gap-2 mb-4'>
<button
type='button'
onClick={() => {
setAuthMode('apikey');
// 切换到密钥认证时,清空用户名密码
setFormData({ ...formData, Username: '', Password: '' });
}}
className={`flex-1 px-4 py-2 rounded-lg font-medium transition-colors ${
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>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
</label>
<input
type='text'
value={formData.Username}
onChange={(e) => setFormData({ ...formData, Username: 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'
/>
</div>
{/* 密钥认证模式 */}
{authMode === 'apikey' && (
<>
{/* API Key */}
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
API Key *
</label>
<input
type='password'
value={formData.ApiKey}
onChange={(e) => setFormData({ ...formData, ApiKey: e.target.value })}
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>
{/* 密码 */}
<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'
/>
</div>
{/* 用户 ID */}
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
ID *
</label>
<input
type='text'
value={formData.UserId}
onChange={(e) => setFormData({ ...formData, UserId: e.target.value })}
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'
/>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
Emby ID URL /Users/[userId]/...
</p>
</div>
</>
)}
{/* 用户 ID */}
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
ID使 API Key
</label>
<input
type='text'
value={formData.UserId}
onChange={(e) => setFormData({ ...formData, UserId: e.target.value })}
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'
/>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
Emby ID URL /Users/[userId]/...
</p>
</div>
{/* 账号认证模式 */}
{authMode === 'password' && (
<>
{/* 用户名 */}
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
*
</label>
<input
type='text'
value={formData.Username}
onChange={(e) => setFormData({ ...formData, Username: 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'
/>
</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'>

View File

@@ -52,9 +52,9 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: '请填写 Emby 服务器地址' }, { status: 400 });
}
if (!ApiKey && (!Username || !Password)) {
if (!ApiKey && !Username) {
return NextResponse.json(
{ error: '请填写 API Key 或用户名密码' },
{ error: '请填写 API Key 或用户名' },
{ status: 400 }
);
}
@@ -69,9 +69,9 @@ export async function POST(request: NextRequest) {
const client = new EmbyClient(testConfig);
// 如果使用用户名密码,先认证
if (!ApiKey && Username && Password) {
if (!ApiKey && Username) {
try {
await client.authenticate(Username, Password);
await client.authenticate(Username, Password || '');
} catch (error) {
return NextResponse.json(
{ success: false, message: 'Emby 认证失败: ' + (error as Error).message },

View File

@@ -115,9 +115,9 @@ export class EmbyClient {
// 如果有 AuthToken假设它是有效的
if (this.authToken) return;
// 如果有用户名和密码,自动认证
if (this.username && this.password) {
const authResult = await this.authenticate(this.username, this.password);
// 如果有用户名,自动认证(密码可选)
if (this.username) {
const authResult = await this.authenticate(this.username, this.password || '');
this.authToken = authResult.AccessToken;
this.userId = authResult.User.Id;
}