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:
62
Vendor/mediaremote-adapter/scripts/update-readme-badges.py
vendored
Executable file
62
Vendor/mediaremote-adapter/scripts/update-readme-badges.py
vendored
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import urllib.parse
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
STATIC_BADGES = """
|
||||

|
||||
"""
|
||||
|
||||
|
||||
def get_output(command):
|
||||
result = subprocess.run(
|
||||
command,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def normalize_whitespace(text):
|
||||
return re.sub(r'\s+', ' ', text.strip())
|
||||
|
||||
|
||||
def generate_badges():
|
||||
system_version = get_output(
|
||||
"system_profiler SPSoftwareDataType | sed -En 's/.*System Version: *//p'"
|
||||
)
|
||||
date_output = normalize_whitespace(get_output("date"))
|
||||
encoded_system_version = urllib.parse.quote(system_version)
|
||||
encoded_date = urllib.parse.quote(date_output)
|
||||
badge1 = f""
|
||||
badge2 = f""
|
||||
return f"{STATIC_BADGES.strip()}\n{badge1}\n{badge2}"
|
||||
|
||||
|
||||
def update_readme(script_dir, new_badges):
|
||||
readme_path = os.path.join(script_dir, "..", "README.md")
|
||||
with open(readme_path, "r") as file:
|
||||
content = file.read()
|
||||
new_content = re.sub(
|
||||
r"<!-- BADGES BEGIN -->.*?<!-- BADGES END -->",
|
||||
f"<!-- BADGES BEGIN -->\n{new_badges}\n<!-- BADGES END -->",
|
||||
content,
|
||||
flags=re.DOTALL,
|
||||
)
|
||||
with open(readme_path, "w") as file:
|
||||
file.write(new_content)
|
||||
|
||||
|
||||
def main():
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
badges = generate_badges()
|
||||
update_readme(script_dir, badges)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user