playnite init

This commit is contained in:
Lars Behrends
2026-04-10 08:46:56 +02:00
parent 1caadd12e1
commit 73c578f1ec
6 changed files with 577 additions and 14 deletions

View File

@@ -4,10 +4,12 @@ import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { importFromXBVR, XBVRConfig, ImportProgress } from '@/lib/xbvrImporter';
import { importFromStashAPP, StashAPPConfig, updateActorsFromStashAPP } from '@/lib/stashappImporter';
import { importFromPlaynite, PlayniteConfig } from '@/lib/playniteImporter';
export default function ImporterView({ onBack }: { onBack: () => void }) {
const [xbvrConfig, setXbvrConfig] = useState<XBVRConfig>({ url: 'http://192.168.1.102:4080' });
const [stashappConfig, setStashappConfig] = useState<StashAPPConfig>({ url: 'http://192.168.1.102:10001' });
const [playniteConfig, setPlayniteConfig] = useState<PlayniteConfig>({ ip: 'localhost', apiToken: '', port: 19821 });
const [progress, setProgress] = useState<ImportProgress>({
current: 0,
total: 0,
@@ -101,6 +103,29 @@ export default function ImporterView({ onBack }: { onBack: () => void }) {
setProgress(result);
};
const handlePlayniteImport = async () => {
setProgress({
current: 0,
total: 0,
stage: 'fetching',
message: 'Connecting to Playnite API...',
videosImported: 0,
actorsImported: 0,
errors: []
});
setImportLog([]);
const result = await importFromPlaynite(
playniteConfig,
addLog,
(progressUpdate) => {
setProgress(prev => ({ ...prev, ...progressUpdate }));
}
);
setProgress(result);
};
const resetImport = () => {
setProgress({
current: 0,
@@ -320,10 +345,82 @@ export default function ImporterView({ onBack }: { onBack: () => void }) {
</div>
</div>
{/* Placeholder for future importers */}
<div className="bg-zinc-50 border border-zinc-200 border-dashed rounded-xl p-6 flex flex-col items-center justify-center text-zinc-400">
<Download size={32} className="mb-2" />
<p className="text-sm font-medium">More importers coming soon</p>
{/* Playnite Importer Card */}
<div className="bg-white border border-zinc-200 rounded-xl p-6 hover:border-[#6d28d9]/50 transition-colors">
<div className="flex items-start justify-between mb-4">
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-orange-100 rounded-lg flex items-center justify-center">
<Film className="text-orange-600" size={24} />
</div>
<div>
<h3 className="font-bold text-zinc-900">Playnite</h3>
<p className="text-xs text-zinc-500 font-medium">Game Library Manager</p>
</div>
</div>
<Button
variant="outline"
size="icon"
className="h-8 w-8 border-zinc-200"
onClick={() => {}}
>
<Settings size={16} />
</Button>
</div>
<p className="text-sm text-zinc-600 mb-4">
Import games from your Playnite library via Bridge API.
</p>
<div className="space-y-3">
<div>
<label className="text-xs font-bold text-zinc-500 mb-1 block">IP Address</label>
<input
type="text"
value={playniteConfig.ip}
onChange={(e) => setPlayniteConfig({ ...playniteConfig, ip: e.target.value })}
disabled={progress.stage !== 'idle'}
className="w-full px-3 py-2 text-sm border border-zinc-200 rounded-lg focus:ring-2 focus:ring-[#6d28d9] focus:border-transparent outline-none disabled:bg-zinc-100 disabled:cursor-not-allowed"
placeholder="localhost"
/>
</div>
<div>
<label className="text-xs font-bold text-zinc-500 mb-1 block">Port</label>
<input
type="number"
value={playniteConfig.port || 19821}
onChange={(e) => setPlayniteConfig({ ...playniteConfig, port: parseInt(e.target.value) || 19821 })}
disabled={progress.stage !== 'idle'}
className="w-full px-3 py-2 text-sm border border-zinc-200 rounded-lg focus:ring-2 focus:ring-[#6d28d9] focus:border-transparent outline-none disabled:bg-zinc-100 disabled:cursor-not-allowed"
placeholder="19821"
/>
</div>
<div>
<label className="text-xs font-bold text-zinc-500 mb-1 block">API Token</label>
<input
type="password"
value={playniteConfig.apiToken}
onChange={(e) => setPlayniteConfig({ ...playniteConfig, apiToken: e.target.value })}
disabled={progress.stage !== 'idle'}
className="w-full px-3 py-2 text-sm border border-zinc-200 rounded-lg focus:ring-2 focus:ring-[#6d28d9] focus:border-transparent outline-none disabled:bg-zinc-100 disabled:cursor-not-allowed"
placeholder="pb_your_token_here"
/>
</div>
<Button
onClick={handlePlayniteImport}
disabled={progress.stage !== 'idle' || !playniteConfig.ip || !playniteConfig.apiToken}
className="w-full bg-orange-600 hover:bg-orange-700 text-white font-bold"
>
{progress.stage === 'fetching' || progress.stage === 'importing' ? (
<>
<Loader2 size={16} className="mr-2 animate-spin" />
Importing...
</>
) : (
<>
<Download size={16} className="mr-2" />
Import from Playnite
</>
)}
</Button>
</div>
</div>
</div>
@@ -392,9 +489,9 @@ export default function ImporterView({ onBack }: { onBack: () => void }) {
<div className="bg-zinc-50 rounded-lg p-4">
<div className="flex items-center gap-2 mb-2">
<Film size={16} className="text-zinc-400" />
<span className="text-xs font-bold text-zinc-500">Videos</span>
<span className="text-xs font-bold text-zinc-500">{(progress as any).gamesImported !== undefined ? 'Games' : 'Videos'}</span>
</div>
<p className="text-2xl font-black text-zinc-900">{progress.videosImported}</p>
<p className="text-2xl font-black text-zinc-900">{(progress as any).gamesImported !== undefined ? (progress as any).gamesImported : progress.videosImported}</p>
</div>
<div className="bg-zinc-50 rounded-lg p-4">
<div className="flex items-center gap-2 mb-2">