mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-12-01 00:02:55 +01:00
chore: merge branch dev
to main
(#425)
This commit is contained in:
commit
bc91b35994
21
CHANGELOG.md
21
CHANGELOG.md
@ -1,3 +1,24 @@
|
||||
## [0.111.1-dev.3](https://github.com/revanced/revanced-integrations/compare/v0.111.1-dev.2...v0.111.1-dev.3) (2023-06-27)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* return earlier when possible ([#427](https://github.com/revanced/revanced-integrations/issues/427)) ([12f3f97](https://github.com/revanced/revanced-integrations/commit/12f3f975525863e593216ecf36ca817d162474e1))
|
||||
|
||||
## [0.111.1-dev.2](https://github.com/revanced/revanced-integrations/compare/v0.111.1-dev.1...v0.111.1-dev.2) (2023-06-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **youtube/disable-fullscreen-panels:** prompt to restart after turning on/off ([#426](https://github.com/revanced/revanced-integrations/issues/426)) ([328ecff](https://github.com/revanced/revanced-integrations/commit/328ecff18bf301ec8993bba49356f9813de1d901))
|
||||
|
||||
## [0.111.1-dev.1](https://github.com/revanced/revanced-integrations/compare/v0.111.0...v0.111.1-dev.1) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **youtube/client-spoof:** update settings text for known side effects of spoof signature ([#424](https://github.com/revanced/revanced-integrations/issues/424)) ([d7a3973](https://github.com/revanced/revanced-integrations/commit/d7a3973ef1e6c4443fc4d89f063bc6bf3446bec3))
|
||||
|
||||
# [0.111.0](https://github.com/revanced/revanced-integrations/compare/v0.110.0...v0.111.0) (2023-06-21)
|
||||
|
||||
|
||||
|
@ -1,14 +1,12 @@
|
||||
package app.revanced.integrations.patches;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.shared.PlayerType;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
|
||||
import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
|
||||
|
||||
public class SpoofSignatureVerificationPatch {
|
||||
/**
|
||||
* Enable/disable all workarounds that are required due to signature spoofing.
|
||||
@ -37,28 +35,6 @@ public class SpoofSignatureVerificationPatch {
|
||||
"SAFg" // Autoplay in scrim
|
||||
};
|
||||
|
||||
/**
|
||||
* On app first start, the first video played usually contains a single non-default window setting value
|
||||
* and all other subtitle settings for the video are (incorrect) default shorts window settings.
|
||||
* For this situation, the shorts settings must be replaced.
|
||||
*
|
||||
* But some videos use multiple text positions on screen (such as https://youtu.be/3hW1rMNC89o),
|
||||
* and by chance many of the subtitles uses window positions that match a default shorts position.
|
||||
* To handle these videos, selectively allowing the shorts specific window settings to 'pass thru' unchanged,
|
||||
* but only if the video contains multiple non-default subtitle window positions.
|
||||
*
|
||||
* Do not enable 'pass thru mode' until this many non default subtitle settings are observed for a single video.
|
||||
*/
|
||||
private static final int NUMBER_OF_NON_DEFAULT_SUBTITLES_BEFORE_ENABLING_PASSTHRU = 2;
|
||||
|
||||
/**
|
||||
* The number of non default subtitle settings encountered for the current video.
|
||||
*/
|
||||
private static int numberOfNonDefaultSettingsObserved;
|
||||
|
||||
@Nullable
|
||||
private static String currentVideoId;
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
@ -121,115 +97,4 @@ public class SpoofSignatureVerificationPatch {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Last WindowsSetting constructor values. Values are checked for changes to reduce log spam.
|
||||
*/
|
||||
private static int lastAp, lastAh, lastAv;
|
||||
private static boolean lastVs, lastSd;
|
||||
|
||||
/**
|
||||
* Injection point. Overrides values passed into SubtitleWindowSettings constructor.
|
||||
*
|
||||
* @param ap anchor position. A bitmask with 6 bit fields, that appears to indicate the layout position on screen
|
||||
* @param ah anchor horizontal. A percentage [0, 100], that appears to be a horizontal text anchor point
|
||||
* @param av anchor vertical. A percentage [0, 100], that appears to be a vertical text anchor point
|
||||
* @param vs appears to indicate if subtitles exist, and the value is always true.
|
||||
* @param sd function is not entirely clear
|
||||
*/
|
||||
public static int[] getSubtitleWindowSettingsOverride(int ap, int ah, int av, boolean vs, boolean sd) {
|
||||
final boolean signatureSpoofing = SettingsEnum.SPOOF_SIGNATURE_VERIFICATION.getBoolean();
|
||||
if (SettingsEnum.DEBUG.getBoolean()) {
|
||||
if (ap != lastAp || ah != lastAh || av != lastAv || vs != lastVs || sd != lastSd) {
|
||||
LogHelper.printDebug(() -> "video: " + VideoInformation.getVideoId() + " spoof: " + signatureSpoofing
|
||||
+ " ap:" + ap + " ah:" + ah + " av:" + av + " vs:" + vs + " sd:" + sd);
|
||||
lastAp = ap;
|
||||
lastAh = ah;
|
||||
lastAv = av;
|
||||
lastVs = vs;
|
||||
lastSd = sd;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WORKAROUND) {
|
||||
return new int[]{ap, ah, av};
|
||||
}
|
||||
|
||||
// Videos with custom captions that specify screen positions appear to always have correct screen positions (even with spoofing).
|
||||
// But for auto generated and most other captions, the spoof incorrectly gives various default Shorts caption settings.
|
||||
// Check for these known default shorts captions parameters, and replace with the known correct values.
|
||||
//
|
||||
// If a regular video uses a custom subtitle setting that match a default short setting,
|
||||
// then this will incorrectly replace the setting.
|
||||
// But, if the video uses multiple subtitles in different screen locations, then detect the non-default values
|
||||
// and do not replace any window settings for the video (regardless if they match a shorts default).
|
||||
if (signatureSpoofing && !PlayerType.getCurrent().isNoneOrHidden()
|
||||
&& numberOfNonDefaultSettingsObserved < NUMBER_OF_NON_DEFAULT_SUBTITLES_BEFORE_ENABLING_PASSTHRU) {
|
||||
for (SubtitleWindowReplacementSettings setting : SubtitleWindowReplacementSettings.values()) {
|
||||
if (setting.match(ap, ah, av, vs, sd)) {
|
||||
return setting.replacementSetting();
|
||||
}
|
||||
}
|
||||
|
||||
numberOfNonDefaultSettingsObserved++;
|
||||
LogHelper.printDebug(() ->
|
||||
numberOfNonDefaultSettingsObserved < NUMBER_OF_NON_DEFAULT_SUBTITLES_BEFORE_ENABLING_PASSTHRU
|
||||
? "Non default subtitle found."
|
||||
: "Multiple non default subtitles found. Allowing all subtitles for this video to pass thru unchanged.");
|
||||
}
|
||||
|
||||
return new int[]{ap, ah, av};
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void setCurrentVideoId(@NonNull String videoId) {
|
||||
try {
|
||||
if (videoId.equals(currentVideoId)) {
|
||||
return;
|
||||
}
|
||||
currentVideoId = videoId;
|
||||
numberOfNonDefaultSettingsObserved = 0;
|
||||
} catch (Exception ex) {
|
||||
LogHelper.printException(() -> "setCurrentVideoId failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Known incorrect default Shorts subtitle parameters, and the corresponding correct (non-Shorts) values.
|
||||
*/
|
||||
private enum SubtitleWindowReplacementSettings {
|
||||
DEFAULT_SHORTS_PARAMETERS_1(10, 50, 0, true, false,
|
||||
34, 50, 95),
|
||||
DEFAULT_SHORTS_PARAMETERS_2(9, 20, 0, true, false,
|
||||
34, 50, 90),
|
||||
DEFAULT_SHORTS_PARAMETERS_3(9, 20, 0, true, true,
|
||||
33, 20, 100);
|
||||
|
||||
// original values
|
||||
final int ap, ah, av;
|
||||
final boolean vs, sd;
|
||||
|
||||
// replacement int values
|
||||
final int[] replacement;
|
||||
|
||||
SubtitleWindowReplacementSettings(int ap, int ah, int av, boolean vs, boolean sd,
|
||||
int replacementAp, int replacementAh, int replacementAv) {
|
||||
this.ap = ap;
|
||||
this.ah = ah;
|
||||
this.av = av;
|
||||
this.vs = vs;
|
||||
this.sd = sd;
|
||||
this.replacement = new int[]{replacementAp, replacementAh, replacementAv};
|
||||
}
|
||||
|
||||
boolean match(int ap, int ah, int av, boolean vs, boolean sd) {
|
||||
return this.ap == ap && this.ah == ah && this.av == av && this.vs == vs && this.sd == sd;
|
||||
}
|
||||
|
||||
int[] replacementSetting() {
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,9 +83,10 @@ final class CustomFilterGroup extends StringFilterGroup {
|
||||
class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||
// Modified implementation from https://stackoverflow.com/a/1507813
|
||||
private int indexOf(final byte[] data, final byte[] pattern) {
|
||||
if (data.length == 0)
|
||||
return -1;
|
||||
// Computes the failure function using a boot-strapping process,
|
||||
// where the pattern is matched against itself.
|
||||
|
||||
final int[] failure = new int[pattern.length];
|
||||
|
||||
int j = 0;
|
||||
@ -103,7 +104,6 @@ class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||
// KMP matching algorithm.
|
||||
|
||||
j = 0;
|
||||
if (data.length == 0) return -1;
|
||||
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
while (j > 0 && pattern[j] != data[i]) {
|
||||
@ -130,7 +130,8 @@ class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||
public FilterGroupResult check(final byte[] bytes) {
|
||||
var matched = false;
|
||||
for (byte[] filter : filters) {
|
||||
if (indexOf(bytes, filter) == -1) continue;
|
||||
if (indexOf(bytes, filter) == -1)
|
||||
continue;
|
||||
|
||||
matched = true;
|
||||
break;
|
||||
@ -181,7 +182,8 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
|
||||
|
||||
protected boolean contains(final V stack) {
|
||||
for (T filterGroup : this) {
|
||||
if (!filterGroup.isEnabled()) continue;
|
||||
if (!filterGroup.isEnabled())
|
||||
continue;
|
||||
|
||||
var result = filterGroup.check(stack);
|
||||
if (result.isFiltered()) {
|
||||
@ -205,7 +207,8 @@ abstract class Filter {
|
||||
final protected ByteArrayFilterGroupList protobufBufferFilterGroups = new ByteArrayFilterGroupList();
|
||||
|
||||
/**
|
||||
* Check if the given path, identifier or protobuf buffer is filtered by any {@link FilterGroup}.
|
||||
* Check if the given path, identifier or protobuf buffer is filtered by any
|
||||
* {@link FilterGroup}.
|
||||
*
|
||||
* @return True if filtered, false otherwise.
|
||||
*/
|
||||
@ -232,34 +235,36 @@ abstract class Filter {
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressWarnings("unused")
|
||||
public final class LithoFilterPatch {
|
||||
private static final Filter[] filters = new Filter[]{
|
||||
new DummyFilter() // Replaced by patch.
|
||||
private static final Filter[] filters = new Filter[] {
|
||||
new DummyFilter() // Replaced by patch.
|
||||
};
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean filter(final StringBuilder pathBuilder, final String identifier, final ByteBuffer protobufBuffer) {
|
||||
// TODO: Maybe this can be moved to the Filter class, to prevent unnecessary string creation
|
||||
// because some filters might not need the path.
|
||||
public static boolean filter(final StringBuilder pathBuilder, final String identifier,
|
||||
final ByteBuffer protobufBuffer) {
|
||||
// TODO: Maybe this can be moved to the Filter class, to prevent unnecessary
|
||||
// string creation
|
||||
// because some filters might not need the path.
|
||||
var path = pathBuilder.toString();
|
||||
|
||||
// It is assumed that protobufBuffer is empty as well in this case.
|
||||
if (path.isEmpty()) return false;
|
||||
if (path.isEmpty())
|
||||
return false;
|
||||
|
||||
LogHelper.printDebug(() -> String.format(
|
||||
"Searching (ID: %s, Buffer-size: %s): %s",
|
||||
identifier, protobufBuffer.remaining(), path
|
||||
));
|
||||
identifier, protobufBuffer.remaining(), path));
|
||||
|
||||
var protobufBufferArray = protobufBuffer.array();
|
||||
|
||||
for (var filter : filters) {
|
||||
var filtered = filter.isFiltered(path, identifier, protobufBufferArray);
|
||||
|
||||
LogHelper.printDebug(() ->
|
||||
String.format("%s (ID: %s): %s", filtered ? "Filtered" : "Unfiltered", identifier, path)
|
||||
);
|
||||
LogHelper.printDebug(
|
||||
() -> String.format("%s (ID: %s): %s", filtered ? "Filtered" : "Unfiltered", identifier, path));
|
||||
|
||||
if (filtered) return true;
|
||||
if (filtered)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -110,7 +110,7 @@ public enum SettingsEnum {
|
||||
HIDE_EMAIL_ADDRESS("revanced_hide_email_address", BOOLEAN, FALSE),
|
||||
HIDE_ENDSCREEN_CARDS("revanced_hide_endscreen_cards", BOOLEAN, TRUE),
|
||||
HIDE_FLOATING_MICROPHONE_BUTTON("revanced_hide_floating_microphone_button", BOOLEAN, TRUE, true),
|
||||
HIDE_FULLSCREEN_PANELS("revanced_hide_fullscreen_panels", BOOLEAN, TRUE),
|
||||
HIDE_FULLSCREEN_PANELS("revanced_hide_fullscreen_panels", BOOLEAN, TRUE, true),
|
||||
HIDE_GET_PREMIUM("revanced_hide_get_premium", BOOLEAN, TRUE),
|
||||
HIDE_INFO_CARDS("revanced_hide_info_cards", BOOLEAN, TRUE),
|
||||
HIDE_LOAD_MORE_BUTTON("revanced_hide_load_more_button", BOOLEAN, TRUE, true),
|
||||
|
@ -1,3 +1,3 @@
|
||||
org.gradle.jvmargs = -Xmx2048m
|
||||
android.useAndroidX = true
|
||||
version = 0.111.0
|
||||
version = 0.111.1-dev.3
|
||||
|
Loading…
Reference in New Issue
Block a user