diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index a400fc36..6aaf8d0c 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1387,6 +1387,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/main.dart b/lib/main.dart index e1bbb61a..1fdb2af9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -218,6 +218,7 @@ Future setupHive() async { Hive.registerAdapter(LyricsAlignmentAdapter()); Hive.registerAdapter(LyricsFontSizeAdapter()); Hive.registerAdapter(KeepScreenOnOptionAdapter()); + Hive.registerAdapter(FinampSegmentContainerAdapter()); Hive.registerAdapter(FinampFeatureChipsConfigurationAdapter()); Hive.registerAdapter(FinampFeatureChipTypeAdapter()); diff --git a/lib/models/finamp_models.dart b/lib/models/finamp_models.dart index 398973ad..2d78ebbc 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; const _featureChipsConfigurationDefault = FinampFeatureChipsConfiguration(enabled: true, features: [ FinampFeatureChipType.playCount, @@ -209,6 +211,7 @@ class FinampSettings { _showSeekControlsOnMediaNotificationDefault, this.keepScreenOnOption = _keepScreenOnOption, this.keepScreenOnWhilePluggedIn = _keepScreenOnWhilePluggedIn, + this.transcodingSegmentContainer = _defaultTranscodingSegmentContainer, this.featureChipsConfiguration = _featureChipsConfigurationDefault}); @HiveField(0, defaultValue: _isOfflineDefault) @@ -446,7 +449,10 @@ class FinampSettings { @HiveField(73, defaultValue: _keepScreenOnWhilePluggedIn) bool keepScreenOnWhilePluggedIn; - @HiveField(74, defaultValue: _featureChipsConfigurationDefault) + @HiveField(74, defaultValue: _defaultTranscodingSegmentContainer) + FinampSegmentContainer transcodingSegmentContainer; + + @HiveField(75, defaultValue: _featureChipsConfigurationDefault) FinampFeatureChipsConfiguration featureChipsConfiguration; static Future create() async { @@ -2252,6 +2258,19 @@ 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; +} + +@HiveType(typeId: 74) enum FinampFeatureChipType { @HiveField(0) playCount, @@ -2329,7 +2348,7 @@ enum FinampFeatureChipType { } @JsonSerializable() -@HiveType(typeId: 74) +@HiveType(typeId: 75) class FinampFeatureChipsConfiguration { const FinampFeatureChipsConfiguration({ required this.enabled, diff --git a/lib/models/finamp_models.g.dart b/lib/models/finamp_models.g.dart index f70cd199..5d98677c 100644 --- a/lib/models/finamp_models.g.dart +++ b/lib/models/finamp_models.g.dart @@ -185,7 +185,10 @@ class FinampSettingsAdapter extends TypeAdapter { : fields[72] as KeepScreenOnOption, keepScreenOnWhilePluggedIn: fields[73] == null ? true : fields[73] as bool, - featureChipsConfiguration: fields[74] == null + transcodingSegmentContainer: fields[74] == null + ? FinampSegmentContainer.fragmentedMp4 + : fields[74] as FinampSegmentContainer, + featureChipsConfiguration: fields[75] == null ? const FinampFeatureChipsConfiguration(enabled: true, features: [ FinampFeatureChipType.playCount, FinampFeatureChipType.additionalPeople, @@ -197,7 +200,7 @@ class FinampSettingsAdapter extends TypeAdapter { FinampFeatureChipType.size, FinampFeatureChipType.normalizationGain ]) - : fields[74] as FinampFeatureChipsConfiguration, + : fields[75] as FinampFeatureChipsConfiguration, ) ..disableGesture = fields[19] == null ? false : fields[19] as bool ..showFastScroller = fields[25] == null ? true : fields[25] as bool @@ -207,7 +210,7 @@ class FinampSettingsAdapter extends TypeAdapter { @override void write(BinaryWriter writer, FinampSettings obj) { writer - ..writeByte(73) + ..writeByte(74) ..writeByte(0) ..write(obj.isOffline) ..writeByte(1) @@ -353,6 +356,8 @@ class FinampSettingsAdapter extends TypeAdapter { ..writeByte(73) ..write(obj.keepScreenOnWhilePluggedIn) ..writeByte(74) + ..write(obj.transcodingSegmentContainer) + ..writeByte(75) ..write(obj.featureChipsConfiguration); } @@ -978,7 +983,7 @@ class MediaItemIdAdapter extends TypeAdapter { class FinampFeatureChipsConfigurationAdapter extends TypeAdapter { @override - final int typeId = 74; + final int typeId = 75; @override FinampFeatureChipsConfiguration read(BinaryReader reader) { @@ -1931,10 +1936,50 @@ class KeepScreenOnOptionAdapter extends TypeAdapter { typeId == other.typeId; } -class FinampFeatureChipTypeAdapter extends TypeAdapter { +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; +} + +class FinampFeatureChipTypeAdapter extends TypeAdapter { + @override + final int typeId = 74; + @override FinampFeatureChipType read(BinaryReader reader) { switch (reader.readByte()) { diff --git a/lib/screens/transcoding_settings_screen.dart b/lib/screens/transcoding_settings_screen.dart index c595dd0a..63b07c98 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 98a3b65f..48c9d09a 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 {