YtDownloadService: Gast-Zugang neben Baka-Auth unterstützen

Optionaler gast-Parameter (GastZugang) — sendet bei fehlender
Baka-Anmeldung X-Guest-Token statt Authorization, übernimmt
guest_remaining aus der Erfolgsantwort, holt bei 401 automatisch
einen neuen Gast-Token nach.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Hermes (Server)
2026-08-26 20:45:52 +02:00
co-authored by Claude Sonnet 5
parent 2e14256ded
commit 1ff03c96ed
2 changed files with 112 additions and 5 deletions
+96
View File
@@ -6,6 +6,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:melo/services/baka_auth.dart';
import 'package:melo/services/gast_zugang.dart';
import 'package:melo/services/yt_download_service.dart';
class _MemorySpeicher implements TokenSpeicher {
@@ -284,4 +285,99 @@ void main() {
expect((jsonDecode(auftrag!) as Map)['cookies'], isFalse);
});
group('Gast-Zugang', () {
test('Mit Gast-Token statt Anmeldung wird der Proxy trotzdem gefragt',
() async {
final speicher = _MemorySpeicher();
speicher.werte['guest_token'] = 'gast-xyz';
final gast = GastZugang(
client: MockClient((_) async => http.Response('', 500)),
speicher: speicher,
);
await gast.laden();
final dienst = YtDownloadService(
auth: BakaAuth(
client: MockClient((_) async => http.Response('', 500)),
speicher: _MemorySpeicher()),
gast: gast,
client: MockClient((req) async {
if (req.url.path == '/api/yt-dl') {
expect(req.headers['X-Guest-Token'], 'gast-xyz');
expect(req.headers.containsKey('Authorization'), isFalse);
return http.Response(
jsonEncode({
'titel': 'Gast-Lied',
'dauer': 30,
'mp3_url': '/api/dl/g.mp3',
'guest_remaining': 4,
}),
200);
}
return http.Response.bytes([1], 200);
}),
);
final ergebnis = await dienst.herunterladen('https://youtu.be/abc',
zielOrdner: ziel.path);
expect(ergebnis, isNotNull);
expect(gast.verbleibend, 4);
});
test('Ohne Anmeldung UND ohne Gast-Token wird gar nicht erst gefragt',
() async {
var aufrufe = 0;
final dienst = YtDownloadService(
auth: BakaAuth(
client: MockClient((_) async => http.Response('', 500)),
speicher: _MemorySpeicher()),
client: MockClient((_) async {
aufrufe++;
return http.Response('', 200);
}),
);
final ergebnis = await dienst.herunterladen('https://youtu.be/abc',
zielOrdner: ziel.path);
expect(ergebnis, isNull);
expect(dienst.fehler, 'Bitte zuerst beim Baka-Konto anmelden');
expect(aufrufe, 0);
});
test('401 bei Gast-Zugang holt automatisch einen neuen Gast-Token',
() async {
var tokenAufrufe = 0;
final speicher = _MemorySpeicher();
speicher.werte['guest_token'] = 'alter-token';
final gast = GastZugang(
client: MockClient((_) async {
tokenAufrufe++;
return http.Response(
jsonEncode({'guest_token': 'neuer-token'}), 200);
}),
speicher: speicher,
);
await gast.laden();
final dienst = YtDownloadService(
auth: BakaAuth(
client: MockClient((_) async => http.Response('', 500)),
speicher: _MemorySpeicher()),
gast: gast,
client: MockClient((_) async => http.Response('Unauthorized', 401)),
);
final ergebnis = await dienst.herunterladen('https://youtu.be/abc',
zielOrdner: ziel.path);
// Das Token-Nachholen läuft unawaited im Hintergrund.
await Future<void>.delayed(Duration.zero);
expect(ergebnis, isNull);
expect(dienst.fehler, 'Gast-Zugang abgelaufen — bitte erneut versuchen');
expect(tokenAufrufe, 1);
});
});
}