Er is nu ook een speler voor GNOME #108

Merged
roelof merged 6 commits from gnome-speler into main 2026-08-13 16:14:20 +02:00
Owner

Een tweede desktopspeler, GTK4 en libadwaita over GirCore. Vijfde project in de solution, referenceert RockHeaven.Common alleen — dus geen DbContext en geen TagLib#, afgedwongen door de csproj in plaats van door discipline.

De twee keuzes die je gemaakt hebt

.NET met GirCore, niet Python met PyGObject. De doorslag gaf de contracten: een Python-client zou een eigen handgeschreven kopie van Contracts.cs bijhouden — precies wat de Android-app doet en precies waar diens eigen CLAUDE.md voor waarschuwt. Dit referenceert Common en kan dus niet uit elkaar lopen.

Luisterclient: bladeren, afspelen, hervatten, waarderen. Geen scan, fill, bewerkvenster, verwijderen of beheer — dezelfde streep die RockHeaven.Android trekt.

Wat er draaiend gezien is

In de geneste headless kwin_wayland --virtual op een eigen D-Bus, tegen de live server met jouw token en jouw bibliotheek:

  • Alle drie de collectievormen. TOP 2000 (flat, telt af van 2000), Rock Heaven (volumes, RockHeaven Vol. 001…), Albums (artiesten met de plaatkolom ertussen).
  • De tweede oproep voor Tijd en Jaar — 2000 tags over 2000 rijen — die achter de lijst aan invult.
  • Afspelen: TOTO — Mushanga, 4:00 van 5:41, sterren ★★★☆☆, en de wachtrij schoof uit zichzelf door toen de track afliep.

Drie fouten die alleen dáár zichtbaar werden, en geen ervan zou een build of een type-check hebben gevangen:

  1. De hervatting speelde niets. Hij zocht de track in queue, en die wordt alleen gevuld door een dubbelklik. AdoptQueue is er nu, en de hervatting wacht op de lijst die ze zoekt.
  2. De zoekbalk deed niets. Aan value-changed gehangen kun je de luisteraar niet onderscheiden van mpv's eigen vier meldingen per seconde; change-value wordt alleen voor de luisteraar zelf uitgezonden.
  3. De knop toonde ▶ terwijl er muziek uit kwam. mpv meldt de pauzevlag alleen als hij beweegt, en hij beweegt niet als een track ongepauzeerd start vanuit een speler die al ongepauzeerd was.

En één valkuil van de binding die een echte bug was: een GObject-subklasse draagt gewone C#-properties, en die muteren verandert niets op het scherm. ItemsChanged(0, n, n) zet de waarden wél op de objecten (teruggelezen en bewezen) en laat de kolommen Tijd en Jaar voorgoed leeg, omdat GtkListView het al gebonden widget behoudt als het item op die positie hetzelfde object is. De tagvulling maakt nu verse rij-objecten en Splicet ze erin. Staat uitgeschreven in de CLAUDE.md, want dit komt terug.

Bijwerkingen, en wat ik teruggezet heb

Om het afspelen te kunnen zien heb ik mpv met --ao=null laten draaien — geen geluid — en de speler één track laten hervatten. Dat schrijft in de gedeelde playback-rij. Ik heb je positie exact teruggezet (track 31408, 54,581 s); alleen updatedUtc verschilt, en omdat het dezelfde collectie is en die rij al de nieuwste was, verandert dat niets aan "waar was ik het laatst".

Bewust nog niet

MPRIS (dus de GNOME-shell ziet hem niet), hoezen — nergens, geen omslag in de transportbalk en geen raster —, geen artwork-cache, geen zoeken, en waarderen kan alleen voor wat er speelt. Geen daarvan is een besluit ertegen.

Wat het kost

MpvPlaybackService, SessionStore en de vorm van ApiClient zijn kopieën van de Avalonia-speler. Bewust: in die mpv-driver is elk commentaar betaald. Maar een fix moet nu twee keer gemaakt worden. Het antwoord daarop is niet meer kopiëren maar de gedeelde helft optillen naar een RockHeaven.Client dat beide spelers referenceren — een verbouwing van een wérkende speler, en daarom geen onderdeel van deze PR.

