Files
Melo/lib/services/geteilte_adresse.dart
Hermes (Server) 5645660a2d UX-Simulation, Teil 2: Player-Feinschliff, Downloads, Einstellungen
Zweiter Block aus der UX-Simulation (39 Funde insgesamt):

- Songtext-Icon deaktiviert statt totem Knopf, wenn kein Ziel bekannt ist
- Sleep-Timer-Restzeit jetzt auch im Sheet sichtbar, nicht nur im Tooltip
- Drag-Handle in der Warteschlange, Wisch-nach-unten schließt den
  Vollbild-Player
- Fehlender Tooltip am Songtext-Sheet-Schließen-Knopf ergänzt
- YouTube-Download: Zeitüberschreitung meldet sich anders als toter Server
- Server-Tab zeigte beim Öffnen kurz fälschlich "nicht verbunden"
- Einstellungen: Favoriten-/Playlisten-Sync von "Speicher" zu
  "Musikserver" verschoben, Navidrome-Eintrag erklärt jetzt seinen Zweck
- Kommentar in geteilte_adresse.dart korrigiert (Verhalten unverändert,
  dafür gibt es einen Test)

Bewusst zurückgestellt: ServerFavoriteButton bleibt lokal-only (dokumentierte
Architektur-Entscheidung, siehe G3 im UX-Review), Live-Wischgeste am
Mini-Player, YouTube-Download-Abbrechen, Download-Badge im inaktiven
Unterreiter, Versionsnummer (neue Abhängigkeit nötig).

539 Tests grün (vorher 537), flutter analyze ohne Befund.
2026-08-25 09:49:47 +02:00

51 lines
1.9 KiB
Dart

import 'package:flutter/services.dart';
/// Erste Web-Adresse in [text], oder `null`.
///
/// Wer ein Video an Melo teilt, schickt selten nur die Adresse — die
/// YouTube-App hängt Titel und „über @YouTube" drumherum.
///
/// Bewusst **nicht** auf youtube.com eingeschränkt: das Herauslösen der
/// Adresse aus dem geteilten Text ist unabhängig von ihrer Herkunft. Ob eine
/// nicht-YouTube-Adresse tatsächlich heruntergeladen werden kann, entscheidet
/// [YtDownloadService.herunterladen] — der lehnt sie heute client-seitig ab
/// (siehe dort), unabhängig davon, was der Server könnte.
String? adresseAus(String? text) {
if (text == null || text.isEmpty) return null;
final treffer = _adresse.firstMatch(text);
if (treffer == null) return null;
// Satzzeichen am Ende gehören zum Satz, nicht zur Adresse.
return treffer.group(0)!.replaceAll(RegExp(r'[.,;:!?)\]]+$'), '');
}
final _adresse = RegExp(r'https?://\S+');
/// Nimmt entgegen, was andere Apps über „Teilen" an Melo schicken.
///
/// Eigene Brücke statt eines Zusatzpakets — das Projekt hat für MediaStore
/// und Klangeffekte bereits eigene (siehe `MediaStoreBridge`).
class GeteilteAdresse {
const GeteilteAdresse({this.channel = const MethodChannel(kanalName)});
static const kanalName = 'de.baka.melo/geteilt';
final MethodChannel channel;
/// Die Adresse, mit der die App gerade geöffnet wurde, oder `null`.
///
/// Wird beim Abholen auf der Android-Seite gelöscht: sonst spränge die App
/// bei jedem Wiedereintritt erneut in den Downloader.
Future<String?> abholen() async {
try {
final text = await channel.invokeMethod<String>('hole');
return adresseAus(text);
} on PlatformException {
return null;
} on MissingPluginException {
// Andere Plattformen kennen die Brücke nicht — dort gibt es kein
// Teilen an Melo, und das ist kein Fehler.
return null;
}
}
}