From 27979abecb75263620078038e2094f71b94c1fd0 Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Tue, 8 Oct 2024 17:33:03 +0200 Subject: [PATCH 1/2] add setting for switching segment container, default to fmp4 - this should hopefully fix #600, and maybe some other issues too --- lib/gen/assets.gen.dart | 21 +++++++-- lib/l10n/app_en.arb | 5 ++ lib/models/finamp_models.dart | 23 ++++++++- lib/models/finamp_models.g.dart | 49 +++++++++++++++++++- lib/screens/transcoding_settings_screen.dart | 40 ++++++++++++++++ lib/services/queue_service.dart | 3 +- 6 files changed, 133 insertions(+), 8 deletions(-) diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 6449e43d0..447cb0e12 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -19,16 +19,24 @@ class $ImagesGen { AssetGenImage get finamp => const AssetGenImage('images/finamp.png'); /// File path: images/finamp_cropped.png - AssetGenImage get finampCropped => + AssetGenImage get finampCroppedPng => const AssetGenImage('images/finamp_cropped.png'); + /// File path: images/finamp_cropped.svg + String get finampCroppedSvg => 'images/finamp_cropped.svg'; + /// File path: images/jellyfin-icon-transparent.png AssetGenImage get jellyfinIconTransparent => const AssetGenImage('images/jellyfin-icon-transparent.png'); /// List of all assets - List get values => - [albumWhite, finamp, finampCropped, jellyfinIconTransparent]; + List get values => [ + albumWhite, + finamp, + finampCroppedPng, + finampCroppedSvg, + jellyfinIconTransparent + ]; } class Assets { @@ -38,11 +46,16 @@ class Assets { } class AssetGenImage { - const AssetGenImage(this._assetName, {this.size = null}); + const AssetGenImage( + this._assetName, { + this.size, + this.flavors = const {}, + }); final String _assetName; final Size? size; + final Set flavors; Image image({ Key? key, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7638511fe..cad9562fd 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1375,6 +1375,11 @@ }, "description": "Localized names of special downloadable collection representing cached images for a certain library." }, + "transcodingStreamingContainerTitle": "Select Transcoding Container", + "@transcodingStreamingContainerTitle": { + "description": "Title for the dropdown that selects the container format for transcoded streams" + }, + "transcodingStreamingContainerSubtitle": "Select the segment container to use when streaming transcoded audio. Already queued tracks will not be affected.", "downloadTranscodeEnableTitle": "Enable Transcoded Downloads", "@downloadTranscodeEnableTitle": { "description": "Title for Enable Transcoded Downloads dropdown" diff --git a/lib/models/finamp_models.dart b/lib/models/finamp_models.dart index 41d13e3f7..16667d7e2 100644 --- a/lib/models/finamp_models.dart +++ b/lib/models/finamp_models.dart @@ -113,6 +113,8 @@ const _showStopButtonOnMediaNotificationDefault = false; const _showSeekControlsOnMediaNotificationDefault = true; const _keepScreenOnOption = KeepScreenOnOption.whileLyrics; const _keepScreenOnWhilePluggedIn = true; +const _defaultTranscodingSegmentContainer = + FinampSegmentContainer.fragmentedMp4; @HiveType(typeId: 28) class FinampSettings { @@ -196,7 +198,9 @@ class FinampSettings { this.showSeekControlsOnMediaNotification = _showSeekControlsOnMediaNotificationDefault, this.keepScreenOnOption = _keepScreenOnOption, - this.keepScreenOnWhilePluggedIn = _keepScreenOnWhilePluggedIn}); + this.keepScreenOnWhilePluggedIn = _keepScreenOnWhilePluggedIn, + this.transcodingSegmentContainer = _defaultTranscodingSegmentContainer, + }); @HiveField(0, defaultValue: _isOfflineDefault) bool isOffline; @@ -433,6 +437,9 @@ class FinampSettings { @HiveField(73, defaultValue: _keepScreenOnWhilePluggedIn) bool keepScreenOnWhilePluggedIn; + @HiveField(74, defaultValue: _defaultTranscodingSegmentContainer) + FinampSegmentContainer transcodingSegmentContainer; + static Future create() async { final downloadLocation = await DownloadLocation.create( name: "Internal Storage", @@ -2234,3 +2241,17 @@ enum KeepScreenOnOption { } } } + + +@HiveType(typeId: 73) +enum FinampSegmentContainer { + @HiveField(0) + mpegTS("ts"), + @HiveField(1) + fragmentedMp4("mp4"); + + const FinampSegmentContainer(this.container); + + /// The container to use to transport the segments + final String container; +} diff --git a/lib/models/finamp_models.g.dart b/lib/models/finamp_models.g.dart index 4c9612b83..b2f922687 100644 --- a/lib/models/finamp_models.g.dart +++ b/lib/models/finamp_models.g.dart @@ -185,6 +185,9 @@ class FinampSettingsAdapter extends TypeAdapter { : fields[72] as KeepScreenOnOption, keepScreenOnWhilePluggedIn: fields[73] == null ? true : fields[73] as bool, + transcodingSegmentContainer: fields[74] == null + ? FinampSegmentContainer.fragmentedMp4 + : fields[74] as FinampSegmentContainer, ) ..disableGesture = fields[19] == null ? false : fields[19] as bool ..showFastScroller = fields[25] == null ? true : fields[25] as bool @@ -194,7 +197,7 @@ class FinampSettingsAdapter extends TypeAdapter { @override void write(BinaryWriter writer, FinampSettings obj) { writer - ..writeByte(72) + ..writeByte(73) ..writeByte(0) ..write(obj.isOffline) ..writeByte(1) @@ -338,7 +341,9 @@ class FinampSettingsAdapter extends TypeAdapter { ..writeByte(72) ..write(obj.keepScreenOnOption) ..writeByte(73) - ..write(obj.keepScreenOnWhilePluggedIn); + ..write(obj.keepScreenOnWhilePluggedIn) + ..writeByte(74) + ..write(obj.transcodingSegmentContainer); } @override @@ -1878,6 +1883,46 @@ class KeepScreenOnOptionAdapter extends TypeAdapter { typeId == other.typeId; } +class FinampSegmentContainerAdapter + extends TypeAdapter { + @override + final int typeId = 73; + + @override + FinampSegmentContainer read(BinaryReader reader) { + switch (reader.readByte()) { + case 0: + return FinampSegmentContainer.mpegTS; + case 1: + return FinampSegmentContainer.fragmentedMp4; + default: + return FinampSegmentContainer.mpegTS; + } + } + + @override + void write(BinaryWriter writer, FinampSegmentContainer obj) { + switch (obj) { + case FinampSegmentContainer.mpegTS: + writer.writeByte(0); + break; + case FinampSegmentContainer.fragmentedMp4: + writer.writeByte(1); + break; + } + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FinampSegmentContainerAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + // ************************************************************************** // IsarCollectionGenerator // ************************************************************************** diff --git a/lib/screens/transcoding_settings_screen.dart b/lib/screens/transcoding_settings_screen.dart index c595dd0af..63b07c989 100644 --- a/lib/screens/transcoding_settings_screen.dart +++ b/lib/screens/transcoding_settings_screen.dart @@ -24,6 +24,7 @@ class TranscodingSettingsScreen extends StatelessWidget { children: [ const TranscodeSwitch(), const BitrateSelector(), + const StreamingTranscodeSegmentContainerDropdownListTile(), Padding( padding: const EdgeInsets.all(8.0), child: Text( @@ -165,3 +166,42 @@ class DownloadTranscodeCodecDropdownListTile extends StatelessWidget { ); } } + +class StreamingTranscodeSegmentContainerDropdownListTile + extends StatelessWidget { + const StreamingTranscodeSegmentContainerDropdownListTile({super.key}); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (_, box, __) { + final finampSettings = box.get("FinampSettings")!; + + return ListTile( + title: Text( + AppLocalizations.of(context)!.transcodingStreamingContainerTitle), + subtitle: Text(AppLocalizations.of(context)! + .transcodingStreamingContainerSubtitle), + trailing: DropdownButton( + value: finampSettings.transcodingSegmentContainer, + items: FinampSegmentContainer.values + .map((e) => DropdownMenuItem( + value: e, + child: Text(e.container!.toUpperCase()), + )) + .toList(), + onChanged: (value) { + if (value != null) { + FinampSettings finampSettingsTemp = finampSettings; + finampSettingsTemp.transcodingSegmentContainer = value; + Hive.box("FinampSettings") + .put("FinampSettings", finampSettingsTemp); + } + }, + ), + ); + }, + ); + } +} diff --git a/lib/services/queue_service.dart b/lib/services/queue_service.dart index 98a3b65f5..48c9d09a8 100644 --- a/lib/services/queue_service.dart +++ b/lib/services/queue_service.dart @@ -1160,7 +1160,8 @@ class QueueService { "maxAudioBitDepth": "16", "audioBitRate": FinampSettingsHelper.finampSettings.transcodeBitrate.toString(), - "segmentContainer": "ts", + "segmentContainer": FinampSettingsHelper + .finampSettings.transcodingSegmentContainer.container, "transcodeReasons": "ContainerBitrateExceedsLimit", }); } else { From 49a26ae34bad285a55a7457ef3fd04b59ea74ddd Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Fri, 25 Oct 2024 22:02:40 +0200 Subject: [PATCH 2/2] register missing adapter --- lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/main.dart b/lib/main.dart index 298d1e92f..da46ca791 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -221,6 +221,7 @@ Future setupHive() async { Hive.registerAdapter(LyricsAlignmentAdapter()); Hive.registerAdapter(LyricsFontSizeAdapter()); Hive.registerAdapter(KeepScreenOnOptionAdapter()); + Hive.registerAdapter(FinampSegmentContainerAdapter()); final dir = (Platform.isAndroid || Platform.isIOS) ? await getApplicationDocumentsDirectory()