dotnet build RockHeaven.sln is schoon. libadwaita moest erbij (jij hebt hem geïnstalleerd); GirCore is een binding en brengt zelf geen bibliotheken mee, dus het bouwt zonder en start niet zonder.

Een tweede desktopspeler, GTK4 en libadwaita over **GirCore**. Vijfde project in de solution, referenceert **`RockHeaven.Common` alleen** — dus geen DbContext en geen TagLib#, afgedwongen door de csproj in plaats van door discipline. ### De twee keuzes die je gemaakt hebt **.NET met GirCore**, niet Python met PyGObject. De doorslag gaf de contracten: een Python-client zou een eigen handgeschreven kopie van `Contracts.cs` bijhouden — precies wat de Android-app doet en precies waar diens eigen CLAUDE.md voor waarschuwt. Dit referenceert Common en kan dus niet uit elkaar lopen. **Luisterclient**: bladeren, afspelen, hervatten, waarderen. Geen scan, fill, bewerkvenster, verwijderen of beheer — dezelfde streep die `RockHeaven.Android` trekt. ### Wat er draaiend gezien is In de geneste headless `kwin_wayland --virtual` op een eigen D-Bus, tegen de live server met jouw token en jouw bibliotheek: - **Alle drie de collectievormen.** TOP 2000 (flat, telt af van 2000), Rock Heaven (volumes, `RockHeaven Vol. 001…`), Albums (artiesten met de plaatkolom ertussen). - **De tweede oproep voor Tijd en Jaar** — 2000 tags over 2000 rijen — die achter de lijst aan invult. - **Afspelen**: TOTO — Mushanga, 4:00 van 5:41, sterren ★★★☆☆, en de wachtrij schoof uit zichzelf door toen de track afliep. Drie fouten die alleen dáár zichtbaar werden, en geen ervan zou een build of een type-check hebben gevangen: 1. **De hervatting speelde niets.** Hij zocht de track in `queue`, en die wordt alleen gevuld door een dubbelklik. `AdoptQueue` is er nu, en de hervatting wacht op de lijst die ze zoekt. 2. **De zoekbalk deed niets.** Aan `value-changed` gehangen kun je de luisteraar niet onderscheiden van mpv's eigen vier meldingen per seconde; `change-value` wordt alleen voor de luisteraar zelf uitgezonden. 3. **De knop toonde ▶ terwijl er muziek uit kwam.** mpv meldt de pauzevlag alleen als hij *beweegt*, en hij beweegt niet als een track ongepauzeerd start vanuit een speler die al ongepauzeerd was. En één valkuil van de binding die een echte bug was: **een GObject-subklasse draagt gewone C#-properties, en die muteren verandert niets op het scherm.** `ItemsChanged(0, n, n)` zet de waarden wél op de objecten (teruggelezen en bewezen) en laat de kolommen Tijd en Jaar voorgoed leeg, omdat `GtkListView` het al gebonden widget behoudt als het item op die positie hetzelfde object is. De tagvulling maakt nu verse rij-objecten en `Splice`t ze erin. Staat uitgeschreven in de CLAUDE.md, want dit komt terug. ### Bijwerkingen, en wat ik teruggezet heb Om het afspelen te kunnen zien heb ik mpv met `--ao=null` laten draaien — geen geluid — en de speler één track laten hervatten. Dat schrijft in de gedeelde `playback`-rij. **Ik heb je positie exact teruggezet** (track 31408, 54,581 s); alleen `updatedUtc` verschilt, en omdat het dezelfde collectie is en die rij al de nieuwste was, verandert dat niets aan "waar was ik het laatst". ### Bewust nog niet MPRIS (dus de GNOME-shell ziet hem niet), hoezen — nergens, geen omslag in de transportbalk en geen raster —, geen artwork-cache, geen zoeken, en waarderen kan alleen voor wat er speelt. Geen daarvan is een besluit ertegen. ### Wat het kost `MpvPlaybackService`, `SessionStore` en de vorm van `ApiClient` zijn kopieën van de Avalonia-speler. Bewust: in die mpv-driver is elk commentaar betaald. Maar een fix moet nu twee keer gemaakt worden. Het antwoord daarop is niet meer kopiëren maar de gedeelde helft optillen naar een `RockHeaven.Client` dat beide spelers referenceren — een verbouwing van een wérkende speler, en daarom geen onderdeel van deze PR. `dotnet build RockHeaven.sln` is schoon. `libadwaita` moest erbij (jij hebt hem geïnstalleerd); GirCore is een binding en brengt zelf geen bibliotheken mee, dus het bouwt zonder en start niet zonder.
Issue-loze wens: een desktopspeler in GTK4 en libadwaita. RockHeaven.Gnome is
een vijfde project in de solution en referenceert RockHeaven.Common alleen —
net als de Avalonia-speler houdt het dus geen DbContext en geen TagLib#, en dat
wordt door de csproj afgedwongen in plaats van door discipline.

