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.
127 lines
3.9 KiB
Objective-C
127 lines
3.9 KiB
Objective-C
// Copyright (c) 2025 Jonas van den Berg
|
|
// This file is licensed under the BSD 3-Clause License.
|
|
|
|
#ifndef MEDIAREMOTEADAPTER_ADAPTER_H
|
|
#define MEDIAREMOTEADAPTER_ADAPTER_H
|
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
// Methods suffixed with "_env" read its parameters from the environment.
|
|
// Parameters must have the format:
|
|
// MEDIAREMOTEADAPTER_<FUNC_NAME>_<PARAM_INDEX>_<PARAM_NAME>
|
|
// Example: MEDIAREMOTEADAPTER_adapter_send_0_command
|
|
|
|
extern NSString *kMRAProcessIdentifier;
|
|
extern NSString *kMRABundleIdentifier;
|
|
extern NSString *kMRAParentApplicationBundleIdentifier;
|
|
extern NSString *kMRAPlaying;
|
|
|
|
extern NSString *kMRADurationMicros;
|
|
extern NSString *kMRAElapsedTimeMicros;
|
|
extern NSString *kMRATimestampEpochMicros;
|
|
|
|
extern NSString *kMRAElapsedTimeNow;
|
|
extern NSString *kMRAElapsedTimeNowMicros;
|
|
|
|
extern NSString *kMRATitle;
|
|
extern NSString *kMRAArtist;
|
|
extern NSString *kMRAAlbum;
|
|
extern NSString *kMRADuration;
|
|
extern NSString *kMRAElapsedTime;
|
|
extern NSString *kMRATimestamp;
|
|
extern NSString *kMRAArtworkMimeType;
|
|
extern NSString *kMRAArtworkData;
|
|
|
|
extern NSString *kMRAChapterNumber;
|
|
extern NSString *kMRAComposer;
|
|
extern NSString *kMRAGenre;
|
|
extern NSString *kMRAIsAdvertisement;
|
|
extern NSString *kMRAIsBanned;
|
|
extern NSString *kMRAIsInWishList;
|
|
extern NSString *kMRAIsLiked;
|
|
extern NSString *kMRAIsMusicApp;
|
|
extern NSString *kMRAPlaybackRate;
|
|
extern NSString *kMRAProhibitsSkip;
|
|
extern NSString *kMRAQueueIndex;
|
|
extern NSString *kMRARadioStationIdentifier;
|
|
extern NSString *kMRARepeatMode;
|
|
extern NSString *kMRAShuffleMode;
|
|
extern NSString *kMRAStartTime;
|
|
extern NSString *kMRASupportsFastForward15Seconds;
|
|
extern NSString *kMRASupportsIsBanned;
|
|
extern NSString *kMRASupportsIsLiked;
|
|
extern NSString *kMRASupportsRewind15Seconds;
|
|
extern NSString *kMRATotalChapterCount;
|
|
extern NSString *kMRATotalDiscCount;
|
|
extern NSString *kMRATotalQueueCount;
|
|
extern NSString *kMRATotalTrackCount;
|
|
extern NSString *kMRATrackNumber;
|
|
extern NSString *kMRAUniqueIdentifier;
|
|
extern NSString *kMRAContentItemIdentifier;
|
|
extern NSString *kMRARadioStationHash;
|
|
extern NSString *kMRAMediaType;
|
|
|
|
// Prints the current MediaRemote now playing information to stdout.
|
|
// Data is encoded as a JSON dictionary or "null" when there is no information.
|
|
extern void adapter_get();
|
|
extern void adapter_get_env();
|
|
|
|
// Streams MediaRemote now playing updates to stdout.
|
|
// Each update is printed on a separate lined, encoded as a JSON dictionary.
|
|
// Exits when the process receives a SIGTERM signal.
|
|
extern void adapter_stream();
|
|
extern void adapter_stream_env();
|
|
|
|
typedef enum {
|
|
kMRAPlay = 0,
|
|
kMRAPause = 1,
|
|
kMRATogglePlayPause = 2,
|
|
kMRAStop = 3,
|
|
kMRANextTrack = 4,
|
|
kMRAPreviousTrack = 5,
|
|
kMRAToggleShuffle = 6,
|
|
kMRAToggleRepeat = 7,
|
|
kMRAStartForwardSeek = 8,
|
|
kMRAEndForwardSeek = 9,
|
|
kMRAStartBackwardSeek = 10,
|
|
kMRAEndBackwardSeek = 11,
|
|
kMRAGoBackFifteenSeconds = 12,
|
|
kMRASkipFifteenSeconds = 13,
|
|
} MRACommand;
|
|
|
|
// Sends the given MediaRemote command to the current now playing application.
|
|
extern void adapter_send(MRACommand command);
|
|
extern void adapter_send_env();
|
|
|
|
// Seeks the timeline of the nowplaying application to the given position.
|
|
// The position must be given in microseconds.
|
|
extern void adapter_seek(long position);
|
|
extern void adapter_seek_env();
|
|
|
|
typedef enum {
|
|
kMRAShuffleDisabled = 1,
|
|
kMRAShuffleAlbums = 2,
|
|
kMRAShuffleTracks = 3,
|
|
} MRAShuffleMode;
|
|
|
|
extern void adapter_shuffle(MRAShuffleMode mode);
|
|
extern void adapter_shuffle_env();
|
|
|
|
typedef enum {
|
|
kMRARepeatDisabled = 1,
|
|
kMRARepeatTrack = 2,
|
|
kMRARepeatPlaylist = 3,
|
|
} MRARepeatMode;
|
|
|
|
extern void adapter_repeat(MRARepeatMode mode);
|
|
extern void adapter_repeat_env();
|
|
|
|
extern void adapter_speed(int speed);
|
|
extern void adapter_speed_env();
|
|
|
|
// Tests whether the process is entitled to use the MediaRemote framework.
|
|
// Exits with exit code 0, if it is. Any other exit code means it is not.
|
|
extern void adapter_test();
|
|
|
|
#endif // MEDIAREMOTEADAPTER_ADAPTER_H
|