mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-11-30 15:52:55 +01:00
chore: Merge branch dev
to main
(#620)
This commit is contained in:
commit
9147842ac7
111
CHANGELOG.md
111
CHANGELOG.md
@ -1,3 +1,114 @@
|
||||
# [1.9.0-dev.15](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.14...v1.9.0-dev.15) (2024-05-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Client spoof:** Spoof client to fix playback ([#637](https://github.com/ReVanced/revanced-integrations/issues/637)) ([4c1f82a](https://github.com/ReVanced/revanced-integrations/commit/4c1f82aa228239b041c7d3657f1117abd7516991))
|
||||
|
||||
# [1.9.0-dev.14](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.13...v1.9.0-dev.14) (2024-05-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Navigation buttons:** Add option to hide navigation button labels ([#635](https://github.com/ReVanced/revanced-integrations/issues/635)) ([6bd0ac2](https://github.com/ReVanced/revanced-integrations/commit/6bd0ac20dcfc76bfd044fb9f2f03ce98d6efc535))
|
||||
|
||||
# [1.9.0-dev.13](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.12...v1.9.0-dev.13) (2024-05-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide Shorts components:** Hide 'Buy super thanks' button ([#633](https://github.com/ReVanced/revanced-integrations/issues/633)) ([303754c](https://github.com/ReVanced/revanced-integrations/commit/303754c46f6d3471405d67b7911065fc3a87f721))
|
||||
|
||||
# [1.9.0-dev.12](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.11...v1.9.0-dev.12) (2024-05-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide Shorts components:** Hide old layout like/dislike buttons without leaving empty space ([9782338](https://github.com/ReVanced/revanced-integrations/commit/978233843d2dcf9a8a0ddf9c9517d25c973e8689))
|
||||
* **YouTube - Restore old video quality menu:** Do not make click sounds when opening Shorts quality menu ([578a27d](https://github.com/ReVanced/revanced-integrations/commit/578a27dea546f572c3fd270d001fd6024f59dd1a))
|
||||
|
||||
# [1.9.0-dev.11](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.10...v1.9.0-dev.11) (2024-05-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide video action buttons:** Updated path filter ([b74e544](https://github.com/ReVanced/revanced-integrations/commit/b74e54481ad4040d7742bb4f9a6a20351bb9ef71))
|
||||
|
||||
# [1.9.0-dev.10](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.9...v1.9.0-dev.10) (2024-05-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Restore old video quality menu:** Show advanced quality menu in Shorts quality flyout ([#632](https://github.com/ReVanced/revanced-integrations/issues/632)) ([77c9825](https://github.com/ReVanced/revanced-integrations/commit/77c9825b824f95ff6e2c2d5776f4c1d231e6fd0c))
|
||||
|
||||
# [1.9.0-dev.9](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.8...v1.9.0-dev.9) (2024-05-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - SponsorBlock:** Show correct segment times if video is over 24 hours in length ([#630](https://github.com/ReVanced/revanced-integrations/issues/630)) ([81251f9](https://github.com/ReVanced/revanced-integrations/commit/81251f9a34ef9f252ce69b01b40e29480a5add9f))
|
||||
|
||||
# [1.9.0-dev.8](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.7...v1.9.0-dev.8) (2024-05-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Player flyout menu:** Remove obsolete `Hide report menu` ([9e9d969](https://github.com/ReVanced/revanced-integrations/commit/9e9d96910c27568013f2a0744687eb8fba685175))
|
||||
|
||||
# [1.9.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.6...v1.9.0-dev.7) (2024-05-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Use Java instead of Kotlin Regex to improve reliability ([#628](https://github.com/ReVanced/revanced-integrations/issues/628)) ([44c3cc4](https://github.com/ReVanced/revanced-integrations/commit/44c3cc46367520f780537716236e0bed3b9537e6))
|
||||
|
||||
# [1.9.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.5...v1.9.0-dev.6) (2024-04-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide keyword content:** Filter Shorts found in horizontal shelves ([75fa579](https://github.com/ReVanced/revanced-integrations/commit/75fa5797f70123f68d4676201503cf35dcef46dc))
|
||||
|
||||
# [1.9.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.4...v1.9.0-dev.5) (2024-04-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide Shorts components:** Hide Shorts in search result horizontal shelves ([#625](https://github.com/ReVanced/revanced-integrations/issues/625)) ([656ca17](https://github.com/ReVanced/revanced-integrations/commit/656ca17ffc67cae7012436e5cddd7112b73fc450))
|
||||
|
||||
# [1.9.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.3...v1.9.0-dev.4) (2024-04-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Settings:** Use same background color for about screen if Theme patch is not included ([4164ed3](https://github.com/ReVanced/revanced-integrations/commit/4164ed3486330add8ff158f5b6765fa26587154d))
|
||||
|
||||
# [1.9.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.2...v1.9.0-dev.3) (2024-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide ads:** Add option to hide the 'Visit store' button on channel pages ([#622](https://github.com/ReVanced/revanced-integrations/issues/622)) ([9de566c](https://github.com/ReVanced/revanced-integrations/commit/9de566ca02398ab9c628565091ed02316c5e1ed0))
|
||||
|
||||
# [1.9.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v1.9.0-dev.1...v1.9.0-dev.2) (2024-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Comments:** Add option to hide timestamp and emoji buttons ([#621](https://github.com/ReVanced/revanced-integrations/issues/621)) ([6e9e122](https://github.com/ReVanced/revanced-integrations/commit/6e9e12235a69a1f3759180abd48e65b151b62e9a))
|
||||
|
||||
# [1.9.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v1.8.0...v1.9.0-dev.1) (2024-04-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide video action buttons:** Remove obsolete `hide Shop button` ([#618](https://github.com/ReVanced/revanced-integrations/issues/618)) ([a78c3ff](https://github.com/ReVanced/revanced-integrations/commit/a78c3ff09e81b1d1e0fe863135ad19e699b702c8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide Shorts components:** Hide like / dislike button in video ads ([#619](https://github.com/ReVanced/revanced-integrations/issues/619)) ([b2b6b8c](https://github.com/ReVanced/revanced-integrations/commit/b2b6b8c3d7d3c706552ffe70f3ed0314fe5284b6))
|
||||
|
||||
# [1.8.0](https://github.com/ReVanced/revanced-integrations/compare/v1.7.0...v1.8.0) (2024-04-21)
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@ import android.preference.PreferenceGroup;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.FrameLayout;
|
||||
@ -28,6 +29,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import java.text.Bidi;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
@ -36,7 +38,6 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import app.revanced.integrations.shared.settings.BooleanSetting;
|
||||
import app.revanced.integrations.shared.settings.preference.ReVancedAboutPreference;
|
||||
import kotlin.text.Regex;
|
||||
|
||||
public class Utils {
|
||||
|
||||
@ -94,7 +95,7 @@ public class Utils {
|
||||
* @param condition The setting to check for hiding the view.
|
||||
* @param view The view to hide.
|
||||
*/
|
||||
public static void hideViewBy1dpUnderCondition(BooleanSetting condition, View view) {
|
||||
public static void hideViewBy0dpUnderCondition(BooleanSetting condition, View view) {
|
||||
if (!condition.get()) return;
|
||||
|
||||
Logger.printDebug(() -> "Hiding view with setting: " + condition);
|
||||
@ -116,6 +117,15 @@ public class Utils {
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public static void removeViewFromParentUnderConditions(BooleanSetting setting, View view) {
|
||||
if (setting.get()) {
|
||||
ViewParent parent = view.getParent();
|
||||
if (parent instanceof ViewGroup) {
|
||||
((ViewGroup) parent).removeView(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General purpose pool for network calls and other background tasks.
|
||||
* All tasks run at max thread priority.
|
||||
@ -412,27 +422,30 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide a view by setting its layout params to 1x1
|
||||
* Hide a view by setting its layout params to 0x0
|
||||
* @param view The view to hide.
|
||||
*/
|
||||
public static void hideViewByLayoutParams(View view) {
|
||||
if (view instanceof LinearLayout) {
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(1, 1);
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams);
|
||||
} else if (view instanceof FrameLayout) {
|
||||
FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(1, 1);
|
||||
FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams2);
|
||||
} else if (view instanceof RelativeLayout) {
|
||||
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(1, 1);
|
||||
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams3);
|
||||
} else if (view instanceof Toolbar) {
|
||||
Toolbar.LayoutParams layoutParams4 = new Toolbar.LayoutParams(1, 1);
|
||||
Toolbar.LayoutParams layoutParams4 = new Toolbar.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams4);
|
||||
} else if (view instanceof ViewGroup) {
|
||||
ViewGroup.LayoutParams layoutParams5 = new ViewGroup.LayoutParams(1, 1);
|
||||
ViewGroup.LayoutParams layoutParams5 = new ViewGroup.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams5);
|
||||
} else {
|
||||
Logger.printDebug(() -> "Hidden view with id " + view.getId());
|
||||
ViewGroup.LayoutParams params = view.getLayoutParams();
|
||||
params.width = 0;
|
||||
params.height = 0;
|
||||
view.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,14 +487,14 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Regex punctuationRegex = new Regex("\\p{P}+");
|
||||
private static final Pattern punctuationPattern = Pattern.compile("\\p{P}+");
|
||||
|
||||
/**
|
||||
* Strips all punctuation and converts to lower case. A null parameter returns an empty string.
|
||||
*/
|
||||
public static String removePunctuationConvertToLowercase(@Nullable CharSequence original) {
|
||||
if (original == null) return "";
|
||||
return punctuationRegex.replace(original, "").toLowerCase();
|
||||
return punctuationPattern.matcher(original).replaceAll("").toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,6 +16,7 @@ public class ThemeHelper {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static void setTheme(Enum<?> value) {
|
||||
final int newOrdinalValue = value.ordinal();
|
||||
if (themeValue != newOrdinalValue) {
|
||||
@ -40,12 +41,12 @@ public class ThemeHelper {
|
||||
*/
|
||||
private static String darkThemeResourceName() {
|
||||
// Value is changed by Theme patch, if included.
|
||||
return "@android:color/black";
|
||||
return "@color/yt_black3";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The dark theme color as specified by the Theme patch (if included),
|
||||
* or the Android color of black.
|
||||
* or the dark mode background color unpatched YT uses.
|
||||
*/
|
||||
public static int getDarkThemeColor() {
|
||||
if (darkThemeColor == null) {
|
||||
@ -59,12 +60,12 @@ public class ThemeHelper {
|
||||
*/
|
||||
private static String lightThemeResourceName() {
|
||||
// Value is changed by Theme patch, if included.
|
||||
return "@android:color/white";
|
||||
return "@color/yt_white1";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The light theme color as specified by the Theme patch (if included),
|
||||
* or the Android color of white.
|
||||
* or the non dark mode background color unpatched YT uses.
|
||||
*/
|
||||
public static int getLightThemeColor() {
|
||||
if (lightThemeColor == null) {
|
||||
|
@ -2,7 +2,11 @@ package app.revanced.integrations.youtube.patches;
|
||||
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
|
||||
/**
|
||||
* Patch is obsolete and will be deleted in a future release
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated()
|
||||
public class HideEmailAddressPatch {
|
||||
//Used by app.revanced.patches.youtube.layout.personalinformation.patch.HideEmailAddressPatch
|
||||
public static int hideEmailAddress(int originalValue) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package app.revanced.integrations.youtube.patches;
|
||||
|
||||
import static app.revanced.integrations.shared.Utils.hideViewUnderCondition;
|
||||
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.view.View;
|
||||
@ -7,7 +8,7 @@ import android.view.View;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import android.widget.TextView;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ -40,4 +41,11 @@ public final class NavigationButtonsPatch {
|
||||
tabView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void hideNavigationButtonLabels(TextView navigationLabelsView) {
|
||||
hideViewUnderCondition(Settings.HIDE_NAVIGATION_BUTTON_LABELS, navigationLabelsView);
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ public class ReturnYouTubeDislikePatch {
|
||||
true, isRollingNumber);
|
||||
} else if (!isRollingNumber && conversionContextString.contains("|shorts_dislike_button.eml|")) {
|
||||
// Litho Shorts player.
|
||||
if (!Settings.RYD_SHORTS.get()) {
|
||||
if (!Settings.RYD_SHORTS.get() || Settings.HIDE_SHORTS_DISLIKE_BUTTON.get()) {
|
||||
// Must clear the current video here, otherwise if the user opens a regular video
|
||||
// then opens a litho short (while keeping the regular video on screen), then closes the short,
|
||||
// the original video may show the incorrect dislike value.
|
||||
@ -451,7 +451,7 @@ public class ReturnYouTubeDislikePatch {
|
||||
if (!Settings.RYD_ENABLED.get()) {
|
||||
return false;
|
||||
}
|
||||
if (!Settings.RYD_SHORTS.get()) {
|
||||
if (!Settings.RYD_SHORTS.get() || Settings.HIDE_SHORTS_DISLIKE_BUTTON.get()) {
|
||||
// Must clear the data here, in case a new video was loaded while PlayerType
|
||||
// suggested the video was not a short (can happen when spoofing to an old app version).
|
||||
clearData();
|
||||
|
@ -81,7 +81,7 @@ public final class VideoInformation {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String newPlayerResponseSignature(@NonNull String signature, boolean isShortAndOpeningOrPlaying) {
|
||||
public static String newPlayerResponseSignature(@NonNull String signature, String videoId, boolean isShortAndOpeningOrPlaying) {
|
||||
final boolean isShort = playerParametersAreShort(signature);
|
||||
playerResponseVideoIdIsShort = isShort;
|
||||
if (!isShort || isShortAndOpeningOrPlaying) {
|
||||
|
@ -23,6 +23,10 @@ public final class AdsFilter extends Filter {
|
||||
// endregion
|
||||
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
|
||||
private final StringFilterGroup channelProfile;
|
||||
private final ByteArrayFilterGroup visitStoreButton;
|
||||
|
||||
private final StringFilterGroup shoppingLinks;
|
||||
|
||||
public AdsFilter() {
|
||||
@ -100,6 +104,16 @@ public final class AdsFilter extends Filter {
|
||||
"expandable_list"
|
||||
);
|
||||
|
||||
channelProfile = new StringFilterGroup(
|
||||
null,
|
||||
"channel_profile.eml"
|
||||
);
|
||||
|
||||
visitStoreButton = new ByteArrayFilterGroup(
|
||||
Settings.HIDE_VISIT_STORE_BUTTON,
|
||||
"header_store_button"
|
||||
);
|
||||
|
||||
final var webLinkPanel = new StringFilterGroup(
|
||||
Settings.HIDE_WEB_SEARCH_RESULTS,
|
||||
"web_link_panel"
|
||||
@ -122,6 +136,7 @@ public final class AdsFilter extends Filter {
|
||||
viewProducts,
|
||||
selfSponsor,
|
||||
fullscreenAd,
|
||||
channelProfile,
|
||||
webLinkPanel,
|
||||
shoppingLinks,
|
||||
movieAds
|
||||
@ -140,6 +155,13 @@ public final class AdsFilter extends Filter {
|
||||
return false; // Do not actually filter the fullscreen ad otherwise it will leave a dimmed screen.
|
||||
}
|
||||
|
||||
if (matchedGroup == channelProfile) {
|
||||
if (visitStoreButton.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for the index because of likelihood of false positives.
|
||||
if (matchedGroup == shoppingLinks && contentIndex != 0)
|
||||
return false;
|
||||
@ -153,7 +175,7 @@ public final class AdsFilter extends Filter {
|
||||
* @param view The view, which shows ads.
|
||||
*/
|
||||
public static void hideAdAttributionView(View view) {
|
||||
Utils.hideViewBy1dpUnderCondition(Settings.HIDE_GENERAL_ADS, view);
|
||||
Utils.hideViewBy0dpUnderCondition(Settings.HIDE_GENERAL_ADS, view);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,7 @@ final class ButtonsFilter extends Filter {
|
||||
|
||||
bufferFilterPathGroup = new StringFilterGroup(
|
||||
null,
|
||||
"|CellType|CollectionType|CellType|ContainerType|button.eml|"
|
||||
"|ContainerType|button.eml|"
|
||||
);
|
||||
addPathCallbacks(
|
||||
new StringFilterGroup(
|
||||
@ -63,10 +63,6 @@ final class ButtonsFilter extends Filter {
|
||||
Settings.HIDE_CLIP_BUTTON,
|
||||
"yt_outline_scissors"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHOP_BUTTON,
|
||||
"yt_outline_bag"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_THANKS_BUTTON,
|
||||
"yt_outline_dollar_sign_heart"
|
||||
|
@ -1,10 +1,18 @@
|
||||
package app.revanced.integrations.youtube.patches.components;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
final class CommentsFilter extends Filter {
|
||||
|
||||
private static final String TIMESTAMP_OR_EMOJI_BUTTONS_ENDS_WITH_PATH
|
||||
= "|CellType|ContainerType|ContainerType|ContainerType|ContainerType|ContainerType|";
|
||||
|
||||
private final StringFilterGroup commentComposer;
|
||||
private final ByteArrayFilterGroup emojiPickerBufferGroup;
|
||||
|
||||
public CommentsFilter() {
|
||||
var comments = new StringFilterGroup(
|
||||
Settings.HIDE_COMMENTS_SECTION,
|
||||
@ -19,9 +27,38 @@ final class CommentsFilter extends Filter {
|
||||
"comments_entry_point_simplebox"
|
||||
);
|
||||
|
||||
commentComposer = new StringFilterGroup(
|
||||
Settings.HIDE_COMMENT_TIMESTAMP_AND_EMOJI_BUTTONS,
|
||||
"comment_composer.eml"
|
||||
);
|
||||
|
||||
emojiPickerBufferGroup = new ByteArrayFilterGroup(
|
||||
null,
|
||||
"id.comment.quick_emoji.button"
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
comments,
|
||||
previewComment
|
||||
previewComment,
|
||||
commentComposer
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
if (matchedGroup == commentComposer) {
|
||||
// To completely hide the emoji buttons (and leave no empty space), the timestamp button is
|
||||
// also hidden because the buffer is exactly the same and there's no way selectively hide.
|
||||
if (contentIndex == 0
|
||||
&& path.endsWith(TIMESTAMP_OR_EMOJI_BUTTONS_ENDS_WITH_PATH)
|
||||
&& emojiPickerBufferGroup.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ final class DescriptionComponentsFilter extends Filter {
|
||||
);
|
||||
|
||||
final StringFilterGroup transcriptSection = new StringFilterGroup(
|
||||
Settings.HIDE_TRANSCIPT_SECTION,
|
||||
Settings.HIDE_TRANSCRIPT_SECTION,
|
||||
"transcript_section"
|
||||
);
|
||||
|
||||
|
@ -100,7 +100,8 @@ final class KeywordContentFilter extends Filter {
|
||||
private final StringFilterGroup containsFilter = new StringFilterGroup(
|
||||
null,
|
||||
"modern_type_shelf_header_content.eml",
|
||||
"shorts_lockup_cell.eml" // Part of 'shorts_shelf_carousel.eml'
|
||||
"shorts_lockup_cell.eml", // Part of 'shorts_shelf_carousel.eml'
|
||||
"video_card.eml" // Shorts that appear in a horizontal shelf.
|
||||
);
|
||||
|
||||
/**
|
||||
@ -153,7 +154,7 @@ final class KeywordContentFilter extends Filter {
|
||||
return sentence;
|
||||
}
|
||||
final int firstCodePoint = sentence.codePointAt(0);
|
||||
// In some non English languages title case is different than upper case.
|
||||
// In some non English languages title case is different than uppercase.
|
||||
return new StringBuilder()
|
||||
.appendCodePoint(Character.toTitleCase(firstCodePoint))
|
||||
.append(sentence, Character.charCount(firstCodePoint), sentence.length())
|
||||
@ -167,6 +168,7 @@ final class KeywordContentFilter extends Filter {
|
||||
if (sentence.isEmpty()) {
|
||||
return sentence;
|
||||
}
|
||||
|
||||
final int delimiter = ' ';
|
||||
// Use code points and not characters to handle unicode surrogates.
|
||||
int[] codePoints = sentence.codePoints().toArray();
|
||||
|
@ -45,10 +45,6 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||
Settings.HIDE_AMBIENT_MODE_MENU,
|
||||
"yt_outline_screen_light"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_REPORT_MENU,
|
||||
"yt_outline_flag"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_HELP_MENU,
|
||||
"yt_outline_question_circle"
|
||||
|
@ -1,6 +1,7 @@
|
||||
package app.revanced.integrations.youtube.patches.components;
|
||||
|
||||
import static app.revanced.integrations.shared.Utils.hideViewUnderCondition;
|
||||
import static app.revanced.integrations.shared.Utils.removeViewFromParentUnderConditions;
|
||||
import static app.revanced.integrations.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.view.View;
|
||||
@ -65,8 +66,12 @@ public final class ShortsFilter extends Filter {
|
||||
// Path components.
|
||||
//
|
||||
|
||||
// Shorts that appear in the feed/search when the device is using tablet layout.
|
||||
shortsCompactFeedVideoPath = new StringFilterGroup(null, "compact_video.eml");
|
||||
shortsCompactFeedVideoPath = new StringFilterGroup(null,
|
||||
// Shorts that appear in the feed/search when the device is using tablet layout.
|
||||
"compact_video.eml",
|
||||
// Search results that appear in a horizontal shelf.
|
||||
"video_card.eml");
|
||||
|
||||
// Filter out items that use the 'frame0' thumbnail.
|
||||
// This is a valid thumbnail for both regular videos and Shorts,
|
||||
// but it appears these thumbnails are used only for Shorts.
|
||||
@ -194,6 +199,10 @@ public final class ShortsFilter extends Filter {
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHORTS_SEARCH_SUGGESTIONS,
|
||||
"yt_outline_search_"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHORTS_SUPER_THANKS_BUTTON,
|
||||
"yt_outline_dollar_sign_heart_"
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -213,8 +222,7 @@ public final class ShortsFilter extends Filter {
|
||||
}
|
||||
|
||||
if (matchedGroup == shortsCompactFeedVideoPath) {
|
||||
if (shouldHideShortsFeedItems() && contentIndex == 0
|
||||
&& shortsCompactFeedVideoBuffer.check(protobufBufferArray).isFiltered()) {
|
||||
if (shouldHideShortsFeedItems() && shortsCompactFeedVideoBuffer.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
@ -310,6 +318,21 @@ public final class ShortsFilter extends Filter {
|
||||
|
||||
// region Hide the buttons in older versions of YouTube. New versions use Litho.
|
||||
|
||||
public static void hideLikeButton(final View likeButtonView) {
|
||||
// Cannot set the visibility to gone for like/dislike,
|
||||
// as some other unknown YT code also sets the visibility after this hook.
|
||||
//
|
||||
// Setting the view to 0dp works, but that leaves a blank space where
|
||||
// the button was (only relevant for dislikes button).
|
||||
//
|
||||
// Instead remove the view from the parent.
|
||||
removeViewFromParentUnderConditions(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView);
|
||||
}
|
||||
|
||||
public static void hideDislikeButton(final View dislikeButtonView) {
|
||||
removeViewFromParentUnderConditions(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView);
|
||||
}
|
||||
|
||||
public static void hideShortsCommentsButton(final View commentsButtonView) {
|
||||
hideViewUnderCondition(Settings.HIDE_SHORTS_COMMENTS_BUTTON, commentsButtonView);
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ListView;
|
||||
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import app.revanced.integrations.youtube.patches.components.VideoQualityMenuFilterPatch;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
|
||||
/**
|
||||
* This patch contains the logic to show the old video quality menu.
|
||||
@ -44,7 +44,18 @@ public final class RestoreOldVideoQualityMenuPatch {
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Only used if spoofing to an old app version.
|
||||
* Injection point.
|
||||
*
|
||||
* Used to force the creation of the advanced menu item for the Shorts quality flyout.
|
||||
*/
|
||||
public static boolean forceAdvancedVideoQualityMenuCreation() {
|
||||
return Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* Used if spoofing to an old app version, and also used for the Shorts video quality flyout.
|
||||
*/
|
||||
public static void showOldVideoQualityMenu(final ListView listView) {
|
||||
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return;
|
||||
@ -52,17 +63,21 @@ public final class RestoreOldVideoQualityMenuPatch {
|
||||
listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
|
||||
@Override
|
||||
public void onChildViewAdded(View parent, View child) {
|
||||
Logger.printDebug(() -> "Added listener to old type of quality menu");
|
||||
try {
|
||||
parent.setVisibility(View.GONE);
|
||||
|
||||
parent.setVisibility(View.GONE);
|
||||
final var indexOfAdvancedQualityMenuItem = 4;
|
||||
if (listView.indexOfChild(child) != indexOfAdvancedQualityMenuItem) return;
|
||||
|
||||
final var indexOfAdvancedQualityMenuItem = 4;
|
||||
if (listView.indexOfChild(child) != indexOfAdvancedQualityMenuItem) return;
|
||||
Logger.printDebug(() -> "Found advanced menu item in old type of quality menu");
|
||||
|
||||
Logger.printDebug(() -> "Found advanced menu item in old type of quality menu");
|
||||
listView.setSoundEffectsEnabled(false);
|
||||
final var qualityItemMenuPosition = 4;
|
||||
listView.performItemClick(null, qualityItemMenuPosition, 0);
|
||||
|
||||
final var qualityItemMenuPosition = 4;
|
||||
listView.performItemClick(null, qualityItemMenuPosition, 0);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "showOldVideoQualityMenu failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,264 @@
|
||||
package app.revanced.integrations.youtube.patches.spoof;
|
||||
|
||||
import static app.revanced.integrations.youtube.patches.spoof.requests.StoryboardRendererRequester.getStoryboardRenderer;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
import app.revanced.integrations.youtube.patches.VideoInformation;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class SpoofClientPatch {
|
||||
private static final boolean SPOOF_CLIENT_ENABLED = Settings.SPOOF_CLIENT.get();
|
||||
private static final boolean SPOOF_CLIENT_USE_IOS = Settings.SPOOF_CLIENT_USE_IOS.get();
|
||||
private static final boolean SPOOF_CLIENT_STORYBOARD = SPOOF_CLIENT_ENABLED && !SPOOF_CLIENT_USE_IOS;
|
||||
|
||||
/**
|
||||
* Any unreachable ip address. Used to intentionally fail requests.
|
||||
*/
|
||||
private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
|
||||
private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
|
||||
|
||||
@Nullable
|
||||
private static volatile Future<StoryboardRenderer> lastStoryboardFetched;
|
||||
|
||||
private static final Map<String, Future<StoryboardRenderer>> storyboardCache =
|
||||
Collections.synchronizedMap(new LinkedHashMap<>(100) {
|
||||
private static final int CACHE_LIMIT = 100;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Entry eldest) {
|
||||
return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit.
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Blocks /get_watch requests by returning a localhost URI.
|
||||
*
|
||||
* @param playerRequestUri The URI of the player request.
|
||||
* @return Localhost URI if the request is a /get_watch request, otherwise the original URI.
|
||||
*/
|
||||
public static Uri blockGetWatchRequest(Uri playerRequestUri) {
|
||||
if (SPOOF_CLIENT_ENABLED) {
|
||||
try {
|
||||
String path = playerRequestUri.getPath();
|
||||
|
||||
if (path != null && path.contains("get_watch")) {
|
||||
Logger.printDebug(() -> "Blocking: " + playerRequestUri + " by returning: " + UNREACHABLE_HOST_URI_STRING);
|
||||
|
||||
return UNREACHABLE_HOST_URI;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "blockGetWatchRequest failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return playerRequestUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Blocks /initplayback requests.
|
||||
* For iOS, an unreachable host URL can be used, but for Android Testsuite, this is not possible.
|
||||
*/
|
||||
public static String blockInitPlaybackRequest(String originalUrlString) {
|
||||
if (SPOOF_CLIENT_ENABLED) {
|
||||
try {
|
||||
var originalUri = Uri.parse(originalUrlString);
|
||||
String path = originalUri.getPath();
|
||||
|
||||
if (path != null && path.contains("initplayback")) {
|
||||
String replacementUriString = (getSpoofClientType() == ClientType.IOS)
|
||||
? UNREACHABLE_HOST_URI_STRING
|
||||
// TODO: Ideally, a local proxy could be setup and block
|
||||
// the request the same way as Burp Suite is capable of
|
||||
// because that way the request is never sent to YouTube unnecessarily.
|
||||
// Just using localhost unfortunately does not work.
|
||||
: originalUri.buildUpon().clearQuery().build().toString();
|
||||
|
||||
Logger.printDebug(() -> "Blocking: " + originalUrlString + " by returning: " + replacementUriString);
|
||||
|
||||
return replacementUriString;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "blockInitPlaybackRequest failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return originalUrlString;
|
||||
}
|
||||
|
||||
private static ClientType getSpoofClientType() {
|
||||
if (SPOOF_CLIENT_USE_IOS) {
|
||||
return ClientType.IOS;
|
||||
}
|
||||
|
||||
StoryboardRenderer renderer = getRenderer(false);
|
||||
if (renderer == null) {
|
||||
// Video is private or otherwise not available.
|
||||
// Test client still works for video playback, but seekbar thumbnails are not available.
|
||||
// Use iOS client instead.
|
||||
Logger.printDebug(() -> "Using iOS client for paid or otherwise restricted video");
|
||||
return ClientType.IOS;
|
||||
}
|
||||
|
||||
if (renderer.isLiveStream) {
|
||||
// Test client does not support live streams.
|
||||
// Use the storyboard renderer information to fallback to iOS if a live stream is opened.
|
||||
Logger.printDebug(() -> "Using iOS client for livestream: " + renderer.videoId);
|
||||
return ClientType.IOS;
|
||||
}
|
||||
|
||||
return ClientType.ANDROID_TESTSUITE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static int getClientTypeId(int originalClientTypeId) {
|
||||
if (SPOOF_CLIENT_ENABLED) {
|
||||
return getSpoofClientType().id;
|
||||
}
|
||||
|
||||
return originalClientTypeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String getClientVersion(String originalClientVersion) {
|
||||
if (SPOOF_CLIENT_ENABLED) {
|
||||
return getSpoofClientType().version;
|
||||
}
|
||||
|
||||
return originalClientVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean isClientSpoofingEnabled() {
|
||||
return SPOOF_CLIENT_ENABLED;
|
||||
}
|
||||
|
||||
//
|
||||
// Storyboard.
|
||||
//
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String setPlayerResponseVideoId(String parameters, String videoId, boolean isShortAndOpeningOrPlaying) {
|
||||
if (SPOOF_CLIENT_STORYBOARD) {
|
||||
try {
|
||||
// VideoInformation is not a dependent patch, and only this single helper method is used.
|
||||
// Hook can be called when scrolling thru the feed and a Shorts shelf is present.
|
||||
// Ignore these videos.
|
||||
if (!isShortAndOpeningOrPlaying && VideoInformation.playerParametersAreShort(parameters)) {
|
||||
Logger.printDebug(() -> "Ignoring Short: " + videoId);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
Future<StoryboardRenderer> storyboard = storyboardCache.get(videoId);
|
||||
if (storyboard == null) {
|
||||
storyboard = Utils.submitOnBackgroundThread(() -> getStoryboardRenderer(videoId));
|
||||
storyboardCache.put(videoId, storyboard);
|
||||
lastStoryboardFetched = storyboard;
|
||||
|
||||
// Block until the renderer fetch completes.
|
||||
// This is desired because if this returns without finishing the fetch
|
||||
// then video will start playback but the storyboard is not ready yet.
|
||||
getRenderer(true);
|
||||
} else {
|
||||
lastStoryboardFetched = storyboard;
|
||||
// No need to block on the fetch since it previously loaded.
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setPlayerResponseVideoId failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return parameters; // Return the original value since we are observing and not modifying.
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static StoryboardRenderer getRenderer(boolean waitForCompletion) {
|
||||
var future = lastStoryboardFetched;
|
||||
if (future != null) {
|
||||
try {
|
||||
if (waitForCompletion || future.isDone()) {
|
||||
return future.get(20000, TimeUnit.MILLISECONDS); // Any arbitrarily large timeout.
|
||||
} // else, return null.
|
||||
} catch (TimeoutException ex) {
|
||||
Logger.printDebug(() -> "Could not get renderer (get timed out)");
|
||||
} catch (ExecutionException | InterruptedException ex) {
|
||||
// Should never happen.
|
||||
Logger.printException(() -> "Could not get renderer", ex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Called from background threads and from the main thread.
|
||||
*/
|
||||
@Nullable
|
||||
public static String getStoryboardRendererSpec(String originalStoryboardRendererSpec) {
|
||||
if (SPOOF_CLIENT_STORYBOARD) {
|
||||
StoryboardRenderer renderer = getRenderer(false);
|
||||
|
||||
if (renderer != null) {
|
||||
if (!renderer.isLiveStream && renderer.spec != null) {
|
||||
return renderer.spec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return originalStoryboardRendererSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static int getRecommendedLevel(int originalLevel) {
|
||||
if (SPOOF_CLIENT_STORYBOARD) {
|
||||
StoryboardRenderer renderer = getRenderer(false);
|
||||
|
||||
if (renderer != null) {
|
||||
if (!renderer.isLiveStream && renderer.recommendedLevel != null) {
|
||||
return renderer.recommendedLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return originalLevel;
|
||||
}
|
||||
|
||||
private enum ClientType {
|
||||
ANDROID_TESTSUITE(30, "1.9"),
|
||||
IOS(5, Utils.getAppVersionName());
|
||||
|
||||
final int id;
|
||||
final String version;
|
||||
|
||||
ClientType(int id, String version) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
}
|
@ -85,7 +85,7 @@ public class SpoofSignaturePatch {
|
||||
*
|
||||
* @param parameters Original protobuf parameter value.
|
||||
*/
|
||||
public static String spoofParameter(String parameters, boolean isShortAndOpeningOrPlaying) {
|
||||
public static String spoofParameter(String parameters, String videoId, boolean isShortAndOpeningOrPlaying) {
|
||||
try {
|
||||
Logger.printDebug(() -> "Original protobuf parameter value: " + parameters);
|
||||
|
||||
@ -152,12 +152,12 @@ public class SpoofSignaturePatch {
|
||||
if (Settings.SPOOF_SIGNATURE.get() && !useOriginalStoryboardRenderer) {
|
||||
StoryboardRenderer renderer = getRenderer(false);
|
||||
if (renderer != null) {
|
||||
if (returnNullIfLiveStream && renderer.isLiveStream()) {
|
||||
if (returnNullIfLiveStream && renderer.isLiveStream) {
|
||||
return null;
|
||||
}
|
||||
String spec = renderer.getSpec();
|
||||
if (spec != null) {
|
||||
return spec;
|
||||
|
||||
if (renderer.spec != null) {
|
||||
return renderer.spec;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -191,8 +191,9 @@ public class SpoofSignaturePatch {
|
||||
if (Settings.SPOOF_SIGNATURE.get() && !useOriginalStoryboardRenderer) {
|
||||
StoryboardRenderer renderer = getRenderer(false);
|
||||
if (renderer != null) {
|
||||
Integer recommendedLevel = renderer.getRecommendedLevel();
|
||||
if (recommendedLevel != null) return recommendedLevel;
|
||||
if (renderer.recommendedLevel != null) {
|
||||
return renderer.recommendedLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +215,7 @@ public class SpoofSignaturePatch {
|
||||
// Show empty thumbnails so the seek time and chapters still show up.
|
||||
return true;
|
||||
}
|
||||
return renderer.getSpec() != null;
|
||||
return renderer.spec != null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,42 +4,30 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Deprecated
|
||||
public final class StoryboardRenderer {
|
||||
public final String videoId;
|
||||
@Nullable
|
||||
private final String spec;
|
||||
private final boolean isLiveStream;
|
||||
public final String spec;
|
||||
public final boolean isLiveStream;
|
||||
/**
|
||||
* Recommended image quality level, or NULL if no recommendation exists.
|
||||
*/
|
||||
@Nullable
|
||||
private final Integer recommendedLevel;
|
||||
public final Integer recommendedLevel;
|
||||
|
||||
public StoryboardRenderer(@Nullable String spec, boolean isLiveStream, @Nullable Integer recommendedLevel) {
|
||||
public StoryboardRenderer(String videoId, @Nullable String spec, boolean isLiveStream, @Nullable Integer recommendedLevel) {
|
||||
this.videoId = videoId;
|
||||
this.spec = spec;
|
||||
this.isLiveStream = isLiveStream;
|
||||
this.recommendedLevel = recommendedLevel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSpec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
public boolean isLiveStream() {
|
||||
return isLiveStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Recommended image quality level, or NULL if no recommendation exists.
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getRecommendedLevel() {
|
||||
return recommendedLevel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StoryboardRenderer{" +
|
||||
"isLiveStream=" + isLiveStream +
|
||||
"videoId=" + videoId +
|
||||
", isLiveStream=" + isLiveStream +
|
||||
", spec='" + spec + '\'' +
|
||||
", recommendedLevel=" + recommendedLevel +
|
||||
'}';
|
||||
|
@ -10,7 +10,6 @@ import org.json.JSONObject;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
@Deprecated
|
||||
final class PlayerRoutes {
|
||||
private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1/";
|
||||
static final Route.CompiledRoute GET_STORYBOARD_SPEC_RENDERER = new Route(
|
||||
@ -27,7 +26,7 @@ final class PlayerRoutes {
|
||||
/**
|
||||
* TCP connection and HTTP read timeout
|
||||
*/
|
||||
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 4 * 1000; // 4 Seconds.
|
||||
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 10 * 1000; // 10 Seconds.
|
||||
|
||||
static {
|
||||
JSONObject innerTubeBody = new JSONObject();
|
||||
|
@ -19,17 +19,8 @@ import java.util.Objects;
|
||||
import static app.revanced.integrations.shared.StringRef.str;
|
||||
import static app.revanced.integrations.youtube.patches.spoof.requests.PlayerRoutes.*;
|
||||
|
||||
@Deprecated
|
||||
public class StoryboardRendererRequester {
|
||||
|
||||
/**
|
||||
* For videos that have no storyboard.
|
||||
* Usually for low resolution videos as old as YouTube itself.
|
||||
* Does not include paid videos where the renderer fetch fails.
|
||||
*/
|
||||
private static final StoryboardRenderer emptyStoryboard
|
||||
= new StoryboardRenderer(null, false, null);
|
||||
|
||||
private StoryboardRendererRequester() {
|
||||
}
|
||||
|
||||
@ -69,9 +60,9 @@ public class StoryboardRendererRequester {
|
||||
null, showToastOnIOException || BaseSettings.DEBUG_TOAST_ON_ERROR.get());
|
||||
connection.disconnect();
|
||||
} catch (SocketTimeoutException ex) {
|
||||
handleConnectionError(str("revanced_spoof_storyboard_timeout"), ex, showToastOnIOException);
|
||||
handleConnectionError(str("revanced_spoof_client_storyboard_timeout"), ex, showToastOnIOException);
|
||||
} catch (IOException ex) {
|
||||
handleConnectionError(str("revanced_spoof_storyboard_io_exception", ex.getMessage()),
|
||||
handleConnectionError(str("revanced_spoof_client_storyboard_io_exception", ex.getMessage()),
|
||||
ex, showToastOnIOException);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Spoof storyboard fetch failed", ex); // Should never happen.
|
||||
@ -98,22 +89,23 @@ public class StoryboardRendererRequester {
|
||||
* @return StoryboardRenderer or null if playabilityStatus is not OK.
|
||||
*/
|
||||
@Nullable
|
||||
private static StoryboardRenderer getStoryboardRendererUsingBody(@NonNull String innerTubeBody,
|
||||
private static StoryboardRenderer getStoryboardRendererUsingBody(String videoId,
|
||||
@NonNull String innerTubeBody,
|
||||
boolean showToastOnIOException) {
|
||||
final JSONObject playerResponse = fetchPlayerResponse(innerTubeBody, showToastOnIOException);
|
||||
if (playerResponse != null && isPlayabilityStatusOk(playerResponse))
|
||||
return getStoryboardRendererUsingResponse(playerResponse);
|
||||
return getStoryboardRendererUsingResponse(videoId, playerResponse);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static StoryboardRenderer getStoryboardRendererUsingResponse(@NonNull JSONObject playerResponse) {
|
||||
private static StoryboardRenderer getStoryboardRendererUsingResponse(@NonNull String videoId, @NonNull JSONObject playerResponse) {
|
||||
try {
|
||||
Logger.printDebug(() -> "Parsing response: " + playerResponse);
|
||||
if (!playerResponse.has("storyboards")) {
|
||||
Logger.printDebug(() -> "Using empty storyboard");
|
||||
return emptyStoryboard;
|
||||
return new StoryboardRenderer(videoId, null, false, null);
|
||||
}
|
||||
final JSONObject storyboards = playerResponse.getJSONObject("storyboards");
|
||||
final boolean isLiveStream = storyboards.has("playerLiveStoryboardSpecRenderer");
|
||||
@ -123,6 +115,7 @@ public class StoryboardRendererRequester {
|
||||
|
||||
final var rendererElement = storyboards.getJSONObject(storyboardsRendererTag);
|
||||
StoryboardRenderer renderer = new StoryboardRenderer(
|
||||
videoId,
|
||||
rendererElement.getString("spec"),
|
||||
isLiveStream,
|
||||
rendererElement.has("recommendedLevel")
|
||||
@ -144,11 +137,11 @@ public class StoryboardRendererRequester {
|
||||
public static StoryboardRenderer getStoryboardRenderer(@NonNull String videoId) {
|
||||
Objects.requireNonNull(videoId);
|
||||
|
||||
var renderer = getStoryboardRendererUsingBody(
|
||||
var renderer = getStoryboardRendererUsingBody(videoId,
|
||||
String.format(ANDROID_INNER_TUBE_BODY, videoId), false);
|
||||
if (renderer == null) {
|
||||
Logger.printDebug(() -> videoId + " not available using Android client");
|
||||
renderer = getStoryboardRendererUsingBody(
|
||||
renderer = getStoryboardRendererUsingBody(videoId,
|
||||
String.format(TV_EMBED_INNER_TUBE_BODY, videoId, videoId), true);
|
||||
if (renderer == null) {
|
||||
Logger.printDebug(() -> videoId + " not available using TV embedded client");
|
||||
|
@ -20,18 +20,9 @@ import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
||||
public class Settings extends BaseSettings {
|
||||
// External downloader
|
||||
public static final BooleanSetting EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_external_downloader", FALSE);
|
||||
public static final BooleanSetting EXTERNAL_DOWNLOADER_ACTION_BUTTON = new BooleanSetting("revanced_external_downloader_action_button", FALSE);
|
||||
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name",
|
||||
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
|
||||
|
||||
// Copy video URL
|
||||
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
||||
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
||||
|
||||
// Video
|
||||
public static final BooleanSetting RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
|
||||
public static final BooleanSetting HIDE_VIDEO_QUALITY_MENU_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", TRUE);
|
||||
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", TRUE);
|
||||
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
|
||||
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
|
||||
@ -54,9 +45,14 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_SHOPPING_LINKS = new BooleanSetting("revanced_hide_shopping_links", TRUE);
|
||||
public static final BooleanSetting HIDE_SELF_SPONSOR = new BooleanSetting("revanced_hide_self_sponsor_ads", TRUE);
|
||||
public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_hide_video_ads", TRUE, true);
|
||||
public static final BooleanSetting HIDE_VISIT_STORE_BUTTON = new BooleanSetting("revanced_hide_visit_store_button", TRUE);
|
||||
public static final BooleanSetting HIDE_WEB_SEARCH_RESULTS = new BooleanSetting("revanced_hide_web_search_results", TRUE);
|
||||
|
||||
// Layout
|
||||
// Feed
|
||||
public static final BooleanSetting HIDE_ALBUM_CARDS = new BooleanSetting("revanced_hide_album_cards", FALSE, true);
|
||||
public static final BooleanSetting HIDE_ARTIST_CARDS = new BooleanSetting("revanced_hide_artist_cards", FALSE);
|
||||
|
||||
// Alternative thumbnails
|
||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
|
||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_SUBSCRIPTIONS = new EnumSetting<>("revanced_alt_thumbnail_subscription", ThumbnailOption.ORIGINAL);
|
||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_LIBRARY = new EnumSetting<>("revanced_alt_thumbnail_library", ThumbnailOption.ORIGINAL);
|
||||
@ -67,28 +63,27 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST = new BooleanSetting("revanced_alt_thumbnail_dearrow_connection_toast", TRUE, new DeArrowAvailability());
|
||||
public static final EnumSetting<ThumbnailStillTime> ALT_THUMBNAIL_STILLS_TIME = new EnumSetting<>("revanced_alt_thumbnail_stills_time", ThumbnailStillTime.MIDDLE, new StillImagesAvailability());
|
||||
public static final BooleanSetting ALT_THUMBNAIL_STILLS_FAST = new BooleanSetting("revanced_alt_thumbnail_stills_fast", FALSE, new StillImagesAvailability());
|
||||
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
||||
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
||||
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
|
||||
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
|
||||
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
||||
|
||||
// Hide keyword content
|
||||
public static final BooleanSetting HIDE_KEYWORD_CONTENT_HOME = new BooleanSetting("revanced_hide_keyword_content_home", FALSE);
|
||||
public static final BooleanSetting HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS = new BooleanSetting("revanced_hide_keyword_content_subscriptions", FALSE);
|
||||
public static final BooleanSetting HIDE_KEYWORD_CONTENT_SEARCH = new BooleanSetting("revanced_hide_keyword_content_search", FALSE);
|
||||
public static final StringSetting HIDE_KEYWORD_CONTENT_PHRASES = new StringSetting("revanced_hide_keyword_content_phrases", "",
|
||||
parentsAny(HIDE_KEYWORD_CONTENT_HOME, HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS, HIDE_KEYWORD_CONTENT_SEARCH));
|
||||
|
||||
// Uncategorized layout related settings. Do not add to this section, and instead move these out and categorize them.
|
||||
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
|
||||
public static final BooleanSetting GRADIENT_LOADING_SCREEN = new BooleanSetting("revanced_gradient_loading_screen", FALSE);
|
||||
public static final BooleanSetting HIDE_ALBUM_CARDS = new BooleanSetting("revanced_hide_album_cards", FALSE, true);
|
||||
public static final BooleanSetting HIDE_ARTIST_CARDS = new BooleanSetting("revanced_hide_artist_cards", FALSE);
|
||||
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_HORIZONTAL_SHELVES = new BooleanSetting("revanced_hide_horizontal_shelves", TRUE);
|
||||
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
|
||||
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_CHANNEL_BAR = new BooleanSetting("revanced_hide_channel_bar", FALSE);
|
||||
public static final BooleanSetting HIDE_CHANNEL_MEMBER_SHELF = new BooleanSetting("revanced_hide_channel_member_shelf", TRUE);
|
||||
public static final BooleanSetting HIDE_CHIPS_SHELF = new BooleanSetting("revanced_hide_chips_shelf", TRUE);
|
||||
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE, true);
|
||||
public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE);
|
||||
public static final BooleanSetting HIDE_COMMUNITY_POSTS = new BooleanSetting("revanced_hide_community_posts", FALSE);
|
||||
public static final BooleanSetting HIDE_COMPACT_BANNER = new BooleanSetting("revanced_hide_compact_banner", TRUE);
|
||||
public static final BooleanSetting HIDE_CROWDFUNDING_BOX = new BooleanSetting("revanced_hide_crowdfunding_box", FALSE, true);
|
||||
public static final BooleanSetting HIDE_EMAIL_ADDRESS = new BooleanSetting("revanced_hide_email_address", FALSE);
|
||||
@Deprecated public static final BooleanSetting HIDE_EMAIL_ADDRESS = new BooleanSetting("revanced_hide_email_address", FALSE);
|
||||
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
|
||||
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", TRUE);
|
||||
public static final BooleanSetting HIDE_EXPANDABLE_CHIP = new BooleanSetting("revanced_hide_expandable_chip", TRUE);
|
||||
@ -104,19 +99,12 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_IMAGE_SHELF = new BooleanSetting("revanced_hide_image_shelf", TRUE);
|
||||
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", TRUE);
|
||||
public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE);
|
||||
public static final BooleanSetting HIDE_KEYWORD_CONTENT_HOME = new BooleanSetting("revanced_hide_keyword_content_home", FALSE);
|
||||
public static final BooleanSetting HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS = new BooleanSetting("revanced_hide_keyword_content_subscriptions", FALSE);
|
||||
public static final BooleanSetting HIDE_KEYWORD_CONTENT_SEARCH = new BooleanSetting("revanced_hide_keyword_content_search", FALSE);
|
||||
public static final StringSetting HIDE_KEYWORD_CONTENT_PHRASES = new StringSetting("revanced_hide_keyword_content_phrases", "",
|
||||
parentsAny(HIDE_KEYWORD_CONTENT_HOME, HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS, HIDE_KEYWORD_CONTENT_SEARCH));
|
||||
@Deprecated public static final BooleanSetting HIDE_LOAD_MORE_BUTTON = new BooleanSetting("revanced_hide_load_more_button", TRUE);
|
||||
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE);
|
||||
public static final BooleanSetting HIDE_MIX_PLAYLISTS = new BooleanSetting("revanced_hide_mix_playlists", TRUE);
|
||||
public static final BooleanSetting HIDE_MOVIES_SECTION = new BooleanSetting("revanced_hide_movies_section", TRUE);
|
||||
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
|
||||
public static final BooleanSetting HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE);
|
||||
public static final BooleanSetting HIDE_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_preview_comment", FALSE, true);
|
||||
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
|
||||
public static final BooleanSetting HIDE_QUICK_ACTIONS = new BooleanSetting("revanced_hide_quick_actions", FALSE);
|
||||
public static final BooleanSetting HIDE_RELATED_VIDEOS = new BooleanSetting("revanced_hide_related_videos", FALSE);
|
||||
@ -126,23 +114,29 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_TIMESTAMP = new BooleanSetting("revanced_hide_timestamp", FALSE);
|
||||
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
|
||||
public static final BooleanSetting HIDE_FOR_YOU_SHELF = new BooleanSetting("revanced_hide_for_you_shelf", TRUE);
|
||||
public static final BooleanSetting HIDE_VIDEO_QUALITY_MENU_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", TRUE);
|
||||
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
|
||||
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity",100, true);
|
||||
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
|
||||
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "17.33.42", true, parent(SPOOF_APP_VERSION));
|
||||
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
|
||||
public static final BooleanSetting USE_TABLET_MINIPLAYER = new BooleanSetting("revanced_tablet_miniplayer", FALSE, true);
|
||||
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
||||
public static final StringSetting START_PAGE = new StringSetting("revanced_start_page", "");
|
||||
|
||||
// Navigation buttons
|
||||
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
||||
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_SHORTS_BUTTON = new BooleanSetting("revanced_hide_shorts_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
|
||||
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
|
||||
// Player
|
||||
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
|
||||
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
||||
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE);
|
||||
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
||||
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
||||
|
||||
// External downloader
|
||||
public static final BooleanSetting EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_external_downloader", FALSE);
|
||||
public static final BooleanSetting EXTERNAL_DOWNLOADER_ACTION_BUTTON = new BooleanSetting("revanced_external_downloader_action_button", FALSE);
|
||||
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name",
|
||||
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
|
||||
|
||||
// Comments
|
||||
public static final BooleanSetting HIDE_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_preview_comment", FALSE);
|
||||
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE);
|
||||
public static final BooleanSetting HIDE_COMMENT_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comment_timestamp_and_emoji_buttons", TRUE);
|
||||
|
||||
// Description
|
||||
public static final BooleanSetting HIDE_CHAPTERS = new BooleanSetting("revanced_hide_chapters", TRUE);
|
||||
@ -150,10 +144,54 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_GAME_SECTION = new BooleanSetting("revanced_hide_game_section", TRUE);
|
||||
public static final BooleanSetting HIDE_MUSIC_SECTION = new BooleanSetting("revanced_hide_music_section", TRUE);
|
||||
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
|
||||
public static final BooleanSetting HIDE_TRANSCIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
|
||||
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
|
||||
|
||||
// Action buttons
|
||||
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
|
||||
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
|
||||
public static final BooleanSetting HIDE_REMIX_BUTTON = new BooleanSetting("revanced_hide_remix_button", TRUE);
|
||||
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
|
||||
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
|
||||
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
|
||||
public static final BooleanSetting HIDE_PLAYLIST_BUTTON = new BooleanSetting("revanced_hide_playlist_button", FALSE);
|
||||
|
||||
// Player flyout menu items
|
||||
public static final BooleanSetting HIDE_CAPTIONS_MENU = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
|
||||
public static final BooleanSetting HIDE_ADDITIONAL_SETTINGS_MENU = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
||||
public static final BooleanSetting HIDE_LOOP_VIDEO_MENU = new BooleanSetting("revanced_hide_player_flyout_loop_video", FALSE);
|
||||
public static final BooleanSetting HIDE_AMBIENT_MODE_MENU = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
||||
public static final BooleanSetting HIDE_HELP_MENU = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
|
||||
public static final BooleanSetting HIDE_SPEED_MENU = new BooleanSetting("revanced_hide_player_flyout_speed", FALSE);
|
||||
public static final BooleanSetting HIDE_MORE_INFO_MENU = new BooleanSetting("revanced_hide_player_flyout_more_info", TRUE);
|
||||
public static final BooleanSetting HIDE_LOCK_SCREEN_MENU = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
||||
public static final BooleanSetting HIDE_AUDIO_TRACK_MENU = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE);
|
||||
public static final BooleanSetting HIDE_WATCH_IN_VR_MENU = new BooleanSetting("revanced_hide_player_flyout_watch_in_vr", TRUE);
|
||||
|
||||
// General layout
|
||||
public static final StringSetting START_PAGE = new StringSetting("revanced_start_page", "");
|
||||
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "17.33.42", true, parent(SPOOF_APP_VERSION));
|
||||
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
|
||||
public static final BooleanSetting USE_TABLET_MINIPLAYER = new BooleanSetting("revanced_tablet_miniplayer", FALSE, true);
|
||||
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
||||
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE,
|
||||
"revanced_remove_viewer_discretion_dialog_user_dialog_message");
|
||||
|
||||
// Custom filter
|
||||
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
||||
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
||||
|
||||
// Navigation buttons
|
||||
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
||||
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_SHORTS_BUTTON = new BooleanSetting("revanced_hide_shorts_button", TRUE, true);
|
||||
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
|
||||
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
|
||||
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
|
||||
|
||||
// Shorts
|
||||
@Deprecated public static final BooleanSetting DEPRECATED_HIDE_SHORTS = new BooleanSetting("revanced_hide_shorts", FALSE);
|
||||
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_HOME = new BooleanSetting("revanced_hide_shorts_home", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_SUBSCRIPTIONS = new BooleanSetting("revanced_hide_shorts_subscriptions", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_SEARCH = new BooleanSetting("revanced_hide_shorts_search", FALSE);
|
||||
@ -165,6 +203,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_SHORTS_LOCATION_LABEL = new BooleanSetting("revanced_hide_shorts_location_label", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_SAVE_SOUND_BUTTON = new BooleanSetting("revanced_hide_shorts_save_sound_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_SEARCH_SUGGESTIONS = new BooleanSetting("revanced_hide_shorts_search_suggestions", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_SUPER_THANKS_BUTTON = new BooleanSetting("revanced_hide_shorts_super_thanks_button", TRUE);
|
||||
public static final BooleanSetting HIDE_SHORTS_LIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_like_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
|
||||
@ -179,60 +218,45 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_SHORTS_NAVIGATION_BAR = new BooleanSetting("revanced_hide_shorts_navigation_bar", TRUE, true);
|
||||
|
||||
// Seekbar
|
||||
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", TRUE);
|
||||
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
|
||||
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE);
|
||||
public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE);
|
||||
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true);
|
||||
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE);
|
||||
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
|
||||
public static final StringSetting SEEKBAR_CUSTOM_COLOR_VALUE = new StringSetting("revanced_seekbar_custom_color_value", "#FF0000", true, parent(SEEKBAR_CUSTOM_COLOR));
|
||||
|
||||
// Action buttons
|
||||
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
|
||||
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
|
||||
public static final BooleanSetting HIDE_REMIX_BUTTON = new BooleanSetting("revanced_hide_remix_button", TRUE);
|
||||
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
|
||||
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
|
||||
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
|
||||
public static final BooleanSetting HIDE_PLAYLIST_BUTTON = new BooleanSetting("revanced_hide_playlist_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHOP_BUTTON = new BooleanSetting("revanced_hide_shop_button", TRUE);
|
||||
|
||||
// Player flyout menu items
|
||||
public static final BooleanSetting HIDE_CAPTIONS_MENU = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
|
||||
public static final BooleanSetting HIDE_ADDITIONAL_SETTINGS_MENU = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
||||
public static final BooleanSetting HIDE_LOOP_VIDEO_MENU = new BooleanSetting("revanced_hide_player_flyout_loop_video", FALSE);
|
||||
public static final BooleanSetting HIDE_AMBIENT_MODE_MENU = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
||||
public static final BooleanSetting HIDE_REPORT_MENU = new BooleanSetting("revanced_hide_player_flyout_report", TRUE);
|
||||
public static final BooleanSetting HIDE_HELP_MENU = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
|
||||
public static final BooleanSetting HIDE_SPEED_MENU = new BooleanSetting("revanced_hide_player_flyout_speed", FALSE);
|
||||
public static final BooleanSetting HIDE_MORE_INFO_MENU = new BooleanSetting("revanced_hide_player_flyout_more_info", TRUE);
|
||||
public static final BooleanSetting HIDE_LOCK_SCREEN_MENU = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
||||
public static final BooleanSetting HIDE_AUDIO_TRACK_MENU = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE);
|
||||
public static final BooleanSetting HIDE_WATCH_IN_VR_MENU = new BooleanSetting("revanced_hide_player_flyout_watch_in_vr", TRUE);
|
||||
|
||||
// Misc
|
||||
public static final BooleanSetting AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
|
||||
public static final BooleanSetting DISABLE_ZOOM_HAPTICS = new BooleanSetting("revanced_disable_zoom_haptics", TRUE);
|
||||
public static final BooleanSetting EXTERNAL_BROWSER = new BooleanSetting("revanced_external_browser", TRUE, true);
|
||||
public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
|
||||
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
|
||||
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE);
|
||||
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", TRUE);
|
||||
public static final BooleanSetting SPOOF_SIGNATURE = new BooleanSetting("revanced_spoof_signature_verification_enabled", TRUE, true,
|
||||
"revanced_spoof_signature_verification_enabled_user_dialog_message");
|
||||
public static final BooleanSetting SPOOF_SIGNATURE_IN_FEED = new BooleanSetting("revanced_spoof_signature_in_feed_enabled", FALSE, false,
|
||||
parent(SPOOF_SIGNATURE));
|
||||
public static final BooleanSetting SPOOF_STORYBOARD_RENDERER = new BooleanSetting("revanced_spoof_storyboard", TRUE, true,
|
||||
parent(SPOOF_SIGNATURE));
|
||||
public static final BooleanSetting SPOOF_DEVICE_DIMENSIONS = new BooleanSetting("revanced_spoof_device_dimensions", FALSE, true,
|
||||
"revanced_spoof_device_dimensions_user_dialog_message");
|
||||
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
||||
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
|
||||
public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", TRUE, true, "revanced_spoof_client_user_dialog_message");
|
||||
public static final BooleanSetting SPOOF_CLIENT_USE_IOS = new BooleanSetting("revanced_spoof_client_use_ios", FALSE, true, parent(SPOOF_CLIENT));
|
||||
@Deprecated
|
||||
public static final StringSetting DEPRECATED_ANNOUNCEMENT_LAST_HASH = new StringSetting("revanced_announcement_last_hash", "");
|
||||
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1);
|
||||
public static final BooleanSetting REMOVE_TRACKING_QUERY_PARAMETER = new BooleanSetting("revanced_remove_tracking_query_parameter", TRUE);
|
||||
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG= new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE,
|
||||
"revanced_remove_viewer_discretion_dialog_user_dialog_message");
|
||||
|
||||
// Debugging
|
||||
/**
|
||||
* When enabled, share the debug logs with care.
|
||||
* The buffer contains select user data, including the client ip address and information that could identify the end user.
|
||||
*/
|
||||
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, parent(BaseSettings.DEBUG));
|
||||
|
||||
// Old deprecated signature spoofing
|
||||
@Deprecated public static final BooleanSetting SPOOF_SIGNATURE = new BooleanSetting("revanced_spoof_signature_verification_enabled", TRUE, true, false,
|
||||
"revanced_spoof_signature_verification_enabled_user_dialog_message", null);
|
||||
@Deprecated public static final BooleanSetting SPOOF_SIGNATURE_IN_FEED = new BooleanSetting("revanced_spoof_signature_in_feed_enabled", FALSE, false, false, null,
|
||||
parent(SPOOF_SIGNATURE));
|
||||
@Deprecated public static final BooleanSetting SPOOF_STORYBOARD_RENDERER = new BooleanSetting("revanced_spoof_storyboard", TRUE, true, false, null,
|
||||
parent(SPOOF_SIGNATURE));
|
||||
|
||||
// Swipe controls
|
||||
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", TRUE);
|
||||
@ -252,12 +276,6 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
|
||||
public static final FloatSetting SWIPE_BRIGHTNESS_VALUE = new FloatSetting("revanced_swipe_brightness_value", -1f);
|
||||
public static final BooleanSetting SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS = new BooleanSetting("revanced_swipe_lowest_value_enable_auto_brightness", FALSE, true, parent(SWIPE_BRIGHTNESS));
|
||||
// Debugging
|
||||
/**
|
||||
* When enabled, share the debug logs with care.
|
||||
* The buffer contains select user data, including the client ip address and information that could identify the YT account.
|
||||
*/
|
||||
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, parent(BaseSettings.DEBUG));
|
||||
|
||||
// ReturnYoutubeDislike
|
||||
public static final BooleanSetting RYD_ENABLED = new BooleanSetting("ryd_enabled", TRUE);
|
||||
@ -289,6 +307,9 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting SB_USER_IS_VIP = new BooleanSetting("sb_user_is_vip", FALSE);
|
||||
public static final IntegerSetting SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS = new IntegerSetting("sb_local_time_saved_number_segments", 0);
|
||||
public static final LongSetting SB_LOCAL_TIME_SAVED_MILLISECONDS = new LongSetting("sb_local_time_saved_milliseconds", 0L);
|
||||
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false);
|
||||
public static final BooleanSetting SB_HIDE_EXPORT_WARNING = new BooleanSetting("sb_hide_export_warning", FALSE, false, false);
|
||||
public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false);
|
||||
|
||||
public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_SPONSOR_COLOR = new StringSetting("sb_sponsor_color","#00D400");
|
||||
@ -311,11 +332,6 @@ public class Settings extends BaseSettings {
|
||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color","#FFFFFF");
|
||||
|
||||
// SB Setting not exported
|
||||
public static final LongSetting SB_LAST_VIP_CHECK = new LongSetting("sb_last_vip_check", 0L, false, false);
|
||||
public static final BooleanSetting SB_HIDE_EXPORT_WARNING = new BooleanSetting("sb_hide_export_warning", FALSE, false, false);
|
||||
public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false);
|
||||
|
||||
static {
|
||||
// region Migration
|
||||
|
||||
@ -383,15 +399,6 @@ public class Settings extends BaseSettings {
|
||||
// Remove any previously saved announcement consumer (a random generated string).
|
||||
Setting.preferences.removeKey("revanced_announcement_consumer");
|
||||
|
||||
// Shorts
|
||||
if (DEPRECATED_HIDE_SHORTS.get()) {
|
||||
Logger.printInfo(() -> "Migrating hide Shorts setting");
|
||||
DEPRECATED_HIDE_SHORTS.resetToDefault();
|
||||
HIDE_SHORTS_HOME.save(true);
|
||||
HIDE_SHORTS_SUBSCRIPTIONS.save(true);
|
||||
HIDE_SHORTS_SEARCH.save(true);
|
||||
}
|
||||
|
||||
migrateOldSettingToNew(HIDE_LOAD_MORE_BUTTON, HIDE_SHOW_MORE_BUTTON);
|
||||
|
||||
// endregion
|
||||
|
@ -2,7 +2,6 @@ package app.revanced.integrations.youtube.sponsorblock;
|
||||
|
||||
import static app.revanced.integrations.shared.StringRef.str;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@ -13,13 +12,14 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Duration;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
import app.revanced.integrations.youtube.patches.VideoInformation;
|
||||
import app.revanced.integrations.youtube.settings.Settings;
|
||||
import app.revanced.integrations.youtube.sponsorblock.objects.CategoryBehaviour;
|
||||
@ -28,25 +28,16 @@ import app.revanced.integrations.youtube.sponsorblock.objects.SponsorSegment;
|
||||
import app.revanced.integrations.youtube.sponsorblock.objects.SponsorSegment.SegmentVote;
|
||||
import app.revanced.integrations.youtube.sponsorblock.requests.SBRequester;
|
||||
import app.revanced.integrations.youtube.sponsorblock.ui.SponsorBlockViewController;
|
||||
import app.revanced.integrations.shared.Logger;
|
||||
import app.revanced.integrations.shared.Utils;
|
||||
|
||||
/**
|
||||
* Not thread safe. All fields/methods must be accessed from the main thread.
|
||||
*/
|
||||
public class SponsorBlockUtils {
|
||||
private static final String MANUAL_EDIT_TIME_FORMAT = "HH:mm:ss.SSS";
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private static final SimpleDateFormat manualEditTimeFormatter = new SimpleDateFormat(MANUAL_EDIT_TIME_FORMAT);
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private static final SimpleDateFormat voteSegmentTimeFormatter = new SimpleDateFormat();
|
||||
private static final NumberFormat statsNumberFormatter = NumberFormat.getNumberInstance();
|
||||
static {
|
||||
TimeZone utc = TimeZone.getTimeZone("UTC");
|
||||
manualEditTimeFormatter.setTimeZone(utc);
|
||||
voteSegmentTimeFormatter.setTimeZone(utc);
|
||||
}
|
||||
private static final String LOCKED_COLOR = "#FFC83D";
|
||||
private static final String MANUAL_EDIT_TIME_TEXT_HINT = "hh:mm:ss.sss";
|
||||
private static final Pattern manualEditTimePattern
|
||||
= Pattern.compile("((\\d{1,2}):)?(\\d{1,2}):(\\d{2})(\\.(\\d{1,3}))?");
|
||||
private static final NumberFormat statsNumberFormatter = NumberFormat.getNumberInstance();
|
||||
|
||||
private static long newSponsorSegmentDialogShownMillis;
|
||||
private static long newSponsorSegmentStartMillis = -1;
|
||||
@ -131,17 +122,17 @@ public class SponsorBlockUtils {
|
||||
final boolean isStart = DialogInterface.BUTTON_NEGATIVE == which;
|
||||
|
||||
final EditText textView = new EditText(context);
|
||||
textView.setHint(MANUAL_EDIT_TIME_FORMAT);
|
||||
textView.setHint(MANUAL_EDIT_TIME_TEXT_HINT);
|
||||
if (isStart) {
|
||||
if (newSponsorSegmentStartMillis >= 0)
|
||||
textView.setText(manualEditTimeFormatter.format(new Date(newSponsorSegmentStartMillis)));
|
||||
textView.setText(formatSegmentTime(newSponsorSegmentStartMillis));
|
||||
} else {
|
||||
if (newSponsorSegmentEndMillis >= 0)
|
||||
textView.setText(manualEditTimeFormatter.format(new Date(newSponsorSegmentEndMillis)));
|
||||
textView.setText(formatSegmentTime(newSponsorSegmentEndMillis));
|
||||
}
|
||||
|
||||
editByHandSaveDialogListener.settingStart = isStart;
|
||||
editByHandSaveDialogListener.editText = new WeakReference<>(textView);
|
||||
editByHandSaveDialogListener.editTextRef = new WeakReference<>(textView);
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(str(isStart ? "revanced_sb_new_segment_time_start" : "revanced_sb_new_segment_time_end"))
|
||||
.setView(textView)
|
||||
@ -243,7 +234,7 @@ public class SponsorBlockUtils {
|
||||
new AlertDialog.Builder(SponsorBlockViewController.getOverLaysViewGroupContext())
|
||||
.setTitle(str("revanced_sb_new_segment_title"))
|
||||
.setMessage(str("revanced_sb_new_segment_mark_time_as_question",
|
||||
newSponsorSegmentDialogShownMillis / 60000,
|
||||
newSponsorSegmentDialogShownMillis / 3600000,
|
||||
newSponsorSegmentDialogShownMillis / 1000 % 60,
|
||||
newSponsorSegmentDialogShownMillis % 1000))
|
||||
.setNeutralButton(android.R.string.cancel, null)
|
||||
@ -265,15 +256,13 @@ public class SponsorBlockUtils {
|
||||
} else if (!newSponsorSegmentPreviewed && newSponsorSegmentStartMillis != 0) {
|
||||
Utils.showToastLong(str("revanced_sb_new_segment_preview_segment_first"));
|
||||
} else {
|
||||
long length = (newSponsorSegmentEndMillis - newSponsorSegmentStartMillis) / 1000;
|
||||
long start = (newSponsorSegmentStartMillis) / 1000;
|
||||
long end = (newSponsorSegmentEndMillis) / 1000;
|
||||
final long segmentLength = (newSponsorSegmentEndMillis - newSponsorSegmentStartMillis) / 1000;
|
||||
new AlertDialog.Builder(SponsorBlockViewController.getOverLaysViewGroupContext())
|
||||
.setTitle(str("revanced_sb_new_segment_confirm_title"))
|
||||
.setMessage(str("revanced_sb_new_segment_confirm_content",
|
||||
start / 60, start % 60,
|
||||
end / 60, end % 60,
|
||||
length / 60, length % 60))
|
||||
formatSegmentTime(newSponsorSegmentStartMillis),
|
||||
formatSegmentTime(newSponsorSegmentEndMillis),
|
||||
getTimeSavedString(segmentLength)))
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes, segmentReadyDialogButtonListener)
|
||||
.show();
|
||||
@ -295,19 +284,6 @@ public class SponsorBlockUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
// use same time formatting as shown in the video player
|
||||
final long videoLength = VideoInformation.getVideoLength();
|
||||
final String formatPattern;
|
||||
if (videoLength < (10 * 60 * 1000)) {
|
||||
formatPattern = "m:ss.SSS"; // less than 10 minutes
|
||||
} else if (videoLength < (60 * 60 * 1000)) {
|
||||
formatPattern = "mm:ss.SSS"; // less than 1 hour
|
||||
} else if (videoLength < (10 * 60 * 60 * 1000)) {
|
||||
formatPattern = "H:mm:ss.SSS"; // less than 10 hours
|
||||
} else {
|
||||
formatPattern = "HH:mm:ss.SSS"; // why is this on YouTube
|
||||
}
|
||||
voteSegmentTimeFormatter.applyPattern(formatPattern);
|
||||
|
||||
final int numberOfSegments = segments.length;
|
||||
CharSequence[] titles = new CharSequence[numberOfSegments];
|
||||
@ -319,9 +295,9 @@ public class SponsorBlockUtils {
|
||||
StringBuilder htmlBuilder = new StringBuilder();
|
||||
htmlBuilder.append(String.format("<b><font color=\"#%06X\">⬤</font> %s<br>",
|
||||
segment.category.color, segment.category.title));
|
||||
htmlBuilder.append(voteSegmentTimeFormatter.format(new Date(segment.start)));
|
||||
htmlBuilder.append(formatSegmentTime(segment.start));
|
||||
if (segment.category != SegmentCategory.HIGHLIGHT) {
|
||||
htmlBuilder.append(" to ").append(voteSegmentTimeFormatter.format(new Date(segment.end)));
|
||||
htmlBuilder.append(" to ").append(formatSegmentTime(segment.end));
|
||||
}
|
||||
htmlBuilder.append("</b>");
|
||||
if (i + 1 != numberOfSegments) // prevents trailing new line after last segment
|
||||
@ -367,7 +343,7 @@ public class SponsorBlockUtils {
|
||||
SegmentPlaybackController.addUnsubmittedSegment(
|
||||
new SponsorSegment(SegmentCategory.UNSUBMITTED, null,
|
||||
newSponsorSegmentStartMillis, newSponsorSegmentEndMillis, false));
|
||||
VideoInformation.seekTo(newSponsorSegmentStartMillis - 2500);
|
||||
VideoInformation.seekTo(newSponsorSegmentStartMillis - 2000);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onPreviewClicked failure", ex);
|
||||
@ -408,6 +384,65 @@ public class SponsorBlockUtils {
|
||||
return statsNumberFormatter.format(viewCount);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private static long parseSegmentTime(@NonNull String time) {
|
||||
Matcher matcher = manualEditTimePattern.matcher(time);
|
||||
if (!matcher.matches()) {
|
||||
return -1;
|
||||
}
|
||||
String hoursStr = matcher.group(2); // Hours is optional.
|
||||
String minutesStr = matcher.group(3);
|
||||
String secondsStr = matcher.group(4);
|
||||
String millisecondsStr = matcher.group(6); // Milliseconds is optional.
|
||||
|
||||
try {
|
||||
final int hours = (hoursStr != null) ? Integer.parseInt(hoursStr) : 0;
|
||||
final int minutes = Integer.parseInt(minutesStr);
|
||||
final int seconds = Integer.parseInt(secondsStr);
|
||||
final int milliseconds;
|
||||
if (millisecondsStr != null) {
|
||||
// Pad out with zeros if not all decimal places were used.
|
||||
millisecondsStr = String.format(Locale.US, "%-3s", millisecondsStr).replace(' ', '0');
|
||||
milliseconds = Integer.parseInt(millisecondsStr);
|
||||
} else {
|
||||
milliseconds = 0;
|
||||
}
|
||||
|
||||
return (hours * 3600000L) + (minutes * 60000L) + (seconds * 1000L) + milliseconds;
|
||||
} catch (NumberFormatException ex) {
|
||||
Logger.printInfo(() -> "Time format exception: " + time, ex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatSegmentTime(long segmentTime) {
|
||||
// Use same time formatting as shown in the video player.
|
||||
final long videoLength = VideoInformation.getVideoLength();
|
||||
|
||||
// Cannot use DateFormatter, as videos over 24 hours will rollover and not display correctly.
|
||||
final long hours = TimeUnit.MILLISECONDS.toHours(segmentTime);
|
||||
final long minutes = TimeUnit.MILLISECONDS.toMinutes(segmentTime) % 60;
|
||||
final long seconds = TimeUnit.MILLISECONDS.toSeconds(segmentTime) % 60;
|
||||
final long milliseconds = segmentTime % 1000;
|
||||
|
||||
final String formatPattern;
|
||||
Object[] formatArgs = {minutes, seconds, milliseconds};
|
||||
|
||||
if (videoLength < (10 * 60 * 1000)) {
|
||||
formatPattern = "%01d:%02d.%03d"; // Less than 10 minutes.
|
||||
} else if (videoLength < (60 * 60 * 1000)) {
|
||||
formatPattern = "%02d:%02d.%03d"; // Less than 1 hour.
|
||||
} else if (videoLength < (10 * 60 * 60 * 1000)) {
|
||||
formatPattern = "%01d:%02d:%02d.%03d"; // Less than 10 hours.
|
||||
formatArgs = new Object[]{hours, minutes, seconds, milliseconds};
|
||||
} else {
|
||||
formatPattern = "%02d:%02d:%02d.%03d"; // Why is this on YouTube?
|
||||
formatArgs = new Object[]{hours, minutes, seconds, milliseconds};
|
||||
}
|
||||
|
||||
return String.format(Locale.US, formatPattern, formatArgs);
|
||||
}
|
||||
|
||||
public static String getTimeSavedString(long totalSecondsSaved) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
Duration duration = Duration.ofSeconds(totalSecondsSaved);
|
||||
@ -431,17 +466,24 @@ public class SponsorBlockUtils {
|
||||
|
||||
private static class EditByHandSaveDialogListener implements DialogInterface.OnClickListener {
|
||||
boolean settingStart;
|
||||
WeakReference<EditText> editText;
|
||||
WeakReference<EditText> editTextRef = new WeakReference<>(null);
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
try {
|
||||
final EditText editText = this.editText.get();
|
||||
final EditText editText = editTextRef.get();
|
||||
if (editText == null) return;
|
||||
|
||||
long time = (which == DialogInterface.BUTTON_NEUTRAL) ?
|
||||
VideoInformation.getVideoTime() :
|
||||
(Objects.requireNonNull(manualEditTimeFormatter.parse(editText.getText().toString())).getTime());
|
||||
final long time;
|
||||
if (which == DialogInterface.BUTTON_NEUTRAL) {
|
||||
time = VideoInformation.getVideoTime();
|
||||
} else {
|
||||
time = parseSegmentTime(editText.getText().toString());
|
||||
if (time < 0) {
|
||||
Utils.showToastLong(str("revanced_sb_new_segment_edit_by_hand_parse_error"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (settingStart)
|
||||
newSponsorSegmentStartMillis = Math.max(time, 0);
|
||||
@ -452,8 +494,6 @@ public class SponsorBlockUtils {
|
||||
editByHandDialogListener.onClick(dialog, settingStart ?
|
||||
DialogInterface.BUTTON_NEGATIVE :
|
||||
DialogInterface.BUTTON_POSITIVE);
|
||||
} catch (ParseException e) {
|
||||
Utils.showToastLong(str("revanced_sb_new_segment_edit_by_hand_parse_error"));
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "EditByHandSaveDialogListener failure", ex);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
android.useAndroidX = true
|
||||
version = 1.8.0
|
||||
version = 1.9.0-dev.15
|
||||
|
@ -1,10 +1,11 @@
|
||||
[versions]
|
||||
#noinspection GradleDependency
|
||||
agp = "8.2.2" # 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818
|
||||
annotation = "1.7.1"
|
||||
kotlin = "1.9.22"
|
||||
kotlin = "1.9.23"
|
||||
appcompat = "1.7.0-alpha03"
|
||||
okhttp = "5.0.0-alpha.12"
|
||||
retrofit = "2.9.0"
|
||||
retrofit = "2.11.0"
|
||||
|
||||
[libraries]
|
||||
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
|
||||
|
2450
package-lock.json
generated
2450
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,6 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.9.1",
|
||||
"semantic-release": "^23.0.0"
|
||||
"semantic-release": "^23.0.6"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package com.google.protos.youtube.api.innertube;
|
||||
|
||||
public class InnertubeContext$ClientInfo {
|
||||
public int r;
|
||||
}
|
Loading…
Reference in New Issue
Block a user