Tree / Self Hosting / Games
Library Centralisation & Curation
One curated game library across several stores, with complete artwork and metadata per edition, shown through my own Jellyfin bridge.
- Status
- Green
- Type
- workflow
- Hours
- about 67
- Tokens
- about 21 M
Completed: all 1,603 game versions are curated in Playnite, and in the last full comparison covers, backgrounds, icons and logos matched the server with 0 deviations.
In this article
- What I set out to do
- How it was done
- Inside Playnite: metadata
- Inside Playnite: posters and backgrounds
- Logos, the step towards the server
- Character art and the special artwork types
- Series and collections
- Around the library
- Tools and models
- Taste cannot be scored, and other hurdles
- Attempts, kept and rejected
- 1,603 versions, one source of truth
What I set out to do
I did all of this so that everything looks good in Playnite Bridge. The order was metadata first, then posters and backgrounds, and only after that character art and the special artwork types.
My games are spread over 13 shops. Playnite pulls them into one list: 911 entries from Steam, 401 from Epic, 148 from GOG, 47 from EA, 39 from Ubisoft, 23 from PlayStation, 16 from Indiegala, and a few each from Xbox, Rockstar, Battle.net, Riot, itch and Humble. That makes 1,603 entries, 67 of them hidden. So the list existed. A library that looked as if it belonged together did not.
Every shop and every metadata source had left its own habits behind:
- Genres, features and tags were a mix of German and English, with several spellings of the same thing. There were 38 genres, 21 features and 130 tags, and most tags sat on only a handful of games.
- Companies appeared under several names, EA in four of them.
- 74 cover files were empty, left by broken transfers, and many others were low resolution, landscape banners, or box scans with rating stamps printed on them.
- Of 1,603 backgrounds, 1,337 had a problem: missing, too small, thin 3:1 strips, or covered with logos and shop banners.
- Logos were missing for hundreds of games, usable icons for 1,146 versions.
The goal: every version with clean, English, consistent metadata and a good, normed image of every kind, and Playnite as the one source of truth for everything that later appears on my server.
How it was done
Claude Code agents planned, coded and ran the work. They read Playnite's open source code to learn how its LiteDB database stores games, then wrote to it from PowerShell through Playnite's own library. I set goals and conventions, judged by taste and made the unsure selections. The images come from their sources and are not my work.
Three working methods carried the whole project.
One pipeline for every image type. Crawl, prepare, catalogue, deploy. Candidates are fetched through a cache. Each type has one norm. Finished files sit in a catalogue under their final names before anything reaches a game folder, with a record of where each came from.
A safe write. Playnite must be closed, because the database is locked while it runs. The database is backed up, the change is listed, and a fresh copy is read afterwards to prove nothing else moved. Without a deploy flag, every script is a dry run. An md5 comparison ends each change.
Eyes first, decisions at the edge. Agents viewed images on numbered contact sheets, decided the clear cases themselves and sent only the borderline ones to me, on HTML selection pages with ticks, hover zoom, comment fields and a copy button. I did not want to go through hundreds of entries, and anything I left open was dropped.
Inside Playnite: metadata
The first block of work was purely in Playnite, with no server and no plugin involved. It has two parts, metadata and then posters and backgrounds, done back to back.
My brief for metadata was one spelling, one language, no doubles, nothing too long, and all fields clean for all games. What it became:
- Genres: 24 in English, at most 3 per game, ranked by Steam votes. A general word falls away when a sub-genre is present, so Action went from 951 games to 287. "Indie" became a tag, as it says nothing about how a game plays. "Software" and "Mod" were added as the only exceptions.
- Features: 15, taken from Steam's categories.
- Tags: one pool of 109, each used by at least 10 games, at most 8 per game plus "Indie". Steam's community tags covered 1,365 games, directly, through a duplicate's Steam entry, or through a name search. IGDB keywords covered another 182. The rest inherited tags from the main game or got them by hand, always from the pool.
- Companies and series: doubles merged, with 13 similar company pairs kept apart because they really are different firms.
- Descriptions and dates: all in English, filled from Steam and IGDB.
- Age rating: exactly one value per game, PEGI 3, 7, 12, 16, 18 or Not Rated. PEGI has no "not rated", and I wanted a field that a parser can always trust.
Inside Playnite: posters and backgrounds
All 1,603 covers were viewed and 223 replaced. The poster rules came from concrete cases where I disagreed with a pick:
- True portrait only. A landscape image padded to 2:3 does not count.
- No frames, no bars, no printed stamps.
- An official image has no priority over these rules.
- If no true portrait exists anywhere, cut a borderless 2:3 from original game art.
- A normal version gets the normal cover, a special edition gets the special one if it exists.
Backgrounds became borderless 16:9 at 1920x1080, preferably without text. Crop only at similar proportions, at most 12 %. A poster or a banner is never cropped to 16:9, so another image has to be found. Never stretch. Sources went from IGDB artworks and Steam backgrounds to IGDB screenshots and a Bing image search for the last games. Around 1,000 backgrounds came out new.
Logos, the step towards the server
Playnite itself barely needs logos, but my Jellyfin plugin, Playnite Bridge, does. The first idea was an extension and a SteamGridDB account. Fetching directly turned out simpler. A Steam logo belongs to the game, not to the shop, so Steam IDs from the tag work also served Epic and GOG purchases. The rest came from SteamGridDB through the public search of its website.
All logos then went to one standard, the fanart.tv HD ClearLOGO convention I knew from my Kodi work: 800x310, transparent, centred, logo at most 780x290. 854 were replaced, 75 wrong hits sorted out, 114 small ones upscaled. All 1,376 logos on the server were measured for brightness, because a black logo disappears on a dark background. 56 were flagged, I marked 23, and those got a new logo or turned white. 1,525 of 1,536 visible versions have a logo, and the other 11 are without one on purpose.
Character art and the special artwork types
Only here does the project leave what Playnite itself can show. All of this is preparation for Playnite Bridge and for artwork plugins on the server.
Character art means transparent renders of game characters, made by artists on DeviantArt. These images belong to their artists and stay private, never published and never shipped. A scout crawled tags per game and the galleries of known render artists, Fandom supplied character names, and each image was cleaned, placed on a 1024x1024 canvas and upscaled where needed. I went through candidates in two passes, first keep, then take and assign to a version. The main character always comes first, and images are not duplicated across versions. 264 files are in place.
The LaunchBox Games Database became the big source for the rest. Its metadata file maps 1,406 of my 1,536 visible games, 664 by Steam ID and 742 by name and platform.
- Backdrops: 3,356 after 92 review sheets, up to 21 per version, in a fixed order by the image number on the LaunchBox website.
- Landscape: 319 at 1000x562, a backdrop with its logo, and a crop may never cut text.
- Banners: 142, true 5.4:1 only.
- Discs: 298 plus 147 further ones in sets, on the fanart.tv discart template: 1000x1000, centred by a circle fit on the edge, transparent cutouts only. An oval edge or an off-centre hole means rejection. English is judged by what is printed on the disc, and sets follow the printed number.
- Box sets: front, back and spine from one region at one height, never upscaled. 381 versions have a set, 234 with a spine.
- 3D boxes: 731 versions, cleaned of banners, white backgrounds and back covers.
- Squares: 247 versions at 1080x1080.
- Screenshots: 5,014 files in 1,088 versions, sorted by type, never cropped.
Icons are 256x256 PNG only. Extensions do not help, since Steam gives 32 pixels and the other sources give nothing, so most came from SteamGridDB and some from LaunchBox. 1,000 versions now have one, 273 still have an older ICO. 209 have none, because nothing of that size was found.
The 67 hidden entries, demos, map packs, art books and programs, then got the same treatment, in Playnite and on the server.
Series and collections
Three levels, for all 1,603 versions. The franchise is the brand under its official English name, and every game gets one, even as the only one I own. A sub-series needs at least two different games, so a game with its add-ons is not one. Cross collections span several franchises, such as LEGO Games or Tom Clancy's. Genres and studios are never collections. The plan passed an audit and a counter-audit before the write. In the same work, developers and publishers were collected from the Steam store API, LaunchBox and main games, and matched to existing company names.
Around the library
Outside Playnite sits the system I built by hand over time: one disk with one folder per shop, launchers kept apart from game data, a launch folder with a shortcut to every launcher, and a storage archive for whatever no shop can bring back. Star Trek games have their own museum of 47 games with covers and links. An agent mapped this system, audited and counter-audited it, and I fixed ten conventions. The core rule: every shortcut and folder takes its icon from its own disk, never from another disk or an installed program. Shortcuts with such an icon went from 7 of 158 to 191 of 191. Shortcuts were rewritten through Windows' own shell interface, and every icon was checked the way Explorer shows it, not just as a file.
Tools and models
| Tool or model | Used for | Result |
|---|---|---|
| Claude Code | Planning, scripts, viewing, selection pages | Ran everything, 91 scripts |
| PowerShell and LiteDB | Reading and writing Playnite's database | Kept |
| Python, Pillow, NumPy | Norms, sheets, comparison | Kept |
| OpenCV, SciPy | Tilt by Canny and Hough lines, logo brightness and shapes, ORB matching | Kept |
| dHash, 256 bit | Finding near-duplicates, then checked by eye | Kept |
| 4x-UltraSharp via PyTorch and spandrel | All upscaling, colour and alpha separately | Kept |
| Real-ESRGAN x4plus | Upscaling backgrounds | Replaced |
| Steam store and community tags | Metadata, covers, logos | Kept |
| IGDB | Keywords, artworks, screenshots, gaps | Kept, ambiguous keywords removed |
| LaunchBox Games Database | Most special image types | Kept as main source |
| SteamGridDB | Covers, logos, icons, motifs | Kept |
| GOG, Bing image search, press kits | Last gaps | Kept as last resort |
| DeviantArt API | Character art | Kept |
| Fandom | Character names | Names only |
| Extra Metadata Loader | Logos | Not needed |
| RapidOCR | Reading logo text, checking lettering | Kept for the poster idea |
| YuNet, YOLOv10m | Keeping logos off faces and heads | Kept for the poster idea |
| ComfyUI with Qwen-Image-Edit-2511, Q4 GGUF, Lightning LoRA | Lettering, removing logos from covers | Rejected for lettering, kept for removal |
| FLUX.2 Klein 9B | Lettering | Rejected for lettering |
| Calligrapher, FLUX.1-Fill-dev, SigLIP | Lettering in a logo's style | Best result, not finished |
| Font matching with Google Fonts | Lettering without AI | Reserve |
Taste cannot be scored, and other hurdles
Looking at everything. Taste was the largest hurdle. Formats and resolutions can be measured, but printed stamps and ugly fills only show when someone looks. Ratings do not help either, since all SteamGridDB ratings are 0. The answer was viewing everything, plus finding ways to turn my choices into rules.
Sources without structure. LaunchBox gives no language, no image size and no order, only a region. Its database ID is not the number on its website, so image order had to be read from the site. 88 loose name matches had to be checked one by one. Automatic matching often picked the wrong edition or the wrong game in a series, so every image had to be viewed. Steam and SteamGridDB also throttle, which meant slow fetching with caches and restarts.
Many versions of one game. There is no main version. All versions are equal, each with its own folder, and the same game can sit in several shops. Grouping them into one tile needed data fixes such as aligned titles and added Steam links. Images then had to be distributed so that versions differ where good alternatives exist.
One direction only. The Bridge copies from Playnite to the server and never back, while the LaunchBox round had written to the server. Playnite ended up behind by 955 backgrounds, 44 icons and 1 cover, and these were brought back. Since then the rule is Playnite first, then a byte-identical copy with the same file time, then a new folder icon. The two projects work under frozen rules that say who writes what, and details matter: the Bridge stops reading after 3 missing numbers, so backdrops must be numbered without gaps.
Things Playnite does not cover. Its backup leaves out the logo folder, so logos get their own archive. Windows caches folder icons per path, so icons have to be forced and the cache cleared.
12 GB of VRAM. A generated poster for each collection needs several large models on a laptop GPU. Calligrapher fits only with a 4-bit transformer at about 9 GB, and it cannot run beside Qwen, which takes about 3 minutes per cover. Finding clean logos took hours until OCR ran in parallel.
Attempts, kept and rejected
- Worked: Viewing every image on sheets. It found what measurements could not.
- Worked: Learning a rule from my box set picks. I chose the first half, a script derived my preferences, matched my choices 81 % of the time and decided the rest.
- Worked: A ranked list of icon creators in place of 757 selections. I named whose style I like, and the script took the first one available.
- Worked: Measuring disc tilt, with one limit. Above about 8 degrees the tilt is usually intended design, so only smaller angles were corrected.
- Worked: Qwen-Image-Edit for removing logos and stamps from covers, with only the changed area taken over.
- Partly: Real-ESRGAN. The backgrounds were upscaled with it, but realesrgan-ncnn-vulkan is broken on RTX 50 cards, so 4x-UltraSharp replaced it.
- Partly: Automatic border detection. It took dark edges and plain backgrounds for frames, so results needed checking by eye.
- Partly: Font matching without AI. It compares letter shapes against a font pool and works only for plain text logos.
- Partly: Calligrapher. The style fits, and 4 posters were kept. Others had faults in size, colour or single letters, and the search was too slow.
- Did not work: Raw IGDB keywords. They were too coarse and put wrong tags on well-known games, so ambiguous ones were taken out.
- Did not work: Grouping by a year in the title, ruled out by two DOOM games of the same name.
- Did not work: Copying character art into all versions. I did not want duplicates, so images are distributed.
- Did not work: Fandom images. The image server answers scripts with 403. This was not bypassed.
- Did not work: Console covers as a fallback. I disliked them, so SteamGridDB comes first.
- Did not work: Proposing icons by score, since no usable scores exist.
- Did not work: Lettering with Qwen-Image-Edit and FLUX.2 Klein. Text looks pasted in, and Klein changes the whole image.
1,603 versions, one source of truth
The library is done: 1,603 versions, 427 franchises, 37 sub-series, 7 cross collections, 13 image types, folder icons included.
One idea is still open: a poster for each of the 471 collections in the style of a film collection, with a motif, the series logo and the word COLLECTION in exactly the same lettering. Nothing of this is applied yet, and Qwen3-VL as a logo check is planned for it. After that come editing edition words in the Bridge's admin menu and aligning my grouping with the Bridge's.
Connected
Next: Masterface