Phase 4: Medien-Widget mit dreistufiger Kaskade

Adapter für alles inklusive Browser, AppleScript für Spotify und Musik,
Medientasten als letzte Reserve. Die Reihenfolge ist keine Vorliebe, sondern
Absicherung: Apple hat die Now-Playing-Schnittstelle in macOS 15.4 schon
einmal zugemacht. Fällt der Adapter aus, verliert das Widget seine Anzeige —
aber nicht seine Funktion. Deshalb bleiben die Transporttasten auch dann
bedienbar, wenn gar keine Metadaten da sind.

Der Fortschritt zählt zwischen den Ereignissen selbst hoch. Spike B hatte
gezeigt, dass elapsedTime nicht laufend nachgeliefert wird: ein Scrubber, der
stur den letzten Wert anzeigt, steht still, während der Titel läuft. Gerechnet
wird aus asOf und playbackRate, begrenzt auf die Titellänge — sonst zeigt die
Anzeige nach dem Ende weiter steigende Zahlen.

Teilmeldungen (diff = true) ergänzen den Stand, statt ihn zu ersetzen. Eine
Meldung mit nur der Position würde sonst Titel, Interpret und Cover löschen.

In AnyDecodable wird Bool vor Double geprüft. Andernfalls wird "playing": true
zu 1 und die Wiedergabe gilt für immer als angehalten.

Play/Pause schaltet sofort um, statt auf das nächste Ereignis zu warten. Ein
Knopf, der eine halbe Sekunde später reagiert, fühlt sich kaputt an; das echte
Ereignis korrigiert den Stand ohnehin.

Der Adapter liegt als Quellcode unter Vendor/ (BSD-3, Stand in
ONYX-VERSION.txt) und wird beim Bauen kompiliert, statt als fertige Binary
eingecheckt zu werden. Er wird bewusst nicht gelinkt: geladen wird er von
/usr/bin/perl, im eigenen Prozess greift Apples Entitlement-Prüfung.

124 Tests grün.
This commit is contained in:
Guido Schmit
2026-08-10 21:02:13 +02:00
parent 03dd7b945e
commit 433cdcc3dd
53 changed files with 4888 additions and 2 deletions

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2025 Jonas van den Berg
// This file is licensed under the BSD 3-Clause License.
#include "private/MediaRemote.h"
#include <limits.h>
#import <Foundation/Foundation.h>
#import "MediaRemoteAdapter.h"
#import "adapter/env.h"
#import "adapter/globals.h"
#import "adapter/now_playing.h"
#import "utility/helpers.h"
static NSArray<NSNumber *> *acceptedModes;
__attribute__((constructor)) static void init() {
acceptedModes = @[
@(kMRARepeatDisabled),
@(kMRARepeatTrack),
@(kMRARepeatPlaylist),
];
}
static bool isModeAccepted(int mode) {
return [acceptedModes containsObject:@(mode)];
}
void adapter_repeat(MRARepeatMode mode) {
if (!isModeAccepted((int)mode)) {
failf(@"Invalid repeat mode: %d", (int)mode);
}
g_mediaRemote.setRepeatMode((int)mode);
waitForCommandCompletion();
}
static inline int repeat_0_mode() {
return getEnvFuncParamIntSafe(@"adapter_repeat", 0, @"mode");
}
void adapter_repeat_env() { adapter_repeat((MRARepeatMode)repeat_0_mode()); }