Het is een luisterclient: bladeren, afspelen, hervatten en waarderen. Geen
scan, geen fill, geen bewerkvenster, geen verwijderen, geen beheervenster. Dat
is dezelfde streep die RockHeaven.Android trekt, en om dezelfde reden: die vijf
dingen zijn één taak, ze zijn gevormd naar volumes van honderd, en de
Avalonia-speler doet ze al.

.NET met GirCore en niet Python met PyGObject, om één reden: de contracten. Een
Python-client zou een eigen handgeschreven kopie van Contracts.cs bijhouden —
precies wat de Android-app doet en precies waar diens eigen CLAUDE.md voor
waarschuwt. Dit project referenceert Common en kan dus niet uit elkaar lopen.

Drie dingen zijn bewust kopieën van de Avalonia-speler, MpvPlaybackService
voorop: elk commentaar daarin is betaald — de start-eigenschap die geen
loadfile-optie mag zijn en een string moet zijn, de header bij elke play,
end-file dat alleen op eof doorschuift. Een tweede driver schrijven had een
tweede stel dezelfde fouten opgeleverd. Wat het kost is dat een fix twee keer
gemaakt moet worden, en het antwoord daarop is niet meer kopiëren maar de
gedeelde helft optillen naar een RockHeaven.Client dat beide spelers
referenceren. Dat is een verbouwing van een wérkende speler en hoort daarom
niet bij de wijziging die deze speler introduceert.

session.json wordt gedeeld en player.json niet. Het tokenbestand houdt één veld
vast, dus een tweede schrijver heeft niets te verliezen, en delen betekent één
keer aanmelden per machine — een code die per sms komt vraag je niet twee keer.
player.json houdt een heel object vast en noemt zichzelf "the one owner of that
file"; de tweede eigenaar zijn is precies waar die regel tegen geschreven is,
dus de minimumsterren staan in gnome.json ernaast.

Geen themaservice en geen fontservice: libadwaita volgt de licht/donker-voorkeur
van het bureaublad zelf en GTK gebruikt het bureaubladlettertype al.
Elk plaatje dat die speler laat zien staat er nu ook in: het duimnagel-
plaatje naast elke rij van de zijbalk en van de platenkolom, het grotere
eronder van wat er geselecteerd is, de hoes van wat er speelt, en de
brede foto van de artiest achter de trackkolom. Zelfde endpoints,
zelfde regels — alleen het gereedschap verschilt, en vier van die
verschillen kostten iets om te vinden.

- **Gtk.Image voor alles met een vaste maat, geen Gtk.Picture.** Een
  Gtk.Picture vraagt om de maat van het plaatje dát erin zit, en
  SetSizeRequest verhoogt alleen zijn *minimum* — een op 512 gedecodeerde
  hoes kwam zo 512 pixels breed in de transportbalk, en het plaatje van
  de collectie pakte twee derde van de zijbalk af van de artiestenlijst.
  Gtk.Image vraagt om zijn pixelmaat, wat precies de bovengrens is die
  hier bedoeld werd. Wat het kost is de uitsnede: dit past het hele
  plaatje in het vierkant in plaats van het te vullen. De backdrop
  blijft een Gtk.Picture, want vullen is daar juist de bedoeling.
