emby支持无密码登录
This commit is contained in:
+129
-54
@@ -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'>
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user