chore: merge branch dev to main (#425)

This commit is contained in:
oSumAtrIX 2023-07-01 02:36:52 +02:00 committed by GitHub
commit bc91b35994
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 156 deletions

View File

@ -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) # [0.111.0](https://github.com/revanced/revanced-integrations/compare/v0.110.0...v0.111.0) (2023-06-21)

View File

@ -1,14 +1,12 @@
package app.revanced.integrations.patches; package app.revanced.integrations.patches;
import androidx.annotation.NonNull; import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
import androidx.annotation.Nullable;
import app.revanced.integrations.settings.SettingsEnum; import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.shared.PlayerType; import app.revanced.integrations.shared.PlayerType;
import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils; import app.revanced.integrations.utils.ReVancedUtils;
import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
public class SpoofSignatureVerificationPatch { public class SpoofSignatureVerificationPatch {
/** /**
* Enable/disable all workarounds that are required due to signature spoofing. * Enable/disable all workarounds that are required due to signature spoofing.
@ -37,28 +35,6 @@ public class SpoofSignatureVerificationPatch {
"SAFg" // Autoplay in scrim "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. * 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;
}
}
} }

View File

@ -83,9 +83,10 @@ final class CustomFilterGroup extends StringFilterGroup {
class ByteArrayFilterGroup extends FilterGroup<byte[]> { class ByteArrayFilterGroup extends FilterGroup<byte[]> {
// Modified implementation from https://stackoverflow.com/a/1507813 // Modified implementation from https://stackoverflow.com/a/1507813
private int indexOf(final byte[] data, final byte[] pattern) { private int indexOf(final byte[] data, final byte[] pattern) {
if (data.length == 0)
return -1;
// Computes the failure function using a boot-strapping process, // Computes the failure function using a boot-strapping process,
// where the pattern is matched against itself. // where the pattern is matched against itself.
final int[] failure = new int[pattern.length]; final int[] failure = new int[pattern.length];
int j = 0; int j = 0;
@ -103,7 +104,6 @@ class ByteArrayFilterGroup extends FilterGroup<byte[]> {
// KMP matching algorithm. // KMP matching algorithm.
j = 0; j = 0;
if (data.length == 0) return -1;
for (int i = 0; i < data.length; i++) { for (int i = 0; i < data.length; i++) {
while (j > 0 && pattern[j] != data[i]) { while (j > 0 && pattern[j] != data[i]) {
@ -130,7 +130,8 @@ class ByteArrayFilterGroup extends FilterGroup<byte[]> {
public FilterGroupResult check(final byte[] bytes) { public FilterGroupResult check(final byte[] bytes) {
var matched = false; var matched = false;
for (byte[] filter : filters) { for (byte[] filter : filters) {
if (indexOf(bytes, filter) == -1) continue; if (indexOf(bytes, filter) == -1)
continue;
matched = true; matched = true;
break; break;
@ -181,7 +182,8 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
protected boolean contains(final V stack) { protected boolean contains(final V stack) {
for (T filterGroup : this) { for (T filterGroup : this) {
if (!filterGroup.isEnabled()) continue; if (!filterGroup.isEnabled())
continue;
var result = filterGroup.check(stack); var result = filterGroup.check(stack);
if (result.isFiltered()) { if (result.isFiltered()) {
@ -205,7 +207,8 @@ abstract class Filter {
final protected ByteArrayFilterGroupList protobufBufferFilterGroups = new ByteArrayFilterGroupList(); 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. * @return True if filtered, false otherwise.
*/ */
@ -232,34 +235,36 @@ abstract class Filter {
@RequiresApi(api = Build.VERSION_CODES.N) @RequiresApi(api = Build.VERSION_CODES.N)
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class LithoFilterPatch { public final class LithoFilterPatch {
private static final Filter[] filters = new Filter[]{ private static final Filter[] filters = new Filter[] {
new DummyFilter() // Replaced by patch. new DummyFilter() // Replaced by patch.
}; };
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static boolean filter(final StringBuilder pathBuilder, final String identifier, final ByteBuffer protobufBuffer) { public static boolean filter(final StringBuilder pathBuilder, final String identifier,
// TODO: Maybe this can be moved to the Filter class, to prevent unnecessary string creation final ByteBuffer protobufBuffer) {
// because some filters might not need the path. // 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(); var path = pathBuilder.toString();
// It is assumed that protobufBuffer is empty as well in this case. // 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( LogHelper.printDebug(() -> String.format(
"Searching (ID: %s, Buffer-size: %s): %s", "Searching (ID: %s, Buffer-size: %s): %s",
identifier, protobufBuffer.remaining(), path identifier, protobufBuffer.remaining(), path));
));
var protobufBufferArray = protobufBuffer.array(); var protobufBufferArray = protobufBuffer.array();
for (var filter : filters) { for (var filter : filters) {
var filtered = filter.isFiltered(path, identifier, protobufBufferArray); var filtered = filter.isFiltered(path, identifier, protobufBufferArray);
LogHelper.printDebug(() -> LogHelper.printDebug(
String.format("%s (ID: %s): %s", filtered ? "Filtered" : "Unfiltered", identifier, path) () -> String.format("%s (ID: %s): %s", filtered ? "Filtered" : "Unfiltered", identifier, path));
);
if (filtered) return true; if (filtered)
return true;
} }
return false; return false;

View File

@ -110,7 +110,7 @@ public enum SettingsEnum {
HIDE_EMAIL_ADDRESS("revanced_hide_email_address", BOOLEAN, FALSE), HIDE_EMAIL_ADDRESS("revanced_hide_email_address", BOOLEAN, FALSE),
HIDE_ENDSCREEN_CARDS("revanced_hide_endscreen_cards", BOOLEAN, TRUE), HIDE_ENDSCREEN_CARDS("revanced_hide_endscreen_cards", BOOLEAN, TRUE),
HIDE_FLOATING_MICROPHONE_BUTTON("revanced_hide_floating_microphone_button", BOOLEAN, TRUE, 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_GET_PREMIUM("revanced_hide_get_premium", BOOLEAN, TRUE),
HIDE_INFO_CARDS("revanced_hide_info_cards", BOOLEAN, TRUE), HIDE_INFO_CARDS("revanced_hide_info_cards", BOOLEAN, TRUE),
HIDE_LOAD_MORE_BUTTON("revanced_hide_load_more_button", BOOLEAN, TRUE, true), HIDE_LOAD_MORE_BUTTON("revanced_hide_load_more_button", BOOLEAN, TRUE, true),

View File

@ -1,3 +1,3 @@
org.gradle.jvmargs = -Xmx2048m org.gradle.jvmargs = -Xmx2048m
android.useAndroidX = true android.useAndroidX = true
version = 0.111.0 version = 0.111.1-dev.3