imports are cool xD

This commit is contained in:
Lars Behrends
2026-04-10 12:24:54 +02:00
parent 73c578f1ec
commit d6ad4c80b3
4 changed files with 92 additions and 9 deletions

View File

@@ -0,0 +1,32 @@
{
"type": "Game",
"title": "1-2-Switch",
"playtime": 120,
"completionStatus": "Completed",
"favorite": true,
"communityScore": 55,
"userScore": 80,
"achievements": [
{
"name": "First Victory",
"description": "Win your first game",
"icon": "https://example.com/achievement-icon.png",
"unlocked": true,
"unlocked_date": "2026-04-09T18:00:00"
},
{
"name": "Master Player",
"description": "Win 100 games",
"icon": "https://example.com/master-icon.png",
"unlocked": true,
"unlocked_date": "2026-04-09T20:30:00"
},
{
"name": "Champion",
"description": "Win 1000 games",
"icon": "https://example.com/champion-icon.png",
"unlocked": false,
"unlocked_date": null
}
]
}

View File

@@ -7,9 +7,16 @@ import { importFromStashAPP, StashAPPConfig, updateActorsFromStashAPP } from '@/
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 [xbvrConfig, setXbvrConfig] = useState<XBVRConfig>({ url: import.meta.env.VITE_XBVR_URL || '' });
const [stashappConfig, setStashappConfig] = useState<StashAPPConfig>({
url: import.meta.env.VITE_STASHAPP_URL || '',
apiKey: import.meta.env.VITE_STASHAPP_API_KEY || ''
});
const [playniteConfig, setPlayniteConfig] = useState<PlayniteConfig>({
ip: import.meta.env.VITE_PLAYNITE_IP || '',
apiToken: import.meta.env.VITE_PLAYNITE_API_TOKEN || '',
port: parseInt(import.meta.env.VITE_PLAYNITE_PORT || '19821')
});
const [progress, setProgress] = useState<ImportProgress>({
current: 0,
total: 0,
@@ -167,6 +174,7 @@ export default function ImporterView({ onBack }: { onBack: () => void }) {
{/* Importer Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
{/* XBVR Importer Card */}
{xbvrConfig.url && (
<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">
@@ -221,8 +229,10 @@ export default function ImporterView({ onBack }: { onBack: () => void }) {
</Button>
</div>
</div>
)}
{/* StashAPP Importer Card */}
{stashappConfig.url && (
<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">
@@ -288,8 +298,10 @@ export default function ImporterView({ onBack }: { onBack: () => void }) {
</Button>
</div>
</div>
)}
{/* StashAPP Actor Updater Card */}
{stashappConfig.url && (
<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">
@@ -344,8 +356,10 @@ export default function ImporterView({ onBack }: { onBack: () => void }) {
</Button>
</div>
</div>
)}
{/* Playnite Importer Card */}
{playniteConfig.ip && playniteConfig.apiToken && (
<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">
@@ -422,6 +436,7 @@ export default function ImporterView({ onBack }: { onBack: () => void }) {
</Button>
</div>
</div>
)}
</div>
{/* Progress Section */}

View File

@@ -60,6 +60,29 @@ export interface PlayniteGamesResponse {
export type LogCallback = (message: string) => void;
export type ProgressCallback = (progress: Partial<ImportProgress>) => void;
async function fetchGameCover(baseUrl: string, headers: Record<string, string>, gameId: string): Promise<string | null> {
try {
const coverResponse = await fetch(`${baseUrl}/api/games/${gameId}/cover`, {
method: 'GET',
headers
});
if (!coverResponse.ok) {
return null;
}
const blob = await coverResponse.blob();
const arrayBuffer = await blob.arrayBuffer();
const base64 = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
// Determine MIME type from blob
const mimeType = blob.type || 'image/jpeg';
return `data:${mimeType};base64,${base64}`;
} catch (error) {
return null;
}
}
export async function importFromPlaynite(
config: PlayniteConfig,
logCallback: LogCallback,
@@ -194,7 +217,6 @@ export async function importFromPlaynite(
// Staff is for actors/performers only - leave empty for games
const staff: any[] = [];
// Determine type based on genres/features
let type = 'Game';
//if (game.genres?.includes('Visual Novel') || game.genres?.includes('Adventure')) {
@@ -242,7 +264,7 @@ export async function importFromPlaynite(
links: game.links || [],
achievements: [],
year: year.toString(),
poster: null,
poster: game.coverBase64 || null,
banner: null,
rating: rating,
category: 'Game',

14
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_XBVR_URL?: string;
readonly VITE_STASHAPP_URL?: string;
readonly VITE_STASHAPP_API_KEY?: string;
readonly VITE_PLAYNITE_IP?: string;
readonly VITE_PLAYNITE_PORT?: string;
readonly VITE_PLAYNITE_API_TOKEN?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}