From cbd675174d81b3651f311fdca9b33325d274a4d3 Mon Sep 17 00:00:00 2001 From: GenTT <18240601+GennadyTem@users.noreply.github.com> Date: Thu, 25 Jun 2026 21:35:48 +0300 Subject: [PATCH] player: remember video preset per channel --- .../controllers/HQDialogController.java | 32 ++++++++ .../controllers/VideoStateController.java | 10 +++ .../settings/PlayerSettingsPresenter.java | 2 + .../common/prefs/PlayerData.java | 74 ++++++++++++++++++- .../common/utils/AppDialogUtil.java | 55 ++++++++++++++ common/src/main/res/values/strings.xml | 1 + 6 files changed, 173 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/HQDialogController.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/HQDialogController.java index 80ea47ca80..9f7a3c4204 100644 --- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/HQDialogController.java +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/HQDialogController.java @@ -190,6 +190,13 @@ private void onDialogHide() { //} private void addPresetsCategory() { + // Per channel presets (same as the speed list) + if (getPlayerData().isFormatPerChannelEnabled() && getPlayer() != null + && getPlayer().getVideo() != null && getPlayer().getVideo().channelId != null) { + addPresetsPerChannelCategory(); + return; + } + addCategoryInt(AppDialogUtil.createVideoPresetsCategory( getContext(), () -> { if (getPlayer() == null) { @@ -209,6 +216,31 @@ private void addPresetsCategory() { )); } + private void addPresetsPerChannelCategory() { + addCategoryInt(AppDialogUtil.createVideoPresetsPerChannelCategory( + getContext(), getPlayer().getVideo().channelId, () -> { + if (getPlayer() == null || getPlayer().getVideo() == null) { + return; + } + + FormatItem format = getPlayerData().getFormatPerChannel(getPlayer().getVideo().channelId); + + if (format == null) { + format = getPlayerData().getFormat(FormatItem.TYPE_VIDEO); + } + + getPlayer().setFormat(format); + + if (!getPlayer().containsMedia()) { + getPlayer().reloadPlayback(); + } + + // Make result easily be spotted by the user + getPlayer().showOverlay(false); + } + )); + } + private void addVideoZoomCategory() { if (getPlayer() == null) { return; diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/VideoStateController.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/VideoStateController.java index 118b56e90d..844c3d383a 100644 --- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/VideoStateController.java +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/VideoStateController.java @@ -148,6 +148,10 @@ public void onMetadata(MediaItemMetadata metadata) { // Channel info should be loaded at this point if (getVideo() != null && !getVideo().isRemote) { restoreSubtitleFormat(); + // Channel id is set only now, so reapply the per channel format + if (getPlayerData().isFormatPerChannelEnabled() && getPlayerData().getFormatPerChannel(getVideo().channelId) != null) { + restoreVideoFormat(); + } } // Need to contain channel id @@ -403,8 +407,14 @@ private void restoreVideoFormat() { return; } + Video video = getPlayer().getVideo(); + FormatItem formatPerChannel = getPlayerData().isFormatPerChannelEnabled() && video != null + ? getPlayerData().getFormatPerChannel(video.channelId) : null; + if (getPlayerData().getTempVideoFormat() != null) { getPlayer().setFormat(getPlayerData().getTempVideoFormat()); + } else if (formatPerChannel != null) { + getPlayer().setFormat(formatPerChannel); } else { getPlayer().setFormat(getPlayerData().getFormat(FormatItem.TYPE_VIDEO)); } diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/presenters/settings/PlayerSettingsPresenter.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/presenters/settings/PlayerSettingsPresenter.java index e2dce0dba8..ee734e0abd 100644 --- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/presenters/settings/PlayerSettingsPresenter.java +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/presenters/settings/PlayerSettingsPresenter.java @@ -486,6 +486,8 @@ private void appendMiscCategory(AppDialogPresenter settingsPresenter) { option -> mPlayerTweaksData.setQueueRespectsPlaybackMode(option.isSelected()), mPlayerTweaksData.isQueueRespectsPlaybackMode())); + options.add(AppDialogUtil.createVideoPresetChannelOption(getContext())); + settingsPresenter.appendCheckedCategory(getContext().getString(R.string.player_other), options); } diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/prefs/PlayerData.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/prefs/PlayerData.java index b2595c400d..71f169dfe6 100644 --- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/prefs/PlayerData.java +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/prefs/PlayerData.java @@ -95,6 +95,8 @@ public class PlayerData extends DataChangeBase implements PlayerConstants, Profi private boolean mIsSubtitlesPerChannelEnabled; private boolean mIsSpeedPerChannelEnabled; private final Map mSpeeds = new HashMap<>(); + private final Map mFormatsPerChannel = new HashMap<>(); + private boolean mIsFormatPerChannelEnabled; private float mPitch; private long mAfrSwitchTimeMs; private List mLastAudioLanguages; @@ -128,6 +130,32 @@ public String toString() { } } + private static class ChannelFormat { + public String channelId; + public FormatItem format; + + public ChannelFormat(String channelId, FormatItem format) { + this.channelId = channelId; + this.format = format; + } + + public static ChannelFormat fromString(String specs) { + String[] split = Helpers.splitObj(specs); + + if (split == null || split.length != 2) { + return new ChannelFormat(null, null); + } + + return new ChannelFormat(Helpers.parseStr(split[0]), ExoFormatItem.from(Helpers.parseStr(split[1]))); + } + + @NonNull + @Override + public String toString() { + return Helpers.mergeObj(channelId, format); + } + } + private PlayerData(Context context) { mPrefs = AppPrefs.instance(context); mPrefs.addListener(this); @@ -408,6 +436,39 @@ public FormatItem getTempVideoFormat() { return mTempVideoFormat; } + public FormatItem getFormatPerChannel(String channelId) { + if (channelId == null) { + return null; + } + + ChannelFormat format = mFormatsPerChannel.get(channelId); + + return format != null ? format.format : null; + } + + public void setFormatPerChannel(String channelId, FormatItem format) { + if (channelId == null) { + return; + } + + if (format == null) { + mFormatsPerChannel.remove(channelId); + } else { + mFormatsPerChannel.put(channelId, new ChannelFormat(channelId, format)); + } + + persistState(); + } + + public boolean isFormatPerChannelEnabled() { + return mIsFormatPerChannelEnabled; + } + + public void setFormatPerChannelEnabled(boolean enable) { + mIsFormatPerChannelEnabled = enable; + persistState(); + } + public FormatItem getLastSubtitleFormat() { return !mLastSubtitleFormats.isEmpty() ? mLastSubtitleFormats.get(0) : FormatItem.SUBTITLE_NONE; } @@ -847,6 +908,8 @@ private void restoreState() { mLastAudioLanguages = Helpers.parseStrList(split, 60); mIsVideoFlipEnabled = Helpers.parseBoolean(split, 61, false); mIsAudioDelayEnabled = Helpers.parseBoolean(split, 62, false); + String[] formatsPerChannel = Helpers.parseArray(split, 63); + mIsFormatPerChannelEnabled = Helpers.parseBoolean(split, 64, false); if (speeds != null) { for (String speedSpec : speeds) { @@ -855,6 +918,13 @@ private void restoreState() { } } + if (formatsPerChannel != null) { + for (String formatSpec : formatsPerChannel) { + ChannelFormat item = ChannelFormat.fromString(formatSpec); + mFormatsPerChannel.put(item.channelId, item); + } + } + if (!mIsAllSpeedEnabled) { mSpeed = 1.0f; } @@ -884,7 +954,8 @@ private void persistStateInt() { mIsNumberKeySeekEnabled, mIsSkip24RateEnabled, mAfrPauseMs, mIsLiveChatEnabled, mLastSubtitleFormats, mLastSpeed, mRotationAngle, mZoomPercents, mPlaybackMode, mAudioLanguage, mSubtitleLanguage, mEnabledSubtitlesPerChannel, mIsSubtitlesPerChannelEnabled, mIsSpeedPerChannelEnabled, Helpers.mergeArray(mSpeeds.values().toArray()), mPitch, mIsSkipShortsEnabled, mLastAudioLanguages, - mIsVideoFlipEnabled, mIsAudioDelayEnabled + mIsVideoFlipEnabled, mIsAudioDelayEnabled, Helpers.mergeArray(mFormatsPerChannel.values().toArray()), + mIsFormatPerChannelEnabled )); } @@ -894,6 +965,7 @@ public void onProfileChanged() { // reset on profile change mSpeeds.clear(); + mFormatsPerChannel.clear(); restoreState(); } diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/utils/AppDialogUtil.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/utils/AppDialogUtil.java index 2e984b1f88..57a3fe7732 100644 --- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/utils/AppDialogUtil.java +++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/utils/AppDialogUtil.java @@ -80,6 +80,7 @@ public class AppDialogUtil { private static final int PLAYER_ENGINE_ID = 147; private static final int IGNORE_DURATION_ID = 148; private static final int SLEEP_TIMER_ID = 149; + private static final int VIDEO_PRESETS_PER_CHANNEL_ID = 150; private static final int SUBTITLE_STYLES_ID = 45; private static final int SUBTITLE_SIZE_ID = 46; private static final int SUBTITLE_POSITION_ID = 47; @@ -309,6 +310,60 @@ private static List fromPresets(Context context, VideoPreset[] prese return result; } + public static OptionItem createVideoPresetChannelOption(Context context) { + PlayerData playerData = PlayerData.instance(context); + return UiOptionItem.from(context.getString(R.string.video_preset_per_channel), + optionItem -> playerData.setFormatPerChannelEnabled(optionItem.isSelected()), + playerData.isFormatPerChannelEnabled() + ); + } + + public static OptionCategory createVideoPresetsPerChannelCategory(Context context, String channelId, Runnable onFormatSelected) { + return OptionCategory.from( + VIDEO_PRESETS_PER_CHANNEL_ID, + OptionCategory.TYPE_RADIO_LIST, + context.getString(R.string.title_video_presets), + fromChannelPresets( + context, + AppDataSourceManager.instance().getVideoPresets(), + channelId, + onFormatSelected + ) + ); + } + + private static List fromChannelPresets(Context context, VideoPreset[] presets, String channelId, Runnable onFormatSelected) { + List result = new ArrayList<>(); + + PlayerData playerData = PlayerData.instance(context); + PlayerTweaksData playerTweaksData = PlayerTweaksData.instance(context); + FormatItem selectedFormat = playerData.getFormatPerChannel(channelId); + boolean isAllFormatsUnlocked = playerTweaksData.isAllFormatsUnlocked(); + + for (VideoPreset preset : presets) { + if (!isAllFormatsUnlocked && !Utils.isPresetSupported(preset)) { + continue; + } + + result.add(0, UiOptionItem.from(preset.name, + option -> { + playerData.setFormatPerChannel(channelId, preset.format); + onFormatSelected.run(); + }, + preset.format.equals(selectedFormat))); + } + + result.add(0, UiOptionItem.from( + context.getString(R.string.option_disabled), + optionItem -> { + playerData.setFormatPerChannel(channelId, null); + onFormatSelected.run(); + }, + selectedFormat == null)); + + return result; + } + public static OptionCategory createVideoBufferCategory(Context context) { return createVideoBufferCategory(context, () -> {}); } diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 63f82610d3..4497cd894d 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -48,6 +48,7 @@ Signed users only Can\'t load content.\n1) Turn on watch history.\n2) Check date and time on the device.\n3) Check network connection.\n4) Check your proxy settings.\n5) Try to sign in to your account.\n6) Maybe there is nothing in here. Video presets + Remember video preset per channel Accounts Edit categories Themes