import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:plezy/database/app_database.dart'; import 'package:plezy/media/ids.dart'; import 'package:plezy/models/audio_quality_preset.dart'; import 'package:plezy/services/plex_api_cache.dart'; import 'package:plezy/services/plex_client.dart'; import '../test_helpers/backend_client_fixtures.dart'; void main() { late AppDatabase db; setUp(() { db = AppDatabase.forTesting(NativeDatabase.memory()); PlexApiCache.initialize(db); }); tearDown(() async { await db.close(); }); PlexClient makeClient(Future Function(http.Request request) handler) => testPlexClient(serverId: ServerId('server-id'), handler: handler); test('music transcode params cap bitrate and carry the musicProfile target', () { final client = makeClient((_) async => http.Response('not used', 500)); addTearDown(client.close); final params = client.buildMusicTranscodeParamsForTesting( ratingKey: '9669', mediaIndex: 0, preset: AudioQualityPreset.medium, sessionIdentifier: 'session-id', transcodeSessionId: 'transcode-id', ); expect(params['hasMDE'], '1'); expect(params['path'], '/library/metadata/9669'); expect(params['mediaIndex'], '0'); expect(params['partIndex'], '0'); expect(params['protocol'], 'http'); expect(params['directPlay'], '0'); expect(params['directStream'], '0'); expect(params['musicBitrate'], '192'); expect(params['session'], 'transcode-id'); expect(params['X-Plex-Session-Identifier'], 'session-id'); expect( params['X-Plex-Client-Profile-Extra'], 'add-transcode-target(type=musicProfile&context=streaming' '&protocol=http&container=mp3&audioCodec=mp3)', ); }); test('music transcode params carry no video/subtitle params', () { final client = makeClient((_) async => http.Response('not used', 500)); addTearDown(client.close); final params = client.buildMusicTranscodeParamsForTesting( ratingKey: '9669', mediaIndex: 0, preset: AudioQualityPreset.high, sessionIdentifier: 'session-id', transcodeSessionId: 'transcode-id', ); expect(params['musicBitrate'], '320'); for (final videoOnly in ['subtitles', 'subtitleStreamID', 'advancedSubtitles', 'copyts', 'maxVideoBitrate']) { expect(params.containsKey(videoOnly), isFalse, reason: '$videoOnly is video-only'); } expect(params['X-Plex-Client-Profile-Extra'], isNot(contains('videoProfile'))); }); test('original preset omits musicBitrate', () { final client = makeClient((_) async => http.Response('not used', 500)); addTearDown(client.close); final params = client.buildMusicTranscodeParamsForTesting( ratingKey: '9669', mediaIndex: 0, preset: AudioQualityPreset.original, sessionIdentifier: 'session-id', transcodeSessionId: 'transcode-id', ); expect(params.containsKey('musicBitrate'), isFalse); }); test('music start path uses the mp3 start endpoint without token', () { final client = makeClient((_) async => http.Response('not used', 500)); addTearDown(client.close); final params = client.buildMusicTranscodeParamsForTesting( ratingKey: '9669', mediaIndex: 1, partIndex: 2, preset: AudioQualityPreset.medium, sessionIdentifier: 'session-id', transcodeSessionId: 'transcode-id', ); final startPath = client.buildTranscodeStartPathFromParamsForTesting( params, endpoint: '/music/:/transcode/universal/start.mp3', ); expect(startPath, startsWith('/music/:/transcode/universal/start.mp3?')); expect(startPath, contains('musicBitrate=192')); expect(startPath, contains('mediaIndex=1')); expect(startPath, contains('partIndex=2')); // Profile-extra parens/ampersands must be percent-encoded on the wire. expect( startPath, contains( 'X-Plex-Client-Profile-Extra=add-transcode-target%28type%3DmusicProfile%26context%3Dstreaming' '%26protocol%3Dhttp%26container%3Dmp3%26audioCodec%3Dmp3%29', ), ); expect(startPath, isNot(contains('X-Plex-Token'))); }); }