- **Een plaatje krijg je niet op een rij door een property te zetten**,
  om precies de reden die de Tijd- en Jaar-kolom al leerde. Splicen kan
  hier niet: de plaatjes komen één voor één over minuten binnen, en een
  splice per plaatje zou de lijst honderd keer herbinden en telkens de
  selectie weggooien. IPictured houdt daarom de weg terug naar de widget
  bij — de factory zet Bound bij het binden en wist het bij unbind.
- **De volumeplaatjes komen hier van de server.** De Avalonia-speler
  draagt ze in zijn eigen assembly; dit project kent alleen Common en
  vraagt volumes/{n}/image, dat precies diezelfde bestanden serveert.
- **Eén plaatje onder de zijbalk waar de Avalonia-speler er twee toont**,
  omdat deze zijbalk 260 tot 360 pixels breed is. De hoes staat in de
  transportbalk, waar een GNOME-speler hem toch al zet.

De artwork-cache is die van de Avalonia-speler, map en al. Dat mag hier
wel waar player.json dat niet mocht: het zijn de bytes zoals de server
ze stuurde, op adres en pad gesleuteld. Het scheelt een tweede gigabyte,
en het beantwoordt het enige wat deze speler zelf niet kan — wanneer een
plaatje verandert. Deze client cureert niets, dus hij heeft geen moment
om iets weg te gooien; de Avalonia-speler leegt die map bij elke run
achter zijn scanknop, en dat leegt daarmee ook deze.

Draaiend nagekeken tegen de live server, in alle drie de vormen: de
Top 2000 (met collectieplaatje en backdrop), RockHeaven (volumeplaatjes)
en Albums (hoezen in de platenkolom). Geen enkele Gtk-CRITICAL meer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Het paneel, de volumepopup en KDE Connect zien hem nu, met titel, artiest,
album, lengte en hoes, en de knoppen daar sturen hem aan. Het is de
MprisService van de Avalonia-speler, in zijn geheel overgenomen — de
vijfde na mpv, de sessieopslag, de artwork-cache en de vorm van de
API-client, en degene waar overnemen het minst te betwisten valt: elke
regel erin is betaald met fouten die zich melden door te zwijgen. Een
handgeschreven geneste variant beantwoordt de busdaemon door de
verbinding zonder een woord te verbreken, en een naamclaim zonder alle
drie de vlaggen laat de tweede speler onbereikbaar achter de eerste
staan. Tmds.DBus.Protocol is hier een package-referentie waar de
Avalonia-speler hem gratis meekrijgt, vastgezet op dezelfde versie.

Twee dingen moesten verschillen, allebei omdat de twee spelers naast
elkaar draaien:

- **De busnaam is ...rockheaven-gnome.** De claim vraagt om de naam over
  te nemen van wie hem heeft, dus met dezelfde naam zou deze speler de
  bedieningsknoppen van de Avalonia-speler afpakken zodra hij start.
- **Identity zegt welke van de twee het is**, want een applet met twee
  regels die allebei RockHeaven heten is een applet waar niemand mee
  kan sturen. DesktopEntry blijft weg: die noemt een .desktop-bestand
  om een pictogram uit te halen, en deze speler heeft er geen.

De hoes gaat naar $XDG_RUNTIME_DIR/rockheaven-gnome/cover.png, met
opzet niet naar het bestand van de Avalonia-speler: één bestand voor
allebei zou ze elkaars hoes op de telefoon laten tonen.

Eén regel gaat ook in de Avalonia-speler, en die hoort daar thuis: de
**lengte** telt nu mee in wat de metadata laat veranderen. Die komt niet
met de track mee — mpv meldt hem een tel nadat het bestand open is — dus
wat er bij het starten uitgaat zegt er niets over, en iets moet hem
opnieuw sturen zodra hij bekend is. Zonder die test deed alleen de
*hoes* dat, en een track waarvan het bestand er geen heeft hield zolang
hij speelde de lengte van de vórige op de telefoon.

Draaiend nagekeken tegen de echte sessiebus: de naam, de metadata,
PlayPause, Play, Pause, Next, Previous, Seek, SetPosition — ook dat een
SetPosition voor een andere track genegeerd wordt — Volume beide kanten
op, Raise, en Quit, die het venster sluit, het proces beëindigt, mpv
stopt en de naam teruggeeft.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**Het raster** is hetzelfde paar als in de Avalonia-speler, met een knop in de
kop van de trackkolom. Welke van de twee aanstaat wordt niet onthouden tussen
runs: het is een manier van kijken naar de lijst die voor je staat, geen
instelling.

- **Beide views staan op hetzelfde selectiemodel**, en dat maakt het twee
  aanzichten van één lijst in plaats van twee lijsten: de rij die je in de een
  kiest staat in de ander gekozen, en een zoekopdracht versmalt ze allebei.
- **Een tegel haalt zijn hoes op als hij gebonden wordt**, en dat is met opzet
  niet wat de plaatjes in de zijbalk doen. Die lopen de hele lijst één voor één
  af, wat klopt voor een paar duizend artiestenplaatjes van 64 pixels die
  meestal al in de cache staan. Hier zou het een orde van grootte misgaan: een
  aftellijst is tweeduizend tracks en elke hoes is een megabyte over de lijn,
  dus de lijst aflopen haalt een paar gigabyte op voor een raster waar niemand
  in gescrold heeft. GTK hergebruikt zijn tegels en zegt daarmee precies welke
  er bekeken worden.
- **De hoes van een tegel wordt op 160 gedecodeerd waar die van de
  transportbalk op 512 gaat.** Zelfde verzoek en dus dezelfde cache-ingang;
  alleen het decoderen verschilt, en dat is de hele bedoeling.
- **Het invullen van de tags neemt het plaatje mee.** Dat bouwt verse rijen en
  splicet ze erin — zie de GObject-notitie — en een verse rij zonder de hoes
  zou er een weggooien die het raster al had.

**Zoeken is het eerste in deze repository.** Noch de Avalonia-speler noch de
webspeler heeft het, dus er was niets over te nemen en niets in de pas te
houden.

- **Een Gtk.FilterListModel staat tussen elke store en zijn selectie**, en dat
  geeft één regel waar de rest zich aan te houden heeft: **de store is elke rij
  die er is, het selectiemodel is de rijen op het scherm.** Alles wat "wat de
  luisteraar ziet" betekent — de selectie, de wachtrij, de rij waarop
  dubbelgeklikt is, het aantal in de kop — gaat langs het selectiemodel; alles
  wat "elke rij" betekent langs de store. `AdoptQueue` en `SelectSide` moesten
  daarvoor verhuizen: een positie in de store is iemand anders zijn rij op het
  scherm zodra er gefilterd wordt.
- **De wachtrij onder een zoekopdracht is wat er op het scherm staat**, wat
  rechtstreeks uit die regel volgt en klopt: zoek je drie tracks en druk je op
  spelen, dan speel je die drie.
- **Twee zoekbalken, één per paneel**, want ze beantwoorden verschillende
  vragen. Elk komt tevoorschijn met een knop in zijn eigen kop én door in zijn
  eigen paneel te typen — daarom vangt elke balk toetsen af op een **paneel** en
  niet op het venster, dat zou moeten raden voor welke lijst een toetsaanslag
  bedoeld was.
- **De zoekterm wordt gewist als de lijst eronder vervangen wordt**, en anders
  niet. Versmallen tot één artiest komt langs geen van beide paden, dus een
  zoekopdracht overleeft het klikken in zijn eigen resultaten.
- **De kop zegt "22 van 2000 tracks"** zolang er gezocht wordt; een kale "22
  tracks" boven een volume van honderd leest als een lijst die niet geladen is.

Draaiend nagekeken tegen de live server: het raster met zijn hoezen, beide
zoekbalken (in de zijbalk op "queen", in de tracks op "bowie" en "heroes"),
het aantal in de kop, en dat de plaatjes en de tegels elkaar niet in de weg
zitten. Geen enkele Gtk-CRITICAL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
De streep die deze speler bij zijn eerste versie trok — browsen, spelen,
hervatten en waarderen, en niets dat naar de bibliotheek schrijft — is met
opzet doorbroken. De scanknop met zijn vijf stappen, het beheervenster, de
bewerkdialoog en het verwijderen zitten er nu allemaal in, en daarmee zijn de
twee desktopspelers gelijken in plaats van dat de een de kijker van de ander
is. **De streep van de telefoon verschuift niet**, en om een reden die daar
nog steeds geldt: een run van uren en een modale bewerkdialoog zijn niet waar
een telefoon in een broekzak voor is.

Alles hiervan zit achter **CanCurate** — /api/session zegt of deze luisteraar
beheerder is, en zo niet dan gaan de scanknop, de beheerknop en de hele
knoppenkolom **uit de opmaak** in plaats van op grijs. Er valt hier niets te
verdienen, en een uitgeschakelde knop nodigt alleen maar uit tot de vraag.

- **De scanknop is zijn eigen stopknop, en één knop voor vijf stappen.** Hij
  vraagt POST /api/run: één verzoek en niet vijf, want vijf aanroepen op een
  rij vanuit een venster leggen de ketting in dát venster, en de speler
  sluiten liet de lopende stap alleen afmaken met niets dat de volgende
  startte.
- **Stoppen is een verzoek en niet de token.** Ophangen zou de taak laten
  doorlopen en de samenvatting kwijtmaken die hij nog schuldig is — en hoever
  een gestopte run gekomen is, is juist de reden om te stoppen en te kijken.
- **Het rapport is een eigen venster en modeloos**, dezelfde keuze als de kaart
  in de hoek bij de Avalonia-speler en om dezelfde reden: een run duurt uren en
  zijn samenvatting moet er nog staan als degene die hem startte terugkomt. Het
  wordt gevuld terwijl de run loopt, uit de tally die de server bij elke
  afgeronde stap meestuurt — dus een halverwege gestopte run rapporteert nog
  steeds de stappen die wél gebeurd zijn.
- **Een taak die de server al draait wordt opgepakt en gevolgd**, door elke
  seconde GET /api/jobs te vragen in plaats van te luisteren: het kanaal waar
  een taak in meldt heeft één lezer, en die stroom is van wie de run startte.
  Wat niet te herstellen valt is de samenvatting, dus de strook zegt wat er
  draait en hoever, en het rapport zegt alleen dát de run afliep.
  **De artwork-cache gaat alleen leeg voor een taak die schrijft** — de
  beheerveging is ook een taak, maar opent elk bestand en verandert er geen; een
  gigabyte aan bewaarde plaatjes weggooien zou kosten zonder opbrengst zijn.
- **Het beheervenster is modeloos en wordt één keer gebouwd**, dus de muziek en
  de lijsten erachter lopen door en een tweede druk op de knop haalt het
  bestaande venster naar voren. De twee redenen zijn twee schakelaars en geen
  kolom tekst, want "geen hoes" is een feit over het bestand en "verzamelaar"
  een vermoeden over de plaat — en een bewerkte regel wordt doorgestreept in
  plaats van weggehaald, want of hij nog aandacht vraagt is het antwoord van de
  volgende veging.
- **De bewerkdialoog is modaal**, waar het beheervenster dat niet is: het is
  één track en één antwoord. Beide opzoekingen zitten erin, het ophalen van de
  hoes, en "Van schijf" — dat déze machine doorbladert en niet die van de
  server. De knop "Hoes ophalen" is nooit uitgeschakeld bij gebrek aan een
  MusicBrainz-id: een plaat die niemand kon plaatsen is juist waarvoor je deze
  dialoog opent.
- **De dialoog krijgt de tags mee, niet alleen de track.** Jaar en commentaar
  staan in het bestand en niet in TrackDto, en de dialoog schrijft terug wat hij
  meekreeg — zonder die tags zou een albumtitel corrigeren de track zijn jaar en
  commentaar kosten.
- **Een knop per regel leest zijn regel bij de klik**, uit de Gtk.ListItem, die
  altijd weet welk item hij op dat moment toont. De regel in setup vangen zou
  voor altijd de eerste regel bewerken waaraan de knop gebonden werd.
- **Verwijderen vraagt eerst**, in een Adw.AlertDialog met het destructieve
  uiterlijk op het ene antwoord dat verwijdert.

Draaiend nagekeken tegen de live server: het beheervenster met de echte
aandachtslijst, de bewerkdialoog met velden, tags en hoes, de opzoeking op
titel (MusicBrainz antwoordde "...Like Clockwork (2013)"), de bevestiging van
het verwijderen, en — via de beheerveging, de enige lange taak die alleen
leest — de voortgangsstrook, het stoppen, en het oppakken van een taak die de
server al draaide. Het echte scannen en het verwijderen zijn niet uitgevoerd:
dat is werk aan zijn bibliotheek en geen proef.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`.forgejo/workflows/build-gnome.yml` is de derde .NET-workflow, naast die van
de speler en de server: een eigen workflow en niet een tweede taak in een
bestaande, want `run_number` telt per workflow en de tag-prefix is juist wat de
releases van de projecten uit elkaar houdt. Hij brengt `gnome-v<n>` uit met
`rockheaven-gnome-linux-x64-v<n>.tar.gz`, plus de PKGBUILD en SRCINFO als
release-assets, met het buildnummer en de checksum ingevuld.

Jouw ArchRepo stond klaar maar wees nog naar de speler: de tag was `player-v`,
de tarball heette `rockheaven-player-…`, en de afhankelijkheden misten de twee
die deze speler juist nodig heeft. Bijgewerkt:

- **`gtk4` en `libadwaita` staan er nu bij.** Het zijn *runtime*-afhankelijk-
  heden: GirCore is een binding en zoekt de bibliotheken pas bij het draaien op,
  dus dit bouwt op een machine die ze geen van beide heeft en start er niet. De
  CI-image hoeft ze dus ook niet te hebben, en de build draait in dezelfde
  `ci-dotnet-node-docker` als de andere twee.
- **De bron wijst naar `gnome-v<n>`** en naar de tarball van dit pakket.

En het pakket had niets om te installeren: dit project had geen `Assets/`. Die
zijn er nu — een desktop-bestand en het pictogram — en de csproj kopieert ze
naar de uitvoer, waar de PKGBUILD ze uit de tarball haalt.

- **Het desktop-bestand draagt `StartupWMClass=nl.ridderman.RockHeaven`**, de
  GApplication-id uit Program.cs, waar dat van de speler zijn uitvoerbare naam
  noemt. Een GTK4-programma onder Wayland ontleent zijn `app_id` aan die id, en
  daar matcht een shell een venster op; de uitvoerbare naam zetten laat het
  venster met een plaatshouder-pictogram achter.
- **Het pictogram is dat van de speler, onder de naam van dit pakket.** Het is
  hetzelfde programma; wat de twee in een menu uit elkaar houdt is de naam —
  "RockHeaven (GNOME)", precies wat `Identity` over MPRIS ook zegt.

Daarmee kan MPRIS ook `DesktopEntry` beantwoorden, wat tot nu toe wegbleef
omdat dit project geen entry had. Nagekeken: `GetAll` geeft hem nu, en de
klacht die KDE bij elke opvraging in de log zette is weg.

Draaiend nagekeken, niet alleen gebouwd: publish zoals de workflow hem doet,
de tarball ervan, de PKGBUILD ingevuld met sed, en daar `makepkg` op losgelaten.
Het pakket komt eruit met de launcher in /opt, de symlink in /usr/bin, het
desktop-bestand in /usr/share/applications en het pictogram in beide
hicolor-maten — en de binary uit dat pakket start, logt in en laadt de
bibliotheek. `desktop-file-validate` is tevreden en de YAML parst.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
roelof changed title from Er is nu ook een speler voor GNOME, en die cureert niets to Er is nu ook een speler voor GNOME 2026-08-13 16:14:10 +02:00
roelof merged commit 07b4f66d7e into main 2026-08-13 16:14:20 +02:00
roelof deleted branch gnome-speler 2026-08-13 16:14:20 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
KavalirOS/RockHeaven!108
No description provided.