chore: Merge branch dev to main (#3438)

This commit is contained in:
oSumAtrIX 2024-08-06 02:05:44 +02:00 committed by GitHub
commit ad64d089f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
158 changed files with 5528 additions and 3430 deletions

View file

@ -17,7 +17,7 @@ jobs:
ref: dev
- name: Pull strings
uses: crowdin/github-action@v1
uses: crowdin/github-action@v2
with:
config: crowdin.yml
download_translations: true

View file

@ -19,7 +19,7 @@ jobs:
fetch-depth: 0
- name: Push strings
uses: crowdin/github-action@v1
uses: crowdin/github-action@v2
with:
config: crowdin.yml
upload_sources: true

View file

@ -42,7 +42,7 @@ jobs:
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
fingerprint: ${{ env.GPG_FINGERPRINT }}
fingerprint: ${{ vars.GPG_FINGERPRINT }}
- name: Release
env:

View file

@ -1,3 +1,145 @@
# [4.12.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.16...v4.12.0-dev.17) (2024-08-06)
### Bug Fixes
* **Google Photos - GmsCore support:** Fix by checking first if a method exists before trying to patch it ([acf38ca](https://github.com/ReVanced/revanced-patches/commit/acf38cafae5eb9896b43f3a6cbd808ac273cd081))
* **Messenger - Disable switching emoji to sticker:** Constrain to last working version `439.0.0.29.119` ([6207c31](https://github.com/ReVanced/revanced-patches/commit/6207c314c657a1188d1081b0a196a61e49cad83b))
### Features
* **CandyLink:** Remove non-functional `Unlock pro` patch ([7ae9f8f](https://github.com/ReVanced/revanced-patches/commit/7ae9f8fa0a349b91853e9554f18e564ca6ff887c))
* **Expense Manager:** Remove non-functional `Unlock pro` patch ([ebbcac7](https://github.com/ReVanced/revanced-patches/commit/ebbcac74fd8598daebb4be0bd7c430c41333e2d4))
* **Instagram:** Remove unnecessary `Hide timeline ads` patch ([5e1d001](https://github.com/ReVanced/revanced-patches/commit/5e1d001056df68e1e2b39f1365215c91bcc9e46b))
# [4.12.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.15...v4.12.0-dev.16) (2024-08-04)
### Bug Fixes
* **YouTube - Spoof client:** Restore livestream audio only playback with iOS spoofing ([#3504](https://github.com/ReVanced/revanced-patches/issues/3504)) ([90d3288](https://github.com/ReVanced/revanced-patches/commit/90d32880906787d82c4b9a7a1099b46dff3a0870))
# [4.12.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.14...v4.12.0-dev.15) (2024-08-02)
### Bug Fixes
* **YouTube - SponsorBlock:** Improve create segment manual seek accuracy ([#3491](https://github.com/ReVanced/revanced-patches/issues/3491)) ([1544981](https://github.com/ReVanced/revanced-patches/commit/15449819ff74b636fb2fa6aacd770142c51d2e5d))
# [4.12.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.13...v4.12.0-dev.14) (2024-08-01)
### Features
* **YouTube - Description components:** Add `Hide 'Key concepts' section` option ([#3495](https://github.com/ReVanced/revanced-patches/issues/3495)) ([d75b645](https://github.com/ReVanced/revanced-patches/commit/d75b64595a7ac26faca4c0ae21923b22f6783975))
# [4.12.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.12...v4.12.0-dev.13) (2024-07-31)
### Features
* **Boost for Reddit:** Add `Disable ads` patch ([#3474](https://github.com/ReVanced/revanced-patches/issues/3474)) ([b292c20](https://github.com/ReVanced/revanced-patches/commit/b292c200bf4ea5b4f71d96690ac011e7843552f0))
# [4.12.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.11...v4.12.0-dev.12) (2024-07-28)
### Features
* **SwissID:** Add `Remove Google Play Integrity Integrity check` patch ([#3478](https://github.com/ReVanced/revanced-patches/issues/3478)) ([60492ae](https://github.com/ReVanced/revanced-patches/commit/60492aea7863e07d8bf1af9380ae9295ca161f3c))
# [4.12.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.10...v4.12.0-dev.11) (2024-07-28)
### Bug Fixes
* **YouTube - Bypass image region restrictions:** Move setting to `Misc` menu ([094ae59](https://github.com/ReVanced/revanced-patches/commit/094ae59fc92663fff6c5d6f5cbece41822a326f9))
# [4.12.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.9...v4.12.0-dev.10) (2024-07-28)
### Bug Fixes
* **YouTube - Client Spoof:** Restore missing high qualities by spoofing the iOS client user agent ([#3468](https://github.com/ReVanced/revanced-patches/issues/3468)) ([0e6ae5f](https://github.com/ReVanced/revanced-patches/commit/0e6ae5fee752a76604cf9b95f9a76c0cbe5f7dae))
# [4.12.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.8...v4.12.0-dev.9) (2024-07-28)
### Bug Fixes
* **YouTube - Spoof client:** Fix tracking history on brand accounts ([#3480](https://github.com/ReVanced/revanced-patches/issues/3480)) ([69c1f16](https://github.com/ReVanced/revanced-patches/commit/69c1f16f7eb0d5759a44f7f7a09b1757ce8f61dd))
# [4.12.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.7...v4.12.0-dev.8) (2024-07-26)
### Bug Fixes
* **YouTube - SponsorBlock:** Correctly show minute timestamp when creating a new segment ([d74c366](https://github.com/ReVanced/revanced-patches/commit/d74c366dbf5f25c20fbfc5a0157c3c15dda82a16))
# [4.12.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.6...v4.12.0-dev.7) (2024-07-24)
### Bug Fixes
* **SoundCloud - Enable offline sync:** Stop crashing by reversing order of patching instructions from last to first to retain indices ([63b6ced](https://github.com/ReVanced/revanced-patches/commit/63b6cede5fa5bcf377ced422da4e861996a41f0d))
# [4.12.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.5...v4.12.0-dev.6) (2024-07-20)
### Features
* Add `Spoof build info` patch ([e7829b4](https://github.com/ReVanced/revanced-patches/commit/e7829b41e782c9feda23b9d6acf48bae277d24d9))
# [4.12.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.4...v4.12.0-dev.5) (2024-07-20)
### Features
* Add `Hide mock location` patch ([#3417](https://github.com/ReVanced/revanced-patches/issues/3417)) ([5f81b40](https://github.com/ReVanced/revanced-patches/commit/5f81b40e7d5567fb5689d08ccc9caeaa267c3143))
* **Google Photos:** Add `GmsCore support` patch ([#3414](https://github.com/ReVanced/revanced-patches/issues/3414)) ([24528e0](https://github.com/ReVanced/revanced-patches/commit/24528e0a6eec17ce0a3c52f8862585933615ad28))
# [4.12.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.3...v4.12.0-dev.4) (2024-07-20)
### Features
* **Google News:** Add `Enable CustomTabs` and `GmsCore support` patch ([#3111](https://github.com/ReVanced/revanced-patches/issues/3111)) ([ad59096](https://github.com/ReVanced/revanced-patches/commit/ad590962275f888b335252ad5bed0f34e959d3c7))
# [4.12.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.2...v4.12.0-dev.3) (2024-07-18)
### Bug Fixes
* **Instagram - Hide ads:** Restore compatibility with latest version by fixing fingerprint ([#3455](https://github.com/ReVanced/revanced-patches/issues/3455)) ([4505fa4](https://github.com/ReVanced/revanced-patches/commit/4505fa4138bb55c8957790239c01b8dda63d6cdd))
# [4.12.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.1...v4.12.0-dev.2) (2024-07-15)
### Features
* **YouTube:** Add `Bypass image region restrictions` patch ([#3442](https://github.com/ReVanced/revanced-patches/issues/3442)) ([765fab2](https://github.com/ReVanced/revanced-patches/commit/765fab2af2769349446cc0f2109343ef3bd8c621))
# [4.12.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.11.1-dev.1...v4.12.0-dev.1) (2024-07-13)
### Features
* **SoundCloud:** Add `Enable offline sync` patch ([#3407](https://github.com/ReVanced/revanced-patches/issues/3407)) ([4de86c6](https://github.com/ReVanced/revanced-patches/commit/4de86c6407376bcd3cc0513a2f0707410b8d7ccd))
## [4.11.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.11.0...v4.11.1-dev.1) (2024-07-12)
### Bug Fixes
* **YouTube - Hide keyword content:** Do not hide flyout menu ([687c9f7](https://github.com/ReVanced/revanced-patches/commit/687c9f7eb03cca5f7b3486f07f2e3453ebc77faf))
## [4.11.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.11.0...v4.11.1-dev.1) (2024-07-12)
### Bug Fixes
* **YouTube - Hide keyword content:** Do not hide flyout menu ([687c9f7](https://github.com/ReVanced/revanced-patches/commit/687c9f7eb03cca5f7b3486f07f2e3453ebc77faf))
# [4.11.0](https://github.com/ReVanced/revanced-patches/compare/v4.10.0...v4.11.0) (2024-07-11)

View file

@ -22,6 +22,50 @@ public final class app/revanced/patches/all/interaction/gestures/PredictiveBackG
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/all/location/hide/HideMockLocationPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
public static final field INSTANCE Lapp/revanced/patches/all/location/hide/HideMockLocationPatch;
public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair;
public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V
}
public abstract class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
public fun <init> ()V
public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair;
protected fun getBoard ()Ljava/lang/String;
protected fun getBootloader ()Ljava/lang/String;
protected fun getBrand ()Ljava/lang/String;
protected fun getCpuAbi ()Ljava/lang/String;
protected fun getCpuAbi2 ()Ljava/lang/String;
protected fun getDevice ()Ljava/lang/String;
protected fun getDisplay ()Ljava/lang/String;
protected fun getFingerprint ()Ljava/lang/String;
protected fun getHardware ()Ljava/lang/String;
protected fun getHost ()Ljava/lang/String;
protected fun getId ()Ljava/lang/String;
protected fun getManufacturer ()Ljava/lang/String;
protected fun getModel ()Ljava/lang/String;
protected fun getOdmSku ()Ljava/lang/String;
protected fun getProduct ()Ljava/lang/String;
protected fun getRadio ()Ljava/lang/String;
protected fun getSerial ()Ljava/lang/String;
protected fun getSku ()Ljava/lang/String;
protected fun getSocManufacturer ()Ljava/lang/String;
protected fun getSocModel ()Ljava/lang/String;
protected fun getTags ()Ljava/lang/String;
protected fun getTime ()Ljava/lang/Long;
protected fun getType ()Ljava/lang/String;
protected fun getUser ()Ljava/lang/String;
public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V
}
public final class app/revanced/patches/all/misc/build/SpoofBuildInfoPatch : app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch {
public fun <init> ()V
}
public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
@ -217,6 +261,36 @@ public final class app/revanced/patches/finanzonline/detection/root/RootDetectio
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/googlenews/customtabs/EnableCustomTabs : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/googlenews/customtabs/EnableCustomTabs;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch {
public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch;
}
public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/gms/GmsCoreSupportResourcePatch;
}
public final class app/revanced/patches/googlenews/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/integrations/IntegrationsPatch;
}
public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch {
public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch;
}
public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/gms/GmsCoreSupportResourcePatch;
}
public final class app/revanced/patches/googlephotos/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/integrations/IntegrationsPatch;
}
public final class app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -349,6 +423,12 @@ public final class app/revanced/patches/music/ad/video/HideMusicVideoAds : app/r
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/ad/video/HideVideoAds : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/ad/video/HideVideoAds;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/ad/video/MusicVideoAdsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/ad/video/MusicVideoAdsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -610,6 +690,12 @@ public final class app/revanced/patches/reddit/customclients/baconreader/api/Spo
public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
}
public final class app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch;
public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
@ -1050,6 +1136,12 @@ public final class app/revanced/patches/soundcloud/analytics/DisableTelemetryPat
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/spotify/layout/theme/CustomThemePatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/spotify/layout/theme/CustomThemePatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
@ -1098,6 +1190,12 @@ public final class app/revanced/patches/strava/upselling/DisableSubscriptionSugg
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheck : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheck;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/ticktick/misc/themeunlock/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/ticktick/misc/themeunlock/UnlockProPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -1718,6 +1816,12 @@ public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThu
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictions : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictions;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/announcements/AnnouncementsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -1784,6 +1888,16 @@ public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportResourceP
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook;
public final fun addImageUrlErrorCallbackHook (Ljava/lang/String;)V
public final fun addImageUrlHook (Ljava/lang/String;Z)V
public static synthetic fun addImageUrlHook$default (Lapp/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook;Ljava/lang/String;ZILjava/lang/Object;)V
public final fun addImageUrlSuccessCallbackHook (Ljava/lang/String;)V
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/integrations/IntegrationsPatch;
}
@ -1996,6 +2110,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
}
public final class app/revanced/util/BytecodeUtilsKt {
public static final fun alsoResolve (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
@ -2011,7 +2126,11 @@ public final class app/revanced/util/BytecodeUtilsKt {
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
public static final fun resultOrThrow (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
public static final fun returnEarly (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Z)V
public static final fun returnEarly (Ljava/lang/Iterable;Z)V
public static final fun returnEarly (Ljava/util/List;Z)V
public static synthetic fun returnEarly$default (Lapp/revanced/patcher/fingerprint/MethodFingerprint;ZILjava/lang/Object;)V
public static synthetic fun returnEarly$default (Ljava/lang/Iterable;ZILjava/lang/Object;)V
public static synthetic fun returnEarly$default (Ljava/util/List;ZILjava/lang/Object;)V
public static final fun transformMethods (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V
public static final fun traverseClassHierarchy (Lapp/revanced/patcher/data/BytecodeContext;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V

View file

@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 4.11.0
version = 4.12.0-dev.17

View file

@ -2,9 +2,9 @@
revanced-patcher = "19.3.1"
#noinspection GradleDependency
smali = "3.0.5" # 3.0.7 breaks binary compatibility. Tracking https://github.com/google/smali/issues/58.
guava = "33.1.0-jre"
gson = "2.10.1"
binary-compatibility-validator = "0.14.0"
guava = "33.2.1-jre"
gson = "2.11.0"
binary-compatibility-validator = "0.15.1"
kotlin = "2.0.0"
[libraries]

View file

@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

2152
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
"@saithodev/semantic-release-backmerge": "^4.0.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.9.1",
"semantic-release": "^23.0.8"
"gradle-semantic-release-plugin": "^1.9.2",
"semantic-release": "^24.0.0"
}
}

View file

@ -0,0 +1,56 @@
@file:Suppress("unused")
package app.revanced.patches.all.location.hide
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.fromMethodReference
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Hide mock location",
description = "Prevents the app from knowing the device location is being mocked by a third party app.",
use = false
)
object HideMockLocationPatch : BaseTransformInstructionsPatch<Pair<Instruction, Int>>() {
override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
): Pair<Instruction, Int>? {
val reference = instruction.getReference<MethodReference>() ?: return null
if (fromMethodReference<MethodCall>(reference) == null) return null
return instruction to instructionIndex
}
override fun transform(mutableMethod: MutableMethod, entry: Pair<Instruction, Int>) {
val (instruction, index) = entry
instruction as FiveRegisterInstruction
// Replace return value with a constant `false` boolean.
mutableMethod.replaceInstruction(
index + 1,
"const/4 v${instruction.registerC}, 0x0"
)
}
}
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String
) : IMethodCall {
IsMock("Landroid/location/Location;", "isMock", emptyArray(), "Z"),
IsFromMockProvider("Landroid/location/Location;", "isFromMockProvider", emptyArray(), "Z")
}

View file

@ -0,0 +1,120 @@
package app.revanced.patches.all.misc.build
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
abstract class BaseSpoofBuildInfoPatch : BaseTransformInstructionsPatch<Pair<Int, Pair<String, String>>>() {
// The build information supported32BitAbis, supported64BitAbis, and supportedAbis are not supported for now,
// because initializing an array in transform is a bit more complex.
protected open val board: String? = null
protected open val bootloader: String? = null
protected open val brand: String? = null
protected open val cpuAbi: String? = null
protected open val cpuAbi2: String? = null
protected open val device: String? = null
protected open val display: String? = null
protected open val fingerprint: String? = null
protected open val hardware: String? = null
protected open val host: String? = null
protected open val id: String? = null
protected open val manufacturer: String? = null
protected open val model: String? = null
protected open val odmSku: String? = null
protected open val product: String? = null
protected open val radio: String? = null
protected open val serial: String? = null
protected open val sku: String? = null
protected open val socManufacturer: String? = null
protected open val socModel: String? = null
protected open val tags: String? = null
protected open val time: Long? = null
protected open val type: String? = null
protected open val user: String? = null
// Lazy, so that patch options above are initialized before they are accessed.
private val replacements: Map<String, Pair<String, String>> by lazy {
buildMap {
if (board != null) put("BOARD", "const-string" to "\"$board\"")
if (bootloader != null) put("BOOTLOADER", "const-string" to "\"$bootloader\"")
if (brand != null) put("BRAND", "const-string" to "\"$brand\"")
if (cpuAbi != null) put("CPU_ABI", "const-string" to "\"$cpuAbi\"")
if (cpuAbi2 != null) put("CPU_ABI2", "const-string" to "\"$cpuAbi2\"")
if (device != null) put("DEVICE", "const-string" to "\"$device\"")
if (display != null) put("DISPLAY", "const-string" to "\"$display\"")
if (fingerprint != null) put("FINGERPRINT", "const-string" to "\"$fingerprint\"")
if (hardware != null) put("HARDWARE", "const-string" to "\"$hardware\"")
if (host != null) put("HOST", "const-string" to "\"$host\"")
if (id != null) put("ID", "const-string" to "\"$id\"")
if (manufacturer != null) put("MANUFACTURER", "const-string" to "\"$manufacturer\"")
if (model != null) put("MODEL", "const-string" to "\"$model\"")
if (odmSku != null) put("ODM_SKU", "const-string" to "\"$odmSku\"")
if (product != null) put("PRODUCT", "const-string" to "\"$product\"")
if (radio != null) put("RADIO", "const-string" to "\"$radio\"")
if (serial != null) put("SERIAL", "const-string" to "\"$serial\"")
if (sku != null) put("SKU", "const-string" to "\"$sku\"")
if (socManufacturer != null) put("SOC_MANUFACTURER", "const-string" to "\"$socManufacturer\"")
if (socModel != null) put("SOC_MODEL", "const-string" to "\"$socModel\"")
if (tags != null) put("TAGS", "const-string" to "\"$tags\"")
if (time != null) put("TIME", "const-wide" to "$time")
if (type != null) put("TYPE", "const-string" to "\"$type\"")
if (user != null) put("USER", "const-string" to "\"$user\"")
}
}
override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
): Pair<Int, Pair<String, String>>? {
val reference = instruction.getReference<FieldReference>() ?: return null
if (reference.definingClass != BUILD_CLASS_DESCRIPTOR) return null
return replacements[reference.name]?.let { instructionIndex to it }
}
override fun transform(mutableMethod: MutableMethod, entry: Pair<Int, Pair<String, String>>) {
val (index, replacement) = entry
val (opcode, operand) = replacement
val register = mutableMethod.getInstruction<OneRegisterInstruction>(index).registerA
mutableMethod.replaceInstruction(index, "$opcode v$register, $operand")
}
private companion object {
private const val BUILD_CLASS_DESCRIPTOR = "Landroid/os/Build;"
}
}

View file

@ -0,0 +1,183 @@
package app.revanced.patches.all.misc.build
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.longPatchOption
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
@Patch(
name = "Spoof build info",
description = "Spoof the information about the current build.",
use = false
)
@Suppress("unused")
class SpoofBuildInfoPatch : BaseSpoofBuildInfoPatch() {
override val board by stringPatchOption(
key = "board",
default = null,
title = "Board",
description = "The name of the underlying board, like \"goldfish\"."
)
override val bootloader by stringPatchOption(
key = "bootloader",
default = null,
title = "Bootloader",
description = "The system bootloader version number."
)
override val brand by stringPatchOption(
key = "brand",
default = null,
title = "Brand",
description = "The consumer-visible brand with which the product/hardware will be associated, if any."
)
override val cpuAbi by stringPatchOption(
key = "cpu-abi",
default = null,
title = "CPU ABI",
description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead."
)
override val cpuAbi2 by stringPatchOption(
key = "cpu-abi-2",
default = null,
title = "CPU ABI 2",
description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead."
)
override val device by stringPatchOption(
key = "device",
default = null,
title = "Device",
description = "The name of the industrial design."
)
override val display by stringPatchOption(
key = "display",
default = null,
title = "Display",
description = "A build ID string meant for displaying to the user."
)
override val fingerprint by stringPatchOption(
key = "fingerprint",
default = null,
title = "Fingerprint",
description = "A string that uniquely identifies this build."
)
override val hardware by stringPatchOption(
key = "hardware",
default = null,
title = "Hardware",
description = "The name of the hardware (from the kernel command line or /proc)."
)
override val host by stringPatchOption(
key = "host",
default = null,
title = "Host",
description = "The host."
)
override val id by stringPatchOption(
key = "id",
default = null,
title = "ID",
description = "Either a changelist number, or a label like \"M4-rc20\"."
)
override val manufacturer by stringPatchOption(
key = "manufacturer",
default = null,
title = "Manufacturer",
description = "The manufacturer of the product/hardware."
)
override val model by stringPatchOption(
key = "model",
default = null,
title = "Model",
description = "The end-user-visible name for the end product."
)
override val odmSku by stringPatchOption(
key = "odm-sku",
default = null,
title = "ODM SKU",
description = "The SKU of the device as set by the original design manufacturer (ODM)."
)
override val product by stringPatchOption(
key = "product",
default = null,
title = "Product",
description = "The name of the overall product."
)
override val radio by stringPatchOption(
key = "radio",
default = null,
title = "Radio",
description = "This field was deprecated in API level 15. " +
"The radio firmware version is frequently not available when this class is initialized, " +
"leading to a blank or \"unknown\" value for this string. Use getRadioVersion() instead."
)
override val serial by stringPatchOption(
key = "serial",
default = null,
title = "Serial",
description = "This field was deprecated in API level 26. Use getSerial() instead."
)
override val sku by stringPatchOption(
key = "sku",
default = null,
title = "SKU",
description = "The SKU of the hardware (from the kernel command line)."
)
override val socManufacturer by stringPatchOption(
key = "soc-manufacturer",
default = null,
title = "SOC Manufacturer",
description = "The manufacturer of the device's primary system-on-chip."
)
override val socModel by stringPatchOption(
key = "soc-model",
default = null,
title = "SOC Model",
description = "The model name of the device's primary system-on-chip."
)
override val tags by stringPatchOption(
key = "tags",
default = null,
title = "Tags",
description = "Comma-separated tags describing the build, like \"unsigned,debug\"."
)
override val time by longPatchOption(
key = "time",
default = null,
title = "Time",
description = "The time at which the build was produced, given in milliseconds since the UNIX epoch."
)
override val type by stringPatchOption(
key = "type",
default = null,
title = "Type",
description = "The type of build, like \"user\" or \"eng\"."
)
override val user by stringPatchOption(
key = "user",
default = null,
title = "User",
description = "The user."
)
}

View file

@ -125,6 +125,7 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
"sk-rSK" to "sk",
"sl-rSI" to "sl",
"sq-rAL" to "sq",
"sr-rCS" to "b+sr+Latn",
"sr-rSP" to "sr",
"sv-rSE" to "sv",
"sw-rKE" to "sw",
@ -347,7 +348,10 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
val targetFile =
context.get("res/$value/$resourceFileName.xml").also {
it.parentFile?.mkdirs()
it.createNewFile()
if(it.createNewFile()) {
it.writeText("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>")
}
}
context.xmlEditor[targetFile.path].let { editor ->

View file

@ -1,20 +1,23 @@
package app.revanced.patches.candylinkvpn
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.candylinkvpn.fingerprints.IsPremiumPurchasedFingerprint
import app.revanced.util.exception
@Patch(
name = "Unlock pro",
compatiblePackages = [CompatiblePackage("com.candylink.openvpn")]
compatiblePackages = [CompatiblePackage("com.candylink.openvpn")],
)
@Deprecated(
"This patch does not work anymore and will be removed in the future, " +
"because the servers now check the purchase status.",
)
@Suppress("unused")
object UnlockProPatch : BytecodePatch(
setOf(IsPremiumPurchasedFingerprint)
setOf(IsPremiumPurchasedFingerprint),
) {
override fun execute(context: BytecodeContext) {
IsPremiumPurchasedFingerprint.result?.mutableMethod?.addInstructions(
@ -22,7 +25,7 @@ object UnlockProPatch : BytecodePatch(
"""
const/4 v0, 0x1
return v0
"""
""",
) ?: throw IsPremiumPurchasedFingerprint.exception
}
}
}

View file

@ -0,0 +1,32 @@
package app.revanced.patches.googlenews.customtabs
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.googlenews.customtabs.fingerprints.LaunchCustomTabFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Enable CustomTabs",
description = "Enables CustomTabs to open articles in your default browser.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.magazines")],
)
@Suppress("unused")
object EnableCustomTabs : BytecodePatch(
setOf(LaunchCustomTabFingerprint)
) {
override fun execute(context: BytecodeContext) {
LaunchCustomTabFingerprint.resultOrThrow().let { result ->
result.mutableMethod.apply {
val checkIndex = result.scanResult.patternScanResult!!.endIndex + 1
val register = getInstruction<OneRegisterInstruction>(checkIndex).registerA
replaceInstruction(checkIndex, "const/4 v$register, 0x1")
}
}
}
}

View file

@ -0,0 +1,18 @@
package app.revanced.patches.googlenews.customtabs.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object LaunchCustomTabFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
opcodes = listOf(
Opcode.IPUT_OBJECT,
Opcode.CONST_4,
Opcode.IPUT,
Opcode.CONST_4,
Opcode.IPUT_BOOLEAN,
),
customFingerprint = { _, classDef -> classDef.endsWith("CustomTabsArticleLauncher;") },
)

View file

@ -0,0 +1,6 @@
package app.revanced.patches.googlenews.misc.gms
internal object Constants {
const val MAGAZINES_PACKAGE_NAME = "com.google.android.apps.magazines"
const val REVANCED_MAGAZINES_PACKAGE_NAME = "app.revanced.android.magazines"
}

View file

@ -0,0 +1,23 @@
package app.revanced.patches.googlenews.misc.gms
import app.revanced.patches.googlenews.misc.gms.Constants.MAGAZINES_PACKAGE_NAME
import app.revanced.patches.googlenews.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME
import app.revanced.patches.googlenews.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.googlenews.misc.gms.fingerprints.MagazinesActivityOnCreateFingerprint
import app.revanced.patches.googlenews.misc.integrations.IntegrationsPatch
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch
@Suppress("unused")
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
fromPackageName = MAGAZINES_PACKAGE_NAME,
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
primeMethodFingerprint = null,
mainActivityOnCreateFingerprint = MagazinesActivityOnCreateFingerprint,
integrationsPatchDependency = IntegrationsPatch::class,
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
// Remove version constraint,
// once https://github.com/ReVanced/revanced-patches/pull/3111#issuecomment-2240877277 is resolved.
compatiblePackages = setOf(CompatiblePackage(MAGAZINES_PACKAGE_NAME, setOf("5.108.0.644447823"))),
) {
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
}

View file

@ -0,0 +1,11 @@
package app.revanced.patches.googlenews.misc.gms
import app.revanced.patches.googlenews.misc.gms.Constants.MAGAZINES_PACKAGE_NAME
import app.revanced.patches.googlenews.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportResourcePatch
object GmsCoreSupportResourcePatch : BaseGmsCoreSupportResourcePatch(
fromPackageName = MAGAZINES_PACKAGE_NAME,
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
)

View file

@ -0,0 +1,9 @@
package app.revanced.patches.googlenews.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object MagazinesActivityOnCreateFingerprint : MethodFingerprint(
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;")
},
)

View file

@ -0,0 +1,7 @@
package app.revanced.patches.googlenews.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object PrimeMethodFingerprint : MethodFingerprint(
strings = listOf("com.google.android.GoogleCamera", "com.android.vending"),
)

View file

@ -0,0 +1,10 @@
package app.revanced.patches.googlenews.misc.integrations
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.googlenews.misc.integrations.fingerprints.StartActivityInitFingerprint
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
@Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch(
setOf(StartActivityInitFingerprint),
)

View file

@ -0,0 +1,41 @@
package app.revanced.patches.googlenews.misc.integrations.fingerprints
import app.revanced.patches.googlenews.misc.integrations.fingerprints.StartActivityInitFingerprint.getApplicationContextIndex
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object StartActivityInitFingerprint : IntegrationsFingerprint(
opcodes = listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.IPUT_OBJECT,
Opcode.IPUT_BOOLEAN,
Opcode.INVOKE_VIRTUAL, // Calls startActivity.getApplicationContext().
Opcode.MOVE_RESULT_OBJECT,
),
insertIndexResolver = { method ->
getApplicationContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "getApplicationContext"
}
getApplicationContextIndex + 2 // Below the move-result-object instruction.
},
contextRegisterResolver = { method ->
val moveResultInstruction = method.implementation!!.instructions.elementAt(getApplicationContextIndex + 1)
as OneRegisterInstruction
moveResultInstruction.registerA
},
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;")
},
) {
private var getApplicationContextIndex = -1
}

View file

@ -0,0 +1,6 @@
package app.revanced.patches.googlephotos.misc.gms
internal object Constants {
const val PHOTOS_PACKAGE_NAME = "com.google.android.apps.photos"
const val REVANCED_PHOTOS_PACKAGE_NAME = "app.revanced.android.photos"
}

View file

@ -0,0 +1,21 @@
package app.revanced.patches.googlephotos.misc.gms
import app.revanced.patches.googlephotos.misc.gms.Constants.PHOTOS_PACKAGE_NAME
import app.revanced.patches.googlephotos.misc.gms.Constants.REVANCED_PHOTOS_PACKAGE_NAME
import app.revanced.patches.googlephotos.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.googlephotos.misc.gms.fingerprints.PhotosActivityOnCreateFingerprint
import app.revanced.patches.googlephotos.misc.integrations.IntegrationsPatch
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch
@Suppress("unused")
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
fromPackageName = PHOTOS_PACKAGE_NAME,
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
primeMethodFingerprint = null,
mainActivityOnCreateFingerprint = PhotosActivityOnCreateFingerprint,
integrationsPatchDependency = IntegrationsPatch::class,
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
compatiblePackages = setOf(CompatiblePackage(PHOTOS_PACKAGE_NAME)),
) {
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
}

View file

@ -0,0 +1,11 @@
package app.revanced.patches.googlephotos.misc.gms
import app.revanced.patches.googlephotos.misc.gms.Constants.PHOTOS_PACKAGE_NAME
import app.revanced.patches.googlephotos.misc.gms.Constants.REVANCED_PHOTOS_PACKAGE_NAME
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportResourcePatch
object GmsCoreSupportResourcePatch : BaseGmsCoreSupportResourcePatch(
fromPackageName = PHOTOS_PACKAGE_NAME,
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
)

View file

@ -0,0 +1,9 @@
package app.revanced.patches.googlephotos.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object PhotosActivityOnCreateFingerprint : MethodFingerprint(
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;")
},
)

View file

@ -0,0 +1,10 @@
package app.revanced.patches.googlephotos.misc.integrations
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.googlephotos.misc.integrations.fingerprints.HomeActivityInitFingerprint
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
@Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch(
setOf(HomeActivityInitFingerprint),
)

View file

@ -0,0 +1,37 @@
package app.revanced.patches.googlephotos.misc.integrations.fingerprints
import app.revanced.patches.googlephotos.misc.integrations.fingerprints.HomeActivityInitFingerprint.getApplicationContextIndex
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object HomeActivityInitFingerprint : IntegrationsFingerprint(
opcodes = listOf(
Opcode.CONST_STRING,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_NEZ,
Opcode.INVOKE_VIRTUAL, // Calls getApplicationContext().
Opcode.MOVE_RESULT_OBJECT,
),
insertIndexResolver = { method ->
getApplicationContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "getApplicationContext"
}
getApplicationContextIndex + 2 // Below the move-result-object instruction.
},
contextRegisterResolver = { method ->
val moveResultInstruction = method.implementation!!.instructions.elementAt(getApplicationContextIndex + 1)
as OneRegisterInstruction
moveResultInstruction.registerA
},
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;")
},
) {
private var getApplicationContextIndex = -1
}

View file

@ -12,15 +12,15 @@ import app.revanced.util.returnEarly
@Patch(
name = "Remove root detection",
description = "Removes the check for root permissions and unlocked bootloader.",
compatiblePackages = [CompatiblePackage("at.gv.oe.app")]
compatiblePackages = [CompatiblePackage("at.gv.oe.app")],
)
@Suppress("unused")
object RootDetectionPatch : BytecodePatch(
setOf(AttestationSupportedCheckFingerprint, BootloaderCheckFingerprint, RootCheckFingerprint)
setOf(AttestationSupportedCheckFingerprint, BootloaderCheckFingerprint, RootCheckFingerprint),
) {
override fun execute(context: BytecodeContext) = listOf(
override fun execute(context: BytecodeContext) = setOf(
AttestationSupportedCheckFingerprint,
BootloaderCheckFingerprint,
RootCheckFingerprint
RootCheckFingerprint,
).returnEarly(true)
}

View file

@ -2,16 +2,13 @@ package app.revanced.patches.instagram.patches.ad.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object AdInjectorFingerprint : MethodFingerprint(
returnType = "Z",
accessFlags = AccessFlags.PRIVATE.value,
parameters = listOf("L", "L"),
opcodes = listOf(
Opcode.IGET,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
strings = listOf(
"SponsoredContentController.insertItem",
"SponsoredContentController::Delivery",
),
strings = listOf("SponsoredContentController::Delivery"),
)

View file

@ -14,9 +14,9 @@ import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@Patch(
name = "Hide timeline ads",
compatiblePackages = [CompatiblePackage("com.instagram.android")],
)
@Deprecated("This patch is not needed anymore.", replaceWith = ReplaceWith("HideAdsPatch"))
@Suppress("unused")
object HideTimelineAdsPatch : BytecodePatch(
setOf(

View file

@ -13,7 +13,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Disable switching emoji to sticker",
description = "Disables switching from emoji to sticker search mode in message input field.",
compatiblePackages = [CompatiblePackage("com.facebook.orca")],
compatiblePackages = [CompatiblePackage("com.facebook.orca", ["439.0.0.29.119"])],
)
@Suppress("unused")
object DisableSwitchingEmojiToStickerPatch : BytecodePatch(

View file

@ -8,20 +8,20 @@ import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.moneymanager.fingerprints.UnlockProFingerprint
@Patch(
name = "Unlock pro",
compatiblePackages = [CompatiblePackage("com.ithebk.expensemanager")]
compatiblePackages = [CompatiblePackage("com.ithebk.expensemanager")],
)
@Deprecated("This patch is not functional anymore and will be removed in the future.")
@Suppress("unused")
object UnlockProPatch : BytecodePatch(
setOf(UnlockProFingerprint)
){
setOf(UnlockProFingerprint),
) {
override fun execute(context: BytecodeContext) {
UnlockProFingerprint.result!!.mutableMethod.addInstructions(
UnlockProFingerprint.result!!.mutableMethod.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
""",
)
}
}
}

View file

@ -6,11 +6,12 @@ import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsParentFingerprint
import app.revanced.patches.music.ad.video.fingerprints.ShowVideoAdsParentFingerprint
import app.revanced.util.exception
@Patch(
name = "Hide music video ads",
name = "Hide video ads",
description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.",
compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
@ -25,24 +26,32 @@ import app.revanced.util.exception
],
)
@Suppress("unused")
object HideMusicVideoAds : BytecodePatch(
setOf(ShowMusicVideoAdsParentFingerprint),
object HideVideoAds : BytecodePatch(
setOf(ShowVideoAdsParentFingerprint),
) {
override fun execute(context: BytecodeContext) {
ShowMusicVideoAdsParentFingerprint.result?.let {
val showMusicVideoAdsMethod = context
ShowVideoAdsParentFingerprint.result?.let {
val showVideoAdsMethod = context
.toMethodWalker(it.mutableMethod)
.nextMethod(it.scanResult.patternScanResult!!.startIndex + 1, true).getMethod() as MutableMethod
showMusicVideoAdsMethod.addInstruction(0, "const/4 p1, 0x0")
} ?: throw ShowMusicVideoAdsParentFingerprint.exception
showVideoAdsMethod.addInstruction(0, "const/4 p1, 0x0")
} ?: throw ShowVideoAdsParentFingerprint.exception
}
}
@Deprecated("This patch class has been renamed to HideMusicVideoAds.")
@Deprecated("This patch class has been renamed to HideVideoAds.")
object HideMusicVideoAds : BytecodePatch(
dependencies = setOf(HideVideoAds::class)
) {
override fun execute(context: BytecodeContext) {
}
}
@Deprecated("This patch class has been renamed to HideVideoAds.")
object MusicVideoAdsPatch : BytecodePatch(
dependencies = setOf(HideMusicVideoAds::class),
) {
override fun execute(context: BytecodeContext) {
}
}
}

View file

@ -3,7 +3,7 @@ package app.revanced.patches.music.ad.video.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object ShowMusicVideoAdsParentFingerprint : MethodFingerprint(
internal object ShowVideoAdsParentFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,

View file

@ -3,7 +3,9 @@ package app.revanced.patches.music.misc.gms
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.music.misc.gms.fingerprints.*
import app.revanced.patches.music.misc.gms.fingerprints.CastDynamiteModuleV2Fingerprint
import app.revanced.patches.music.misc.gms.fingerprints.MusicActivityOnCreateFingerprint
import app.revanced.patches.music.misc.gms.fingerprints.PrimeMethodFingerprint
import app.revanced.patches.music.misc.integrations.IntegrationsPatch
import app.revanced.patches.shared.fingerprints.CastContextFetchFingerprint
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch
@ -14,9 +16,6 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
toPackageName = REVANCED_MUSIC_PACKAGE_NAME,
primeMethodFingerprint = PrimeMethodFingerprint,
earlyReturnFingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
),
@ -32,13 +31,10 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
"7.01.53",
"7.02.52",
"7.03.52",
)
)
),
),
),
fingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeMethodFingerprint,

View file

@ -1,18 +0,0 @@
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object GooglePlayUtilityFingerprint : MethodFingerprint(
"I",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf(
"This should never happen.",
"MetadataValueReader",
"GooglePlayServicesUtil",
"com.android.vending",
"android.hardware.type.embedded"
)
)

View file

@ -1,12 +0,0 @@
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object ServiceCheckFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf("Google Play Services not available"),
)

View file

@ -15,5 +15,5 @@ abstract class BaseDisableAdsPatch(
compatiblePackages = compatiblePackages,
fingerprints = setOf(IsAdsEnabledFingerprint),
) {
override fun execute(context: BytecodeContext) = listOf(IsAdsEnabledFingerprint).returnEarly()
override fun execute(context: BytecodeContext) = IsAdsEnabledFingerprint.returnEarly()
}

View file

@ -0,0 +1,23 @@
package app.revanced.patches.reddit.customclients.boostforreddit.ads
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patches.reddit.customclients.boostforreddit.ads.fingerprints.*
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.util.resultOrThrow
@Patch(
name = "Disable ads",
compatiblePackages = [CompatiblePackage("com.rubenmayayo.reddit")],
)
@Suppress("unused")
object DisableAdsPatch : BytecodePatch(
setOf(MaxMediationFingerprint, AdmobMediationFingerprint),
) {
override fun execute(context: BytecodeContext) =
arrayOf(MaxMediationFingerprint, AdmobMediationFingerprint).forEach {
it.resultOrThrow().mutableMethod.addInstructions(0, "return-void")
}
}

View file

@ -0,0 +1,7 @@
package app.revanced.patches.reddit.customclients.boostforreddit.ads.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object AdmobMediationFingerprint : MethodFingerprint(
strings = listOf("AdmobMediation: Attempting to initialize SDK")
)

View file

@ -0,0 +1,7 @@
package app.revanced.patches.reddit.customclients.boostforreddit.ads.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object MaxMediationFingerprint : MethodFingerprint(
strings = listOf("MaxMediation: Attempting to initialize SDK")
)

View file

@ -22,5 +22,5 @@ object UnlockSubscriptionPatch : BytecodePatch(
setOf(StartSubscriptionActivityFingerprint, BillingClientOnServiceConnected),
) {
override fun execute(context: BytecodeContext) =
listOf(StartSubscriptionActivityFingerprint, BillingClientOnServiceConnected).returnEarly()
setOf(StartSubscriptionActivityFingerprint, BillingClientOnServiceConnected).returnEarly()
}

View file

@ -11,8 +11,11 @@ import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.ACTIONS
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.AUTHORITIES
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch.Constants.PERMISSIONS
import app.revanced.patches.shared.misc.gms.fingerprints.CastDynamiteModuleFingerprint
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint.GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME
import app.revanced.patches.shared.misc.gms.fingerprints.GooglePlayUtilityFingerprint
import app.revanced.patches.shared.misc.gms.fingerprints.ServiceCheckFingerprint
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.returnEarly
@ -42,8 +45,8 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
abstract class BaseGmsCoreSupportPatch(
private val fromPackageName: String,
private val toPackageName: String,
private val primeMethodFingerprint: MethodFingerprint,
private val earlyReturnFingerprints: Set<MethodFingerprint>,
private val primeMethodFingerprint: MethodFingerprint?,
private val earlyReturnFingerprints: Set<MethodFingerprint> = setOf(),
private val mainActivityOnCreateFingerprint: MethodFingerprint,
private val integrationsPatchDependency: PatchClass,
gmsCoreSupportResourcePatch: BaseGmsCoreSupportResourcePatch,
@ -62,6 +65,9 @@ abstract class BaseGmsCoreSupportPatch(
compatiblePackages = compatiblePackages,
fingerprints = setOf(
GmsCoreSupportFingerprint,
GooglePlayUtilityFingerprint,
ServiceCheckFingerprint,
CastDynamiteModuleFingerprint,
mainActivityOnCreateFingerprint,
) + fingerprints,
requiresIntegrations = true,
@ -91,10 +97,19 @@ abstract class BaseGmsCoreSupportPatch(
}
// Specific method that needs to be patched.
transformPrimeMethod(packageName)
primeMethodFingerprint?.let { transformPrimeMethod(packageName) }
// Return these methods early to prevent the app from crashing.
earlyReturnFingerprints.toList().returnEarly()
earlyReturnFingerprints.returnEarly()
ServiceCheckFingerprint.returnEarly()
// Not all apps have CastDynamiteModule, so we need to check if it's present.
if (CastDynamiteModuleFingerprint.result != null) {
CastDynamiteModuleFingerprint.returnEarly()
}
// Google Play Utility is not present in all apps, so we need to check if it's present.
if (GooglePlayUtilityFingerprint.result != null) {
GooglePlayUtilityFingerprint.returnEarly()
}
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstructions(
@ -192,7 +207,7 @@ abstract class BaseGmsCoreSupportPatch(
}
private fun transformPrimeMethod(packageName: String) {
primeMethodFingerprint.result?.mutableMethod?.apply {
primeMethodFingerprint!!.result?.mutableMethod?.apply {
var register = 2
val index = getInstructions().indexOfFirst {
@ -305,6 +320,7 @@ abstract class BaseGmsCoreSupportPatch(
"com.google.android.gms.languageprofile.service.START",
"com.google.android.gms.clearcut.service.START",
"com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE",
"com.google.android.gms.accountsettings.action.VIEW_SETTINGS",
// potoken
"com.google.android.gms.potokens.service.START",

View file

@ -96,27 +96,23 @@ abstract class BaseGmsCoreSupportResourcePatch(
private fun ResourceContext.patchManifest() {
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName)
val manifest = this.get("AndroidManifest.xml").readText()
this.get("AndroidManifest.xml").writeText(
manifest.replace(
"package=\"$fromPackageName",
"package=\"$packageName",
).replace(
"android:authorities=\"$fromPackageName",
"android:authorities=\"$packageName",
).replace(
"$fromPackageName.permission.C2D_MESSAGE",
"$packageName.permission.C2D_MESSAGE",
).replace(
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
"$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
).replace(
"com.google.android.c2dm",
"$gmsCoreVendorGroupId.android.c2dm",
).replace(
"</queries>",
"<package android:name=\"$gmsCoreVendorGroupId.android.gms\"/></queries>",
),
val transformations = mapOf(
"package=\"$fromPackageName" to "package=\"$packageName",
"android:authorities=\"$fromPackageName" to "android:authorities=\"$packageName",
"$fromPackageName.permission.C2D_MESSAGE" to "$packageName.permission.C2D_MESSAGE",
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" to "$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
"com.google.android.c2dm" to "$packageName.android.c2dm",
"com.google.android.libraries.photos.api.mars" to "$packageName.android.apps.photos.api.mars",
"</queries>" to "<package android:name=\"$gmsCoreVendorGroupId.android.gms\"/></queries>",
)
get("AndroidManifest.xml", false).writeText(
transformations.entries.fold(get("AndroidManifest.xml", false).readText()) { acc, (from, to) ->
acc.replace(
from,
to
)
}
)
}

View file

@ -1,4 +1,4 @@
package app.revanced.patches.music.misc.gms.fingerprints
package app.revanced.patches.shared.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint

View file

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.misc.gms.fingerprints
package app.revanced.patches.shared.misc.gms.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
@ -8,5 +8,5 @@ internal object GooglePlayUtilityFingerprint : MethodFingerprint(
returnType = "I",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L", "I"),
strings = listOf("This should never happen.", "MetadataValueReader", "com.google.android.gms")
)
strings = listOf("This should never happen.", "MetadataValueReader", "com.google.android.gms"),
)

View file

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.misc.gms.fingerprints
package app.revanced.patches.shared.misc.gms.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
@ -8,5 +8,5 @@ internal object ServiceCheckFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L", "I"),
strings = listOf("Google Play Services not available", "GooglePlayServices not available due to error ")
)
strings = listOf("Google Play Services not available")
)

View file

@ -10,7 +10,7 @@ import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.soundcloud.ad.fingerprints.InterceptFingerprint
import app.revanced.patches.soundcloud.ad.fingerprints.FeatureConstructorFingerprint
import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint
import app.revanced.patches.soundcloud.ad.fingerprints.UserConsumerPlanConstructorFingerprint
import app.revanced.util.resultOrThrow

View file

@ -0,0 +1,83 @@
package app.revanced.patches.soundcloud.offlinesync
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.soundcloud.offlinesync.fingerprints.DownloadOperationsHeaderVerificationFingerprint
import app.revanced.patches.soundcloud.offlinesync.fingerprints.DownloadOperationsURLBuilderFingerprint
import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint
import app.revanced.util.getReference
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Patch(
name = "Enable offline sync",
compatiblePackages = [CompatiblePackage("com.soundcloud.android")],
)
@Suppress("unused")
object EnableOfflineSyncPatch : BytecodePatch(
setOf(
FeatureConstructorFingerprint, DownloadOperationsURLBuilderFingerprint,
DownloadOperationsHeaderVerificationFingerprint
),
) {
override fun execute(context: BytecodeContext) {
// Enable the feature to allow offline track syncing by modifying the JSON server response.
// This method is the constructor of a class representing a "Feature" object parsed from JSON data.
// p1 is the name of the feature.
// p2 is true if the feature is enabled, false otherwise.
FeatureConstructorFingerprint.resultOrThrow().mutableMethod.apply {
val afterCheckNotNullIndex = 2
addInstructionsWithLabels(
afterCheckNotNullIndex,
"""
const-string v0, "offline_sync"
invoke-virtual { p1, v0 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :skip
const/4 p2, 0x1
""",
ExternalLabel("skip", getInstruction(afterCheckNotNullIndex)),
)
}
// Patch the URL builder to use the HTTPS_STREAM endpoint
// instead of the offline sync endpoint to downloading the track.
DownloadOperationsURLBuilderFingerprint.resultOrThrow().mutableMethod.apply {
val getEndpointsEnumFieldIndex = 1
val getEndpointsEnumFieldInstruction = getInstruction<OneRegisterInstruction>(getEndpointsEnumFieldIndex)
val targetRegister = getEndpointsEnumFieldInstruction.registerA
val endpointsType = getEndpointsEnumFieldInstruction.getReference<FieldReference>()!!.type
replaceInstruction(
getEndpointsEnumFieldIndex,
"sget-object v$targetRegister, $endpointsType->HTTPS_STREAM:$endpointsType"
)
}
// The HTTPS_STREAM endpoint does not return the necessary headers for offline sync.
// Mock the headers to prevent the app from crashing by setting them to empty strings.
// The headers are all cosmetic and do not affect the functionality of the app.
DownloadOperationsHeaderVerificationFingerprint.resultOrThrow().mutableMethod.apply {
// The first three null checks need to be patched.
getInstructions().asSequence().filter {
it.opcode == Opcode.IF_EQZ
}.take(3).toList().map { it.location.index }.asReversed().forEach { nullCheckIndex ->
val headerStringRegister = getInstruction<OneRegisterInstruction>(nullCheckIndex).registerA
addInstruction(nullCheckIndex, "const-string v$headerStringRegister, \"\"")
}
}
}
}

View file

@ -0,0 +1,21 @@
package app.revanced.patches.soundcloud.offlinesync.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object DownloadOperationsHeaderVerificationFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L","L"),
opcodes = listOf(
Opcode.CONST_STRING,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_STRING
),
customFingerprint = { _, classDef ->
classDef.sourceFile == "DownloadOperations.kt"
}
)

View file

@ -0,0 +1,20 @@
package app.revanced.patches.soundcloud.offlinesync.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object DownloadOperationsURLBuilderFingerprint : MethodFingerprint(
returnType = "Ljava/lang/String",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L","L"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.SGET_OBJECT,
Opcode.FILLED_NEW_ARRAY
),
customFingerprint = { _, classDef ->
classDef.sourceFile == "DownloadOperations.kt"
}
)

View file

@ -1,4 +1,4 @@
package app.revanced.patches.soundcloud.ad.fingerprints
package app.revanced.patches.soundcloud.shared.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View file

@ -0,0 +1,37 @@
package app.revanced.patches.swissid.integritycheck
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.swissid.integritycheck.fingerprints.CheckIntegrityFingerprint
import app.revanced.util.resultOrThrow
@Patch(
name = "Remove Google Play Integrity Integrity check",
description = "Removes the Google Play Integrity check. With this it's possible to use SwissID on custom ROMS." +
"If the device is rooted, root permissions must be hidden from the app.",
compatiblePackages = [CompatiblePackage("com.swisssign.swissid.mobile")],
)
@Suppress("unused")
object RemoveGooglePlayIntegrityCheck : BytecodePatch(
setOf(CheckIntegrityFingerprint),
) {
private const val RESULT_METHOD_REFERENCE = " Lcom/swisssign/deviceintegrity/" +
"DeviceintegrityPlugin\$onMethodCall\$1;->\$result:" +
"Lio/flutter/plugin/common/MethodChannel\$Result;"
private const val SUCCESS_METHOD_REFERENCE =
"Lio/flutter/plugin/common/MethodChannel\$Result;->success(Ljava/lang/Object;)V"
override fun execute(context: BytecodeContext) =
CheckIntegrityFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"""
iget-object p1, p0, $RESULT_METHOD_REFERENCE
const-string v0, "VALID"
invoke-interface {p1, v0}, $SUCCESS_METHOD_REFERENCE
return-void
""",
)
}

View file

@ -0,0 +1,11 @@
package app.revanced.patches.swissid.integritycheck.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object CheckIntegrityFingerprint : MethodFingerprint(
returnType = "V",
parameters = listOf("Lcom/swisssign/deviceintegrity/model/DeviceIntegrityResult;"),
strings = listOf("it", "result")
)

View file

@ -97,6 +97,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
SwitchPreference("revanced_hide_attributes_section"),
SwitchPreference("revanced_hide_chapters_section"),
SwitchPreference("revanced_hide_info_cards_section"),
SwitchPreference("revanced_hide_key_concepts_section"),
SwitchPreference("revanced_hide_podcast_section"),
SwitchPreference("revanced_hide_transcript_section"),
),

View file

@ -1,36 +1,18 @@
package app.revanced.patches.youtube.layout.thumbnails
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.MessageDigestImageUrlFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.MessageDigestImageUrlParentFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.RequestFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnFailureFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnSucceededFingerprint
import app.revanced.patches.youtube.misc.imageurlhook.CronetImageUrlHook
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
@Patch(
name = "Alternative thumbnails",
@ -39,7 +21,8 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
NavigationBarHookPatch::class
NavigationBarHookPatch::class,
CronetImageUrlHook::class
],
compatiblePackages = [
CompatiblePackage(
@ -74,65 +57,10 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
],
)
@Suppress("unused")
object AlternativeThumbnailsPatch : BytecodePatch(
setOf(
MessageDigestImageUrlParentFingerprint,
OnResponseStartedFingerprint,
RequestFingerprint,
),
) {
object AlternativeThumbnailsPatch : BytecodePatch(emptySet()) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/AlternativeThumbnailsPatch;"
private lateinit var loadImageUrlMethod: MutableMethod
private var loadImageUrlIndex = 0
private lateinit var loadImageSuccessCallbackMethod: MutableMethod
private var loadImageSuccessCallbackIndex = 0
private lateinit var loadImageErrorCallbackMethod: MutableMethod
private var loadImageErrorCallbackIndex = 0
/**
* @param highPriority If the hook should be called before all other hooks.
*/
@Suppress("SameParameterValue")
private fun addImageUrlHook(targetMethodClass: String, highPriority: Boolean) {
loadImageUrlMethod.addInstructions(
if (highPriority) 0 else loadImageUrlIndex,
"""
invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String;
move-result-object p1
""",
)
loadImageUrlIndex += 2
}
/**
* If a connection completed, which includes normal 200 responses but also includes
* status 404 and other error like http responses.
*/
@Suppress("SameParameterValue")
private fun addImageUrlSuccessCallbackHook(targetMethodClass: String) {
loadImageSuccessCallbackMethod.addInstruction(
loadImageSuccessCallbackIndex++,
"invoke-static { p1, p2 }, $targetMethodClass->handleCronetSuccess(" +
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;)V",
)
}
/**
* If a connection outright failed to complete any connection.
*/
@Suppress("SameParameterValue")
private fun addImageUrlErrorCallbackHook(targetMethodClass: String) {
loadImageErrorCallbackMethod.addInstruction(
loadImageErrorCallbackIndex++,
"invoke-static { p1, p2, p3 }, $targetMethodClass->handleCronetFailure(" +
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V",
)
}
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@ -177,62 +105,8 @@ object AlternativeThumbnailsPatch : BytecodePatch(
ListPreference("revanced_alt_thumbnail_stills_time", summaryKey = null)
)
fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =
also { resolve(context, fingerprint.resultOrThrow().classDef) }.resultOrThrow()
fun MethodFingerprint.resolveAndLetMutableMethod(
fingerprint: MethodFingerprint,
block: (MutableMethod) -> Unit,
) = alsoResolve(fingerprint).also { block(it.mutableMethod) }
MessageDigestImageUrlFingerprint.resolveAndLetMutableMethod(MessageDigestImageUrlParentFingerprint) {
loadImageUrlMethod = it
addImageUrlHook(INTEGRATIONS_CLASS_DESCRIPTOR, true)
}
OnSucceededFingerprint.resolveAndLetMutableMethod(OnResponseStartedFingerprint) {
loadImageSuccessCallbackMethod = it
addImageUrlSuccessCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
}
OnFailureFingerprint.resolveAndLetMutableMethod(OnResponseStartedFingerprint) {
loadImageErrorCallbackMethod = it
addImageUrlErrorCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
}
// The URL is required for the failure callback hook, but the URL field is obfuscated.
// Add a helper get method that returns the URL field.
RequestFingerprint.resultOrThrow().apply {
// The url is the only string field that is set inside the constructor.
val urlFieldInstruction = mutableMethod.getInstructions().first {
if (it.opcode != Opcode.IPUT_OBJECT) return@first false
val reference = (it as ReferenceInstruction).reference as FieldReference
reference.type == "Ljava/lang/String;"
} as ReferenceInstruction
val urlFieldName = (urlFieldInstruction.reference as FieldReference).name
val definingClass = RequestFingerprint.IMPLEMENTATION_CLASS_NAME
val addedMethodName = "getHookedUrl"
mutableClass.methods.add(
ImmutableMethod(
definingClass,
addedMethodName,
emptyList(),
"Ljava/lang/String;",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
addInstructions(
"""
iget-object v0, p0, $definingClass->$urlFieldName:Ljava/lang/String;
return-object v0
""",
)
},
)
}
CronetImageUrlHook.addImageUrlHook(INTEGRATIONS_CLASS_DESCRIPTOR)
CronetImageUrlHook.addImageUrlSuccessCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
CronetImageUrlHook.addImageUrlErrorCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
}
}

View file

@ -0,0 +1,71 @@
package app.revanced.patches.youtube.layout.thumbnails
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.imageurlhook.CronetImageUrlHook
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
@Patch(
name = "Bypass image region restrictions",
description = "Adds an option to use a different host for user avatar and channel images," +
"and can fix missing images that are blocked in some countries.",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
CronetImageUrlHook::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
]
)
]
)
@Suppress("unused")
object BypassImageRegionRestrictions : BytecodePatch(emptySet()) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/BypassImageRegionRestrictionsPatch;"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.MISC.addPreferences(
SwitchPreference("revanced_bypass_image_region_restrictions")
)
// A priority hook is not needed, as the image urls of interest are not modified
// by AlternativeThumbnails or any other patch in this repo.
CronetImageUrlHook.addImageUrlHook(INTEGRATIONS_CLASS_DESCRIPTOR)
}
}

View file

@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
@ -15,7 +16,9 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackPatch
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.*
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
@ -37,16 +40,21 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
SettingsPatch::class,
AddResourcesPatch::class,
UserAgentClientSpoofPatch::class,
// Required since iOS livestream fix partially enables background playback.
BackgroundPlaybackPatch::class,
PlayerTypeHookPatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
// This patch works with these versions,
// but the dependent background playback patch does not.
// "18.37.36",
// "18.38.44",
// "18.43.45",
// "18.44.41",
// "18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
@ -77,18 +85,29 @@ object SpoofClientPatch : BytecodePatch(
SetPlayerRequestClientTypeFingerprint,
CreatePlayerRequestBodyFingerprint,
CreatePlayerRequestBodyWithModelFingerprint,
CreatePlayerRequestBodyWithVersionReleaseFingerprint,
// Player gesture config.
PlayerGestureConfigSyntheticFingerprint,
// Player speed menu item.
CreatePlaybackSpeedMenuItemFingerprint,
),
// Video qualities missing.
BuildRequestFingerprint,
// Livestream audio only background playback.
PlayerResponseModelBackgroundAudioPlaybackFingerprint,
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/spoof/SpoofClientPatch;"
private const val CLIENT_INFO_CLASS_DESCRIPTOR =
"Lcom/google/protos/youtube/api/innertube/InnertubeContext\$ClientInfo;"
private const val REQUEST_CLASS_DESCRIPTOR =
"Lorg/chromium/net/ExperimentalUrlRequest;"
private const val REQUEST_BUILDER_CLASS_DESCRIPTOR =
"Lorg/chromium/net/ExperimentalUrlRequest\$Builder;"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@ -153,7 +172,7 @@ object SpoofClientPatch : BytecodePatch(
.getInstructions().find { instruction ->
// requestMessage.clientInfo = clientInfoBuilder.build();
instruction.opcode == Opcode.IPUT_OBJECT &&
instruction.getReference<FieldReference>()?.type == CLIENT_INFO_CLASS_DESCRIPTOR
instruction.getReference<FieldReference>()?.type == CLIENT_INFO_CLASS_DESCRIPTOR
}?.getReference<FieldReference>() ?: throw PatchException("Could not find clientInfoField")
// Client info object's client type field.
@ -164,13 +183,15 @@ object SpoofClientPatch : BytecodePatch(
// Client info object's client version field.
val clientInfoClientVersionField = result.mutableMethod
.getInstruction(result.scanResult.stringsScanResult!!.matches.first().index + 1)
.getReference<FieldReference>() ?: throw PatchException("Could not find clientInfoClientVersionField")
.getReference<FieldReference>()
?: throw PatchException("Could not find clientInfoClientVersionField")
Triple(clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField)
}
val clientInfoClientModelField = CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().let {
val getClientModelIndex = CreatePlayerRequestBodyWithModelFingerprint.indexOfBuildModelInstruction(it.method)
val getClientModelIndex =
CreatePlayerRequestBodyWithModelFingerprint.indexOfBuildModelInstruction(it.method)
// The next IPUT_OBJECT instruction after getting the client model is setting the client model field.
val index = it.mutableMethod.indexOfFirstInstructionOrThrow(getClientModelIndex) {
@ -181,6 +202,19 @@ object SpoofClientPatch : BytecodePatch(
?: throw PatchException("Could not find clientInfoClientModelField")
}
val clientInfoOsVersionField = CreatePlayerRequestBodyWithVersionReleaseFingerprint.resultOrThrow().let {
val getOsVersionIndex =
CreatePlayerRequestBodyWithVersionReleaseFingerprint.indexOfBuildVersionReleaseInstruction(it.method)
// The next IPUT_OBJECT instruction after getting the client os version is setting the client os version field.
val index = it.mutableMethod.indexOfFirstInstructionOrThrow(getOsVersionIndex) {
opcode == Opcode.IPUT_OBJECT
}
it.mutableMethod.getInstruction(index).getReference<FieldReference>()
?: throw PatchException("Could not find clientInfoOsVersionField")
}
// endregion
// region Spoof client type for /player requests.
@ -198,7 +232,7 @@ object SpoofClientPatch : BytecodePatch(
addInstruction(
checkCastIndex + 1,
"invoke-static { v$requestMessageInstanceRegister }," +
" ${result.classDef.type}->$setClientInfoMethodName($clientInfoContainerClassName)V",
" ${result.classDef.type}->$setClientInfoMethodName($clientInfoContainerClassName)V",
)
}
@ -240,6 +274,12 @@ object SpoofClientPatch : BytecodePatch(
invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientVersion(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientInfoClientVersionField
# Set client os version to the spoofed value.
iget-object v1, v0, $clientInfoOsVersionField
invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getOsVersion(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientInfoOsVersionField
:disabled
return-void
@ -283,6 +323,23 @@ object SpoofClientPatch : BytecodePatch(
// endregion
// region Fix livestream audio only background play if spoofing to iOS.
// This force enables audio background playback.
PlayerResponseModelBackgroundAudioPlaybackFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"""
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideBackgroundAudioPlayback()Z
move-result v0
if-eqz v0, :do_not_override
return v0
:do_not_override
nop
"""
)
// endregion
// Fix playback speed menu item if spoofing to iOS.
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
@ -291,7 +348,8 @@ object SpoofClientPatch : BytecodePatch(
it.mutableMethod.apply {
// Find the conditional check if the playback speed menu item is not created.
val shouldCreateMenuIndex = indexOfFirstInstructionOrThrow(scanResult.endIndex) { opcode == Opcode.IF_EQZ }
val shouldCreateMenuIndex =
indexOfFirstInstructionOrThrow(scanResult.endIndex) { opcode == Opcode.IF_EQZ }
val shouldCreateMenuRegister = getInstruction<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
addInstructions(
@ -305,5 +363,28 @@ object SpoofClientPatch : BytecodePatch(
}
// endregion
// region Fix video qualities missing, if spoofing to iOS by overriding the user agent.
BuildRequestFingerprint.resultOrThrow().let { result ->
result.mutableMethod.apply {
val buildRequestIndex = getInstructions().lastIndex - 2
val requestBuilderRegister = getInstruction<FiveRegisterInstruction>(buildRequestIndex).registerC
val newRequestBuilderIndex = result.scanResult.patternScanResult!!.endIndex
val urlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
// Replace "requestBuilder.build(): Request" with "overrideUserAgent(requestBuilder, url): Request".
replaceInstruction(
buildRequestIndex,
"invoke-static { v$requestBuilderRegister, v$urlRegister }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->" +
"overrideUserAgent(${REQUEST_BUILDER_CLASS_DESCRIPTOR}Ljava/lang/String;)" +
REQUEST_CLASS_DESCRIPTOR
)
}
}
// endregion
}
}

View file

@ -0,0 +1,15 @@
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object BuildRequestFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
returnType = "Lorg/chromium/net/UrlRequest;",
opcodes = listOf(
Opcode.INVOKE_DIRECT,
Opcode.INVOKE_VIRTUAL
)
)

View file

@ -0,0 +1,31 @@
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyWithVersionReleaseFingerprint.indexOfBuildVersionReleaseInstruction
import app.revanced.util.containsWideLiteralInstructionValue
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal object CreatePlayerRequestBodyWithVersionReleaseFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf(),
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionValue(1073741824) &&
indexOfBuildVersionReleaseInstruction(methodDef) >= 0
},
) {
fun indexOfBuildVersionReleaseInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<FieldReference>()
reference?.definingClass == "Landroid/os/Build\$VERSION;" &&
reference.name == "RELEASE" &&
reference.type == "Ljava/lang/String;"
}
}

View file

@ -0,0 +1,25 @@
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object PlayerResponseModelBackgroundAudioPlaybackFingerprint : MethodFingerprint(
returnType = "Z",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
opcodes = listOf(
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.GOTO,
Opcode.RETURN,
null, // Opcode.CONST_4 or Opcode.MOVE
Opcode.RETURN,
)
)

View file

@ -7,7 +7,8 @@ import app.revanced.patches.youtube.misc.fix.playback.SpoofClientPatch
import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.youtube.misc.gms.fingerprints.*
import app.revanced.patches.youtube.misc.gms.fingerprints.CastDynamiteModuleV2Fingerprint
import app.revanced.patches.youtube.misc.gms.fingerprints.PrimeMethodFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint
@ -17,9 +18,6 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
toPackageName = REVANCED_YOUTUBE_PACKAGE_NAME,
primeMethodFingerprint = PrimeMethodFingerprint,
earlyReturnFingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
),
@ -34,11 +32,12 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
CompatiblePackage(
"com.google.android.youtube",
setOf(
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
// Patch supports these versions but ClientSpoof does not.
// "18.37.36",
// "18.38.44",
// "18.43.45",
// "18.44.41",
// "18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
@ -61,9 +60,6 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
),
),
fingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeMethodFingerprint,

View file

@ -1,8 +0,0 @@
package app.revanced.patches.youtube.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object CastDynamiteModuleFingerprint : MethodFingerprint(
strings = listOf("com.google.android.gms.cast.framework.internal.CastDynamiteModuleImpl")
)

View file

@ -0,0 +1,132 @@
package app.revanced.patches.youtube.misc.imageurlhook
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.MessageDigestImageUrlFingerprint
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.MessageDigestImageUrlParentFingerprint
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.RequestFingerprint
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnFailureFingerprint
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnSucceededFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.util.alsoResolve
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
@Patch(
description = "Hooks Cronet image urls",
dependencies = [
IntegrationsPatch::class
]
)
object CronetImageUrlHook : BytecodePatch(
setOf(
MessageDigestImageUrlParentFingerprint,
OnResponseStartedFingerprint,
RequestFingerprint
)
) {
private lateinit var loadImageUrlMethod: MutableMethod
private var loadImageUrlIndex = 0
private lateinit var loadImageSuccessCallbackMethod: MutableMethod
private var loadImageSuccessCallbackIndex = 0
private lateinit var loadImageErrorCallbackMethod: MutableMethod
private var loadImageErrorCallbackIndex = 0
/**
* @param highPriority If the hook should be called before all other hooks.
*/
fun addImageUrlHook(targetMethodClass: String, highPriority: Boolean = false) {
loadImageUrlMethod.addInstructions(
if (highPriority) 0 else loadImageUrlIndex,
"""
invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String;
move-result-object p1
""",
)
loadImageUrlIndex += 2
}
/**
* If a connection completed, which includes normal 200 responses but also includes
* status 404 and other error like http responses.
*/
fun addImageUrlSuccessCallbackHook(targetMethodClass: String) {
loadImageSuccessCallbackMethod.addInstruction(
loadImageSuccessCallbackIndex++,
"invoke-static { p1, p2 }, $targetMethodClass->handleCronetSuccess(" +
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;)V",
)
}
/**
* If a connection outright failed to complete any connection.
*/
fun addImageUrlErrorCallbackHook(targetMethodClass: String) {
loadImageErrorCallbackMethod.addInstruction(
loadImageErrorCallbackIndex++,
"invoke-static { p1, p2, p3 }, $targetMethodClass->handleCronetFailure(" +
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V",
)
}
override fun execute(context: BytecodeContext) {
loadImageUrlMethod = MessageDigestImageUrlFingerprint
.alsoResolve(context, MessageDigestImageUrlParentFingerprint).mutableMethod
loadImageSuccessCallbackMethod = OnSucceededFingerprint
.alsoResolve(context, OnResponseStartedFingerprint).mutableMethod
loadImageErrorCallbackMethod = OnFailureFingerprint
.alsoResolve(context, OnResponseStartedFingerprint).mutableMethod
// The URL is required for the failure callback hook, but the URL field is obfuscated.
// Add a helper get method that returns the URL field.
RequestFingerprint.resultOrThrow().apply {
// The url is the only string field that is set inside the constructor.
val urlFieldInstruction = mutableMethod.getInstructions().single {
if (it.opcode != Opcode.IPUT_OBJECT) return@single false
val reference = (it as ReferenceInstruction).reference as FieldReference
reference.type == "Ljava/lang/String;"
} as ReferenceInstruction
val urlFieldName = (urlFieldInstruction.reference as FieldReference).name
val definingClass = RequestFingerprint.IMPLEMENTATION_CLASS_NAME
val addedMethodName = "getHookedUrl"
mutableClass.methods.add(
ImmutableMethod(
definingClass,
addedMethodName,
emptyList(),
"Ljava/lang/String;",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
addInstructions(
"""
iget-object v0, p0, $definingClass->$urlFieldName:Ljava/lang/String;
return-object v0
""",
)
}
)
}
}
}

View file

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints
package app.revanced.patches.youtube.misc.imageurlhook.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View file

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints
package app.revanced.patches.youtube.misc.imageurlhook.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View file

@ -1,8 +1,8 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet
package app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.RequestFingerprint.IMPLEMENTATION_CLASS_NAME
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.RequestFingerprint.IMPLEMENTATION_CLASS_NAME
import com.android.tools.smali.dexlib2.AccessFlags
internal object RequestFingerprint : MethodFingerprint(

View file

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
package app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View file

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
package app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View file

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
package app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View file

@ -7,12 +7,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.video.information.fingerprints.*
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.alsoResolve
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
@ -45,9 +47,11 @@ object VideoInformationPatch : BytecodePatch(
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/VideoInformation;"
private const val INTEGRATIONS_PLAYER_INTERFACE = "Lapp/revanced/integrations/youtube/patches/VideoInformation${'$'}PlaybackController;"
private lateinit var playerInitMethod: MutableMethod
private var playerInitInsertIndex = 4
private var playerInitInsertIndex = -1
private var playerInitInsertRegister = -1
private lateinit var mdxInitMethod: MutableMethod
private var mdxInitInsertIndex = -1
@ -70,42 +74,43 @@ object VideoInformationPatch : BytecodePatch(
with(PlayerInitFingerprint.resultOrThrow()) {
playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
// hook the player controller for use through integrations
// find the location of the first invoke-direct call and extract the register storing the 'this' object reference.
val initThisIndex = playerInitMethod.indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
}
playerInitInsertRegister = playerInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
playerInitInsertIndex = initThisIndex + 1
// Hook the player controller for use through integrations.
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
// seek method
val seekFingerprintResultMethod =
SeekFingerprint.also { it.resolve(context, classDef) }.resultOrThrow().method
SeekFingerprint.alsoResolve(context, PlayerInitFingerprint).method
val seekRelativeFingerprintResultMethod =
SeekRelativeFingerprint.alsoResolve(context, PlayerInitFingerprint).method
// create helper method
val seekHelperMethod = generateSeekMethodHelper(seekFingerprintResultMethod)
// add the seekTo method to the class for the integrations to call
mutableClass.methods.add(seekHelperMethod)
// Create integrations interface methods.
addSeekInterfaceMethods(mutableClass, seekFingerprintResultMethod, seekRelativeFingerprintResultMethod)
}
with(MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow()) {
mdxInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
// find the location of the first invoke-direct call and extract the register storing the 'this' object reference
val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
}
mdxInitInsertRegister = mdxInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
mdxInitInsertIndex = initThisIndex + 1
// hook the MDX director for use through integrations
// Hook the MDX director for use through integrations.
onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx")
// MDX seek method
val mdxSeekFingerprintResultMethod =
MdxSeekFingerprint.apply { resolve(context, classDef) }.resultOrThrow().method
MdxSeekFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
val mdxSeekRelativeFingerprintResultMethod =
MdxSeekRelativeFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
// create helper method
val mdxSeekHelperMethod = generateSeekMethodHelper(mdxSeekFingerprintResultMethod)
// add the seekTo method to the class for the integrations to call
mutableClass.methods.add(mdxSeekHelperMethod)
addSeekInterfaceMethods(mutableClass, mdxSeekFingerprintResultMethod, mdxSeekRelativeFingerprintResultMethod)
}
with(CreateVideoPlayerSeekbarFingerprint.result!!) {
@ -173,33 +178,42 @@ object VideoInformationPatch : BytecodePatch(
userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
}
private fun generateSeekMethodHelper(seekMethod: Method): MutableMethod {
private fun addSeekInterfaceMethods(targetClass: MutableClass, seekToMethod: Method, seekToRelativeMethod: Method) {
// Add the interface and methods that integrations calls.
targetClass.interfaces.add(INTEGRATIONS_PLAYER_INTERFACE)
// create helper method
val generatedMethod = ImmutableMethod(
seekMethod.definingClass,
"seekTo",
listOf(ImmutableMethodParameter("J", null, "time")),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
null, null,
MutableMethodImplementation(4)
).toMutable()
arrayOf(
seekToMethod to "seekTo",
seekToRelativeMethod to "seekToRelative"
).forEach { (method, name) ->
// Add interface method.
// Get enum type for the seek helper method.
val seekSourceEnumType = method.parameterTypes[1].toString()
// get enum type for the seek helper method
val seekSourceEnumType = seekMethod.parameterTypes[1].toString()
val interfaceImplementation = ImmutableMethod(
targetClass.type,
name,
listOf(ImmutableMethodParameter("J", null, "time")),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
null, null,
MutableMethodImplementation(4)
).toMutable()
// insert helper method instructions
generatedMethod.addInstructions(
0,
"""
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
invoke-virtual { p0, p1, p2, v0 }, $seekMethod
move-result p1
return p1
"""
)
return generatedMethod
// Insert helper method instructions.
interfaceImplementation.addInstructions(
0,
"""
# first enum (field a) is SEEK_SOURCE_UNKNOWN
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
invoke-virtual { p0, p1, p2, v0 }, $method
move-result p1
return p1
"""
)
targetClass.methods.add(interfaceImplementation)
}
}
private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) =
@ -220,8 +234,8 @@ object VideoInformationPatch : BytecodePatch(
internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) =
playerInitMethod.insert(
playerInitInsertIndex++,
"v0",
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
"v$playerInitInsertRegister",
"$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
)
/**
@ -234,7 +248,7 @@ object VideoInformationPatch : BytecodePatch(
mdxInitMethod.insert(
mdxInitInsertIndex++,
"v$mdxInitInsertRegister",
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
"$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
)
/**

View file

@ -5,6 +5,9 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* Resolves using class found in [MdxPlayerDirectorSetVideoStageFingerprint].
*/
internal object MdxSeekFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",

View file

@ -0,0 +1,19 @@
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* Resolves using class found in [MdxPlayerDirectorSetVideoStageFingerprint].
*/
internal object MdxSeekRelativeFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf("J", "L"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE
)
)

View file

@ -3,6 +3,9 @@ package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
/**
* Resolves using class found in [PlayerInitFingerprint].
*/
internal object SeekFingerprint : MethodFingerprint(
strings = listOf("Attempting to seek during an ad")
)

View file

@ -0,0 +1,21 @@
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* Resolves using class found in [PlayerInitFingerprint].
*/
internal object SeekRelativeFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf("J", "L"),
opcodes = listOf(
Opcode.ADD_LONG_2ADDR,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.RETURN
)
)

View file

@ -114,7 +114,7 @@ fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementati
*
* @return the first literal instruction with the value, or throws [PatchException] if not found.
*/
fun Method.indexOfFirstWideLiteralInstructionValueOrThrow(literal: Long) : Int {
fun Method.indexOfFirstWideLiteralInstructionValueOrThrow(literal: Long): Int {
val index = indexOfFirstWideLiteralInstructionValue(literal)
if (index < 0) throw PatchException("Could not find literal value: $literal")
return index
@ -160,7 +160,7 @@ inline fun <reified T : Reference> Instruction.getReference() = (this as? Refere
// TODO: delete this on next major release, the overloaded method with an optional start index serves the same purposes.
// Method is deprecated, but annotation is commented out otherwise during compilation usage of the replacement is
// incorrectly flagged as deprecated.
//@Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)"))
// @Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)"))
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = indexOfFirstInstruction(0, predicate)
/**
@ -211,28 +211,47 @@ fun Method.findOpcodeIndicesReversed(opcode: Opcode): List<Int> {
}
/**
* Return the resolved methods of [MethodFingerprint]s early.
* Return the resolved method early.
*/
fun List<MethodFingerprint>.returnEarly(bool: Boolean = false) {
fun MethodFingerprint.returnEarly(bool: Boolean = false) {
val const = if (bool) "0x1" else "0x0"
this.forEach { fingerprint ->
fingerprint.result?.let { result ->
val stringInstructions = when (result.method.returnType.first()) {
'L' ->
"""
result?.let { result ->
val stringInstructions = when (result.method.returnType.first()) {
'L' ->
"""
const/4 v0, $const
return-object v0
"""
'V' -> "return-void"
'I', 'Z' ->
"""
'V' -> "return-void"
'I', 'Z' ->
"""
const/4 v0, $const
return v0
"""
else -> throw Exception("This case should never happen.")
}
else -> throw Exception("This case should never happen.")
}
result.mutableMethod.addInstructions(0, stringInstructions)
} ?: throw fingerprint.exception
}
result.mutableMethod.addInstructions(0, stringInstructions)
} ?: throw exception
}
/**
* Return the resolved methods early.
*/
fun Iterable<MethodFingerprint>.returnEarly(bool: Boolean = false) = forEach { fingerprint ->
fingerprint.returnEarly(bool)
}
/**
* Return the resolved methods early.
*/
@Deprecated("Use the Iterable version")
fun List<MethodFingerprint>.returnEarly(bool: Boolean = false) = forEach { fingerprint ->
fingerprint.returnEarly(bool)
}
/**
* Resolves this fingerprint using the classDef of a parent fingerprint.
*/
fun MethodFingerprint.alsoResolve(context: BytecodeContext, parentFingerprint: MethodFingerprint) =
also { resolve(context, parentFingerprint.resultOrThrow().classDef) }.resultOrThrow()

View file

@ -171,8 +171,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
@ -198,6 +196,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->

View file

@ -171,8 +171,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
@ -198,6 +196,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->

View file

@ -211,6 +211,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_info_cards_section_title">إخفاء قسم بطاقات المعلومات</string>
<string name="revanced_hide_info_cards_section_summary_on">تم إخفاء قسم بطاقات المعلومات</string>
<string name="revanced_hide_info_cards_section_summary_off">يتم عرض قسم بطاقات المعلومات</string>
<string name="revanced_hide_key_concepts_section_title">إخفاء قسم \'المفاهيم الأساسية\'</string>
<string name="revanced_hide_key_concepts_section_summary_on">تم إخفاء قسم \'المفاهيم الأساسية\'</string>
<string name="revanced_hide_key_concepts_section_summary_off">يتم عرض قسم \'المفاهيم الأساسية\'</string>
<string name="revanced_hide_transcript_section_title">إخفاء قسم النص</string>
<string name="revanced_hide_transcript_section_summary_on">تم إخفاء قسم النص</string>
<string name="revanced_hide_transcript_section_summary_off">يتم عرض قسم النص</string>
@ -246,7 +249,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">الكلمة المفتاحية غير صالحة. لا يمكن استخدام: \'%s\' كعامل تصفية</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">الكلمة المفتاحية غير صالحة. \'%1$s\' أقل من %2$d حرفًا</string>
<string name="revanced_hide_keyword_toast_invalid_broad">الكلمة الرئيسية \'$s\' سوف تخفي جميع الفيديوهات</string>
<string name="revanced_hide_keyword_toast_invalid_broad">الكلمة الرئيسية \'%s\' سوف تخفي جميع الفيديوهات</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">إخفاء الإعلانات العامة</string>
@ -864,9 +867,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">اختيار فئة المقطع</string>
<string name="revanced_sb_new_segment_disabled_category">الفئة معطلة في الإعدادات. تمكين الفئة للإرسال.</string>
<string name="revanced_sb_new_segment_title">مقطع مانِع رُعَاة جديد</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">تعيين %1$02d:%2$02d:%3$03d كبداية أم نهاية لمقطع جديد؟</string>
<string name="revanced_sb_new_segment_mark_time_as_question">تعيين %s كبداية أو نهاية لمقطع جديد؟</string>
<string name="revanced_sb_new_segment_mark_start">البداية</string>
<string name="revanced_sb_new_segment_mark_end">النهاية</string>
<string name="revanced_sb_new_segment_now">الآن</string>
@ -990,6 +991,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">لون شريط التقدم</string>
<string name="revanced_seekbar_custom_color_invalid">لون الشريط غير صالح. استخدام القيمة الافتراضية.</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
<string name="revanced_bypass_image_region_restrictions_title">تجاوز قيود منطقة الصورة</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">استخدام مضيف الصورة yt4.ggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">استخدام مضيف الصور الأصلي\n\nتمكين هذا يمكن إصلاح الصور المفقودة التي يتم حظرها في بعض المناطق</string>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">علامة تبويب الصفحة الرئيسية</string>
@ -1112,8 +1118,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">لا يتم محاكاة العميل\n\nقد لا يعمل تشغيل الفيديو</string>
<string name="revanced_spoof_client_user_dialog_message">إيقاف تشغيل هذا الإعداد قد يسبب مشاكل في تشغيل الفيديو.</string>
<string name="revanced_spoof_client_use_ios_title">Spoof Client to iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">تتم حاليًا محاكاة العميل إلى iOS\n\nالآثار الجانبية تشمل:\n• لا يوجد فيديو HDR\n• قد لا يعمل سجل المشاهدة\n• قد تكون جودة الفيديو الأعلى مفقودة\n• لا يمكن تشغيل البث المباشر كصوت فقط\n• البث المباشر غير متوفر على Android 8.0</string>
<string name="revanced_spoof_client_use_ios_summary_off">تتم محاكاة العميل حاليًا إلى Android VR\n\nالآثار الجانبية تشمل:\n• لا يوجد فيديو HDR\n• فيديوهات الأطفال لا يتم تشغيلهم\n• مقاطع الفيديو المتوقفة يمكن أن تستأنف عشوائيا\n• جودة منخفضة لمصغرات شريط التقدم\n• زر التنزيل مخفي بشكل دائم\n• بطاقات نهاية الشاشة مخفية بشكل دائم</string>
<string name="revanced_spoof_client_use_ios_summary_on">تتم حاليًا محاكاة العميل إلـى iOS\n\nالآثار الجانبية تشمل:\n• قد لا يكون فيديو HDR متوفرًا\n• قد لا يعمل سجل المشاهدة</string>
<string name="revanced_spoof_client_use_ios_summary_off">تتم حاليًا محاكاة العميل إلـى Android VR\n\nالآثار الجانبية تشمل:\n• لا يوجد فيديو HDR\n• فيديوهات الأطفال لا يتم تشغيلهم\n• مقاطع الفيديو الموقوفة يمكن أن تستأنف عشوائياً\n• جودة منخفضة لمصغرات شريط التقدم\n• زر التنزيل مخفي\n• بطاقات نهاية الشاشة مخفية</string>
<string name="revanced_spoof_client_storyboard_timeout">محاكاة مصغرات العميل غير متوفرة (انتهت مهلة API)</string>
<string name="revanced_spoof_client_storyboard_io_exception">محاكاة مصغرات العميل غير متوفرة مؤقتًا: %s</string>
</patch>

View file

@ -171,8 +171,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
@ -198,6 +196,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->

File diff suppressed because it is too large Load diff

View file

@ -246,7 +246,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">Няправільнае ключавое слова. Немагчыма выкарыстоўваць: \"%s\" у якасці фільтра</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">Няправільнае ключавое слова. \"%1$s\" змяшчае менш за %2$d сімвалаў</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Ключавое слова \"$s\" схавае ўсе відэа</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Ключавое слова \"%s\" схавае ўсе відэа</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Схаваць агульную рэкламу</string>
@ -864,9 +864,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">Выберыце катэгорыю сегмента</string>
<string name="revanced_sb_new_segment_disabled_category">Катэгорыя адключана ў наладах. Уключыце катэгорыю для адпраўкі.</string>
<string name="revanced_sb_new_segment_title">Новы сегмент SponsorBlock</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">Усталяваць %1$02d:%2$02d:%3$03d у якасці пачатку або канца новага сегмента?</string>
<string name="revanced_sb_new_segment_mark_start">пачаць</string>
<string name="revanced_sb_new_segment_mark_end">канец</string>
<string name="revanced_sb_new_segment_now">зараз</string>
@ -990,6 +987,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">Колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_invalid">Няправільнае значэнне колеру панэлі пошуку. Выкарыстоўваецца значэнне па змаўчанні.</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Галоўная ўкладка</string>
@ -1112,8 +1111,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">Кліент не падроблены\n\nПрайграванне відэа можа не працаваць</string>
<string name="revanced_spoof_client_user_dialog_message">Адключэнне гэтай налады можа выклікаць праблемы з прайграваннем відэа.</string>
<string name="revanced_spoof_client_use_ios_title">Падробка кліента для iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">Кліент зараз падроблены на iOS\n\nПабочныя эфекты ўключаюць:\n• Няма HDR-відэа\n• Гісторыя праглядаў можа не працаваць\n• Можа адсутнічаць больш высокая якасць відэа\n• Жывыя трансляцыі не могуць прайгравацца толькі як аўдыя\n• Жывая трансляцыя патокі недаступныя на Android 8.0</string>
<string name="revanced_spoof_client_use_ios_summary_off">Кліент у цяперашні час падроблены для Android VR\n\nПабочныя эфекты ўключаюць:\n• Няма HDR-відэа\n• Дзіцячыя відэа не прайграваюцца\n• Прыпыненыя відэа могуць аднаўляцца выпадковым чынам\n• Нізкая якасць мініяцюр на панэлі пошуку Shorts\n• Кнопка дзеяння Спампаваць заўсёды схавана\n• Карткі канцавога экрана заўсёды схаваны</string>
<string name="revanced_spoof_client_storyboard_timeout">Мініяцюры кліента Spoof недаступныя (час чакання API скончыўся)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Мініяцюры кліента Spoof часова недаступныя: %s</string>
</patch>
@ -1162,6 +1159,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_twitch_debug_mode_summary_off">Рэжым адладкі Twitch адключаны</string>
</patch>
<patch id="misc.settings.SettingsPatch">
<string name="revanced_settings">Налады ReVanced</string>
<string name="revanced_ads_screen_title">Аб\"явы</string>
<string name="revanced_ads_screen_summary">Налады блакіроўкі рэкламы</string>
<string name="revanced_chat_screen_title">Чат</string>

View file

@ -423,8 +423,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_vote_upvote">Положителен вот</string>
<string name="revanced_sb_vote_downvote">Отрицателен вот</string>
<string name="revanced_sb_vote_category">Промяна на категорията</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_start">начало</string>
<string name="revanced_sb_new_segment_mark_end">край</string>
<string name="revanced_sb_new_segment_now">сега</string>
@ -463,6 +461,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->

View file

@ -792,9 +792,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">সেগমেন্টের বিভাগ নির্বাচন করুন</string>
<string name="revanced_sb_new_segment_disabled_category">সেটিং থেকে বিভাগ নিস্ক্রিয় করা হয়েছে। জমা দিতে বিভাগ সক্রিয় করুন।</string>
<string name="revanced_sb_new_segment_title">নতুন স্পন্সরব্লক সেগমেন্ট</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">একটি নতুন সেগমেন্টের শুরু বা শেষ হিসাবে %1$02d:%2$02d:%3$03d সেট করবেন?</string>
<string name="revanced_sb_new_segment_mark_start">শুরু</string>
<string name="revanced_sb_new_segment_mark_end">শেষ</string>
<string name="revanced_sb_new_segment_now">এখন</string>
@ -850,10 +847,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_app_version_target_title">স্পুফ অ্যাপ সংস্করণ লক্ষ্য</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_entry_1">18.33.40 - ছদ্মবেশি মোডে RYD পুনরুদ্ধার করে</string>
<string name="revanced_spoof_app_version_target_entry_2">১৮.২০.৩৯ - প্রশ্বস্ত ভিডিও স্পিড এবং গুণমান মেনু পুনরুদ্ধার করে</string>
<string name="revanced_spoof_app_version_target_entry_3">১৮.০৯.৩৯ - লাইব্রেরি ট্যাপ পুনরুদ্ধার করে</string>
<string name="revanced_spoof_app_version_target_entry_4">১৭.৪১.৩৯ - পুরোনো প্লেলিস্ট শেলফ পুনরুদ্ধার করে</string>
<string name="revanced_spoof_app_version_target_entry_5">১৭.৩৩.৪২ - পুরোনো UI লেআউট পুনরুদ্ধার করে</string>
<string name="revanced_spoof_app_version_target_entry_2">18.20.39 - প্রশ্বস্ত ভিডিও স্পিড এবং গুণমান মেনু পুনরুদ্ধার করে</string>
<string name="revanced_spoof_app_version_target_entry_3">18.09.39 - লাইব্রেরি ট্যাপ পুনরুদ্ধার করে</string>
<string name="revanced_spoof_app_version_target_entry_4">17.41.37 - পুরোনো প্লেলিস্ট শেলফ পুনরুদ্ধার করে</string>
<string name="revanced_spoof_app_version_target_entry_5">17.33.42 - পুরোনো UI লেআউট পুনরুদ্ধার করে</string>
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<string name="revanced_start_page_title">শুরুর পৃষ্ঠা সেট করুন</string>
@ -919,6 +916,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">সিকবারের রং</string>
<string name="revanced_seekbar_custom_color_invalid">সিকবারে ভুল রং দেয়া হয়েছে। মূল রং ব্যবহার করছে।</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">হোম ট্যাব</string>
@ -1041,8 +1040,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">ক্লায়েন্ট স্পুফ করা হয়নি\n\nভিডিও প্লেব্যাক ঠিকমতো কাজ নাও করতে পারে</string>
<string name="revanced_spoof_client_user_dialog_message">এই সেটিংটি বন্ধ করার ফলে ভিডিও প্লেব্যাক ত্রুটি হতে পারে।</string>
<string name="revanced_spoof_client_use_ios_title">ক্লায়েন্ট iOS এ স্পুফ করুন</string>
<string name="revanced_spoof_client_use_ios_summary_on">ক্লায়েন্ট এখন iOS এ স্পুফ করা হয়েছে\n\nপার্শ্বপ্রতিক্রিয়া রয়েছে:\n• কোন এইচিডিআর ভিডিও নেই\n• ভিডিও ইতিহাস কাজ নাও করতে পারে\n• উচ্চ ভিডিও গুণমান অনুপস্থিত থাকতে পারে\n•শুধুমাত্র অডিওতে লাইভ স্ট্রিম চলবে না\n• অ্যান্ড্রয়েট ৮ সংস্করণে লাইভ স্ট্রিম উপলভ্য নয়</string>
<string name="revanced_spoof_client_use_ios_summary_off">ক্লায়েন্টকে বর্তমানে Android VR-এর সাথে প্রতারণা করা হয়েছে\n\nসাইড এফেক্টগুলির মধ্যে রয়েছে:\n• কোনও HDR ভিডিও নেই\n• বাচ্চাদের ভিডিও প্লেব্যাক হয় না\n• বিরতি দেওয়া ভিডিওগুলি এলোমেলোভাবে আবার শুরু হতে পারে\n• নিম্নমানের শর্টস সিকবার থাম্বনেল\n• ডাউনলোড অ্যাকশন বোতাম সবসময় লুকানো থাকে\n• শেষ স্ক্রিন কার্ড সবসময় লুকানো থাকে</string>
<string name="revanced_spoof_client_storyboard_timeout">ক্লায়েন্ট স্পুফ থাম্বনেইল সাময়িকভাবে উপলভ্য নয় (API সময় শেষ হয়েছে)</string>
<string name="revanced_spoof_client_storyboard_io_exception">স্পুফ ক্লায়েন্ট থাম্বনেইল সাময়িকভাবে উপলভ্য নয়: %s</string>
</patch>

View file

@ -171,8 +171,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
@ -198,6 +196,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->

View file

@ -175,8 +175,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_appearance_category">Aparença</string>
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
@ -206,6 +204,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->

View file

@ -214,6 +214,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_info_cards_section_title">Skrýt sekci informačních karet</string>
<string name="revanced_hide_info_cards_section_summary_on">Sekce Info karty je skrytá</string>
<string name="revanced_hide_info_cards_section_summary_off">Je zobrazena sekce Info karty</string>
<string name="revanced_hide_key_concepts_section_title">Skrýt sekci \'Klíčové koncepty\'</string>
<string name="revanced_hide_key_concepts_section_summary_on">Sekce \'Klíčové pojmy\' je skryta</string>
<string name="revanced_hide_key_concepts_section_summary_off">Zobrazí se sekce \'Klíčové pojmy\'</string>
<string name="revanced_hide_transcript_section_title">Skrýt sekci přepisu</string>
<string name="revanced_hide_transcript_section_summary_on">Přepsat oddíl je skrytý</string>
<string name="revanced_hide_transcript_section_summary_off">Je zobrazena sekce přepisu</string>
@ -249,7 +252,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">Neplatné klíčové slovo. Nelze použít: \'%s\' jako filtr</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">Neplatné klíčové slovo. \'%1$s\' je menší než %2$d znaků</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Klíčové slovo \'$s\' skryje všechna videa</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Klíčové slovo \'%s\' skryje všechna videa</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Skrýt obecné reklamy</string>
@ -867,9 +870,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">Vyberte kategorii segmentu</string>
<string name="revanced_sb_new_segment_disabled_category">Kategorie je v nastavení zakázána. Povolte odeslat kategorii.</string>
<string name="revanced_sb_new_segment_title">Nový segment SponsorBlock</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">Nastavit %1$02d:%2$02d:%3$03d jako začátek nebo konec nového segmentu?</string>
<string name="revanced_sb_new_segment_mark_time_as_question">Nastavit %s jako začátek nebo konec nového segmentu?</string>
<string name="revanced_sb_new_segment_mark_start">Začít</string>
<string name="revanced_sb_new_segment_mark_end">konec</string>
<string name="revanced_sb_new_segment_now">nyní</string>
@ -994,6 +995,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">Barva vyhledávacího panelu</string>
<string name="revanced_seekbar_custom_color_invalid">Neplatná hodnota barvy vyhledávacího panelu. Použít výchozí hodnotu.</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
<string name="revanced_bypass_image_region_restrictions_title">Obejít omezení oblasti obrázku</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Použití hostitele obrázků yt4.gggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">Použití originálního hostitele obrázků\n\nPovolení tohoto nastavení může opravit chybějící obrázky, které jsou blokovány v některých regionech</string>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Záložka Domů</string>
@ -1116,8 +1122,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">Klient není spoofed\n\nPřehrávání videa nemusí fungovat</string>
<string name="revanced_spoof_client_user_dialog_message">Vypnutí tohoto nastavení může způsobit problémy při přehrávání videa.</string>
<string name="revanced_spoof_client_use_ios_title">Spořič do iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">Klient je v současné době spokojen s iOS\n\nBoční efekty zahrnují:\n• No HDR video\n• Historie sledování nemusí fungovat\n• Vyšší vlastnosti videa mohou chybět\n• Živé streamy nemohou přehrát pouze\n• Živé streamy nejsou k dispozici na Android 8.0</string>
<string name="revanced_spoof_client_use_ios_summary_off">Klient je v současné době spout s Androidem VR\n\nBoční efekty zahrnují:\n• No HDR video\n• Dětská videa nepřehrávají\n• Pozastavená videa mohou náhodně pokračovat\n• Nízká kvalita krátkých náhledů ve vyhledávacím panelu\n• Tlačítko stahování akce je vždy skryté\n• Karty s ukončenou obrazovkou jsou vždy skryté</string>
<string name="revanced_spoof_client_use_ios_summary_on">Klient je v současné době spokojen s iOS\n\nBoční efekty zahrnují:\n• HDR video nemusí být k dispozici\n• Historie sledování nemusí fungovat</string>
<string name="revanced_spoof_client_use_ios_summary_off">Klient je v současné době spout s Androidem VR. Boční efekty zahrnují:\n• No HDR video\n• Dětská videa nepřehrávají\n• Pozastavená videa mohou náhodně pokračovat\n• Nízká kvalita krátkých náhledů ve vyhledávacím panelu\n• Tlačítko pro stažení akce je skryté\n• Karty s ukončenou obrazovkou jsou skryté</string>
<string name="revanced_spoof_client_storyboard_timeout">Náhledy klientů nejsou k dispozici (vypršel časový limit API)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Spouštění náhledů klientů dočasně není k dispozici: %s</string>
</patch>

View file

@ -213,6 +213,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_info_cards_section_title">Skjul infokort sektion</string>
<string name="revanced_hide_info_cards_section_summary_on">Info-kort sektion er skjult</string>
<string name="revanced_hide_info_cards_section_summary_off">Info-kort sektion er vist</string>
<string name="revanced_hide_key_concepts_section_title">Skjul sektion \'Nøglebegreber\'</string>
<string name="revanced_hide_key_concepts_section_summary_on">\'Nøglebegreber\' sektion er skjult</string>
<string name="revanced_hide_key_concepts_section_summary_off">\'Nøglebegreber\' afsnit vises</string>
<string name="revanced_hide_transcript_section_title">Skjul udskriftsafsnittet</string>
<string name="revanced_hide_transcript_section_summary_on">Afsnittet er skjult</string>
<string name="revanced_hide_transcript_section_summary_off">Afsnittet er vist</string>
@ -248,7 +251,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">Ugyldigt søgeord. Kan ikke bruge: \'%s\' som et filter</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">Ugyldigt søgeord. \'%1$s\' er mindre end %2$d tegn</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Nøgleord \'$s\' vil skjule alle videoer</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Nøgleord \'%s\' vil skjule alle videoer</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Skjul generelle annoncer</string>
@ -579,11 +582,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">Skjul Shorts i hjemmefeed</string>
<string name="revanced_hide_shorts_home_summary_on">Shorts i hjemmet feed er skjult</string>
<string name="revanced_hide_shorts_home_summary_off">Korte i hjemmet feed er vist</string>
<string name="revanced_hide_shorts_home_summary_off">Shorts i hjemmet feed er vist</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">Skjul Shorts i abonnementsfeed</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Korte i abonnementsfeed er skjult</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Korte i abonnementsfeed vises</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Shorts i abonnementsfeed er skjult</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Shorts i abonnementsfeed vises</string>
<string name="revanced_hide_shorts_search_title">Skjul Shorts i søgeresultater</string>
<string name="revanced_hide_shorts_search_summary_on">Shorts i søgeresultater er skjult</string>
<string name="revanced_hide_shorts_search_summary_off">Korte i søgeresultater vises</string>
@ -865,9 +868,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">Vælg segmentkategori</string>
<string name="revanced_sb_new_segment_disabled_category">Kategori er deaktiveret i indstillinger. Aktivér kategori for at indsende.</string>
<string name="revanced_sb_new_segment_title">Nyt SponsorBlock segment</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">Sæt %1$02d:%2$02d:%3$03d som start eller afslutning af et nyt segment?</string>
<string name="revanced_sb_new_segment_mark_time_as_question">Sæt %s som start eller afslutning af et nyt segment?</string>
<string name="revanced_sb_new_segment_mark_start">start</string>
<string name="revanced_sb_new_segment_mark_end">slut</string>
<string name="revanced_sb_new_segment_now">nu</string>
@ -991,6 +992,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">Farven på den søgende bar</string>
<string name="revanced_seekbar_custom_color_invalid">Ugyldig søgelinje farveværdi. Brug standardværdi.</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
<string name="revanced_bypass_image_region_restrictions_title">Bypass billede region restriktioner</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Bruger billedvært yt4.ggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">Brug af original billedvært\n\nAktivering af dette kan rette manglende billeder, der er blokeret i nogle regioner</string>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Hjem fane</string>
@ -1113,8 +1119,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">Klienten er ikke forfalsket\n\nVideoafspilning fungerer muligvis ikke</string>
<string name="revanced_spoof_client_user_dialog_message">At slå denne indstilling fra kan forårsage problemer med videoafspilning.</string>
<string name="revanced_spoof_client_use_ios_title">Spoof klient til iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">Klienten er i øjeblikket spoofed til iOS\n\nBivirkninger omfatter:\n• Ingen HDR-video\n• Se historie fungerer muligvis ikke\n• Højere video kvaliteter mangler muligvis\n• Live streams kan ikke afspilles som lyd kun\n• Live streams ikke tilgængelig på Android 8.0</string>
<string name="revanced_spoof_client_use_ios_summary_off">Klienten er i øjeblikket spoofed til Android VR\n\nBivirkninger omfatter:\n• Ingen HDR-video\n• Kids videoer ikke afspilning\n• Pause videoer kan tilfældigt genoptage\n• Lav kvalitet Shorts søgelinje miniaturer\n• Download handling knap er altid skjult\n• End screen cards are always hidden</string>
<string name="revanced_spoof_client_use_ios_summary_on">Klienten er i øjeblikket spoofed til iOS\n\nBivirkninger omfatter:\n• HDR-video er muligvis ikke tilgængelig\n• Se historie fungerer måske ikke</string>
<string name="revanced_spoof_client_use_ios_summary_off">Klienten er i øjeblikket spoofed til Android VR. Bivirkninger omfatter:\n• Ingen HDR-video\n• Kids videoer ikke afspilning\n• Pause videoer kan tilfældigt genoptage\n• Lav kvalitet Shorts søgelinje miniaturer\n• Download handling knap er skjult\n• End screen cards are hidden</string>
<string name="revanced_spoof_client_storyboard_timeout">Spoof klient miniaturer ikke tilgængelige (API timemed out)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Spoof klient miniaturer midlertidigt ikke tilgængelige: %s</string>
</patch>

View file

@ -214,6 +214,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_info_cards_section_title">Info-Kartenbereich ausblenden</string>
<string name="revanced_hide_info_cards_section_summary_on">Info-Kartenbereich ist ausgeblendet</string>
<string name="revanced_hide_info_cards_section_summary_off">Info-Kartenbereich wird angezeigt</string>
<string name="revanced_hide_key_concepts_section_title">\'Schlüsselbegriffe\' ausblenden</string>
<string name="revanced_hide_key_concepts_section_summary_on">\'Schlüsselbegriffe\' Abschnitt ist versteckt</string>
<string name="revanced_hide_key_concepts_section_summary_off">Abschnitt \'Schlüsselkonzepte\' wird angezeigt</string>
<string name="revanced_hide_transcript_section_title">Transkript-Abschnitt ausblenden</string>
<string name="revanced_hide_transcript_section_summary_on">Transkript-Abschnitt ist ausgeblendet</string>
<string name="revanced_hide_transcript_section_summary_off">Sektion Transkripte wird angezeigt</string>
@ -249,7 +252,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">Ungültiges Schlüsselwort. Kann nicht verwenden: \'%s\' als Filter</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">Ungültiges Schlüsselwort. \'%1$s\' ist weniger als %2$d Zeichen</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Stichwort \'$s\' wird alle Videos ausblenden</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Schlüsselwort \'%s\' wird alle Videos ausblenden</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Allgemeine Werbung ausblenden</string>
@ -680,6 +683,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Nicht anzeigen</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<string name="revanced_ryd_failure_connection_timeout">Dislikes vorläufig nicht verfügbar (API Timeout)</string>
<string name="revanced_ryd_failure_connection_status_code">Dislikes nicht verfügbar (Status %d)</string>
<string name="revanced_ryd_failure_client_rate_limit_requested">Nicht verfügbar (Client-API-Limit erreicht)</string>
<string name="revanced_ryd_failure_generic">Dislikes nicht verfügbar (%s)</string>
@ -864,9 +868,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">Wählen sie die Segmentkategorie</string>
<string name="revanced_sb_new_segment_disabled_category">Kategorie ist in den Einstellungen deaktiviert. Aktivieren Sie die Kategorie zum Abschicken.</string>
<string name="revanced_sb_new_segment_title">Neues SponsorBlock Segment</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">%1$02d:%2$02d:%3$03d als Start oder Ende eines neuen Segments festlegen?</string>
<string name="revanced_sb_new_segment_mark_time_as_question">%s als Start oder Ende eines neuen Segments festlegen?</string>
<string name="revanced_sb_new_segment_mark_start">Start</string>
<string name="revanced_sb_new_segment_mark_end">Ende</string>
<string name="revanced_sb_new_segment_now">Jetzt</string>
@ -991,6 +993,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">Die Farbe der Suchleiste</string>
<string name="revanced_seekbar_custom_color_invalid">Ungültiger Suchleisten-Farbwert. Standardwert wird verwendet.</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
<string name="revanced_bypass_image_region_restrictions_title">Bildgebietsbeschränkungen umgehen</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Bild-Host yt4.ggpht.com verwenden</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">Verwendung des ursprünglichen Bild-Hosts\n\nAktivieren kann fehlende Bilder beheben, die in einigen Regionen blockiert werden</string>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Home-Tab</string>
@ -1113,8 +1120,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">Client ist nicht gefälscht\n\nVideo-Wiedergabe funktioniert möglicherweise nicht</string>
<string name="revanced_spoof_client_user_dialog_message">Das Deaktivieren dieser Einstellung kann zu Videowiedergabeproblemen führen.</string>
<string name="revanced_spoof_client_use_ios_title">Spoof-Client zu iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">Der Client ist derzeit auf iOS\n\nseitliche Effekte gefälscht:\n• Kein HDR-Video\n• Verlauf kann nicht funktionieren\n• Höhere Video-Qualitäten fehlen möglicherweise\n• Live-Streams können nicht nur als Audio abspielen\n• Live-Streams auf Android 8.0 nicht verfügbar</string>
<string name="revanced_spoof_client_use_ios_summary_off">Der Client ist derzeit auf Android VR\n\nSeiteneffekte gefälscht:\n• Kein HDR Video\n• Kindervideos spielen nicht\n• Pausierte Videos können zufällig wieder aufgenommen werden\n• Niedrige Shorts Suchleisten-Thumbnails\n• Download-Button ist immer versteckt\n• Endbildschirm-Karten werden immer versteckt</string>
<string name="revanced_spoof_client_use_ios_summary_on">Der Client ist derzeit zu iOS\n\nSeiteneffekten gefälscht:\n• HDR-Video ist möglicherweise nicht verfügbar\n• Verlauf kann nicht funktionieren</string>
<string name="revanced_spoof_client_use_ios_summary_off">Der Client ist derzeit auf Android VR. Seiteneffekte gefälscht:\n• Kein HDR Video\n• Kindervideos spielen nicht\n• Pausierte Videos können zufällig wieder aufgenommen werden\n• Niedrige Shorts Suchleisten-Thumbnails\n• Download-Button ist ausgeblendet\n• Endbildschirm-Karten werden versteckt</string>
<string name="revanced_spoof_client_storyboard_timeout">Spoof-Thumbnails nicht verfügbar (API-Zeitüberschreitung)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Spoof-Client-Thumbnails vorübergehend nicht verfügbar: %s</string>
</patch>

View file

@ -212,6 +212,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_info_cards_section_title">Ενότητα καρτών πληροφοριών</string>
<string name="revanced_hide_info_cards_section_summary_on">Κρυμμένη</string>
<string name="revanced_hide_info_cards_section_summary_off">Εμφανίζεται</string>
<string name="revanced_hide_key_concepts_section_title">Ενότητα σχετιζόμενων εννοιών</string>
<string name="revanced_hide_key_concepts_section_summary_on">Κρυμμένη</string>
<string name="revanced_hide_key_concepts_section_summary_off">Εμφανίζεται</string>
<string name="revanced_hide_transcript_section_title">Ενότητα Απομαγνητοφώνησης</string>
<string name="revanced_hide_transcript_section_summary_on">Κρυμμένη</string>
<string name="revanced_hide_transcript_section_summary_off">Εμφανίζεται</string>
@ -247,7 +250,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">Μη έγκυρη λέξη-κλειδί. Αδυναμία χρήσης: \'%s\' ως φίλτρο</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">Μη έγκυρη λέξη-κλειδί. Το \'%1$s\' είναι μικρότερο από %2$d χαρακτήρες</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Η λέξη «$s» θα κρύψει όλα τα βίντεο</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Η λέξη «%s» θα κρύψει όλα τα βίντεο</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Γενικές διαφημίσεις</string>
@ -865,9 +868,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">Επιλέξτε την κατηγορία του τμήματος</string>
<string name="revanced_sb_new_segment_disabled_category">Η κατηγορία είναι απενεργοποιημένη στις ρυθμίσεις. Ενεργοποιήστε την κατηγορία για υποβολή.</string>
<string name="revanced_sb_new_segment_title">Νέο τμήμα SponsorBlock</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">Ορισμός %1$02d:%2$02d:%3$03d ως αρχή ή τέλος ενός νέου τμήματος;</string>
<string name="revanced_sb_new_segment_mark_time_as_question">Ορισμός %s ως αρχή ή τέλος ενός νέου τμήματος;</string>
<string name="revanced_sb_new_segment_mark_start">αρχή</string>
<string name="revanced_sb_new_segment_mark_end">τέλος</string>
<string name="revanced_sb_new_segment_now">τώρα</string>
@ -992,6 +993,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">Το χρώμα της γραμμής προόδου</string>
<string name="revanced_seekbar_custom_color_invalid">Μη έγκυρη τιμή χρώματος. Επαναφορά...</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
<string name="revanced_bypass_image_region_restrictions_title">Παράκαμψη μπλοκαρίσματος φόρτωσης εικόνων</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Χρησιμοποιείται το domain yt4.ggpht.com για την φόρτωση εικόνων</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">Χρησιμοποιείται το αρχικό domain για την φόρτωση εικόνων\n\nΗ ενεργοποίηση αυτής της ρύθμισης μπορεί να διορθώσει την φόρτωση εικόνων που είναι μπλοκαρισμένες σε κάποιες περιοχές</string>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Αρχική σελίδα</string>
@ -1114,8 +1120,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">Το πρόγραμμα πελάτη δεν παραποιείται\n\nΗ αναπαραγωγή βίντεο ενδέχεται να μη λειτουργεί</string>
<string name="revanced_spoof_client_user_dialog_message">Η απενεργοποίηση αυτής της ρύθμισης ενδέχεται να προκαλέσει προβλήματα αναπαραγωγής βίντεο.</string>
<string name="revanced_spoof_client_use_ios_title">Παραποίηση σε iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">Ο πελάτης αυτή τη στιγμή παραποιείται ως iOS\n\nΟι παρενέργειες περιλαμβάνουν:\n• Δεν υπάρχει η ποιότητα HDR στα βίντεο\n• Το ιστορικό παρακολούθησης μπορεί να μην λειτουργεί\n• Μπορεί να λείπουν επιλογές για υψηλές αναλύσεις βίντεο\n• Δε γίνεται στις ζωντανές ροές η αναπαραγωγή μόνο του ήχου\n• Οι ζωντανές ροές δεν είναι διαθέσιμες σε παλαιότερες συσκευές με Android 8.0</string>
<string name="revanced_spoof_client_use_ios_summary_off">Ο πελάτης αυτή τη στιγμή παραποιείται ως Android VR\n\nΟι παρενέργειες περιλαμβάνουν:\n• Δεν υπάρχει η ποιότητα HDR στα βίντεο\n• Δεν λειτουργεί η αναπαραγωγή στα βίντεο για παιδιά\n• Βίντεο που έχετε θέσει σε παύση μπορεί να συνεχίσουν να παίζουν τυχαία\n• Χαμηλή ποιότητα μικρογραφιών μπάρας αναζήτησης στα shorts\n• Το κουμπί λήψης είναι πάντα κρυμμένο\n• Οι κάρτες τελικής οθόνης είναι πάντα κρυμμένες</string>
<string name="revanced_spoof_client_use_ios_summary_on">Ο πελάτης αυτή τη στιγμή παραποιείται ως iOS\n\nΟι παρενέργειες περιλαμβάνουν:\n• Δεν υπάρχει η ποιότητα HDR στα βίντεο\n• Το ιστορικό παρακολούθησης μπορεί να μη λειτουργεί</string>
<string name="revanced_spoof_client_use_ios_summary_off">Ο πελάτης αυτή τη στιγμή παραποιείται ως Android VR. Οι παρενέργειες περιλαμβάνουν:\n• Δεν υπάρχει η ποιότητα HDR στα βίντεο\n• Δεν λειτουργεί η αναπαραγωγή στα βίντεο για παιδιά\n• Βίντεο που έχετε θέσει σε παύση μπορεί να συνεχίσουν να παίζουν τυχαία\n• Χαμηλή ποιότητα μικρογραφιών μπάρας αναζήτησης στα shorts\n• Το κουμπί λήψης είναι πάντα κρυμμένο\n• Οι κάρτες τελικής οθόνης είναι πάντα κρυμμένες</string>
<string name="revanced_spoof_client_storyboard_timeout">Μικρογραφίες προεπισκόπησης μη διαθέσιμες (καθυστέρηση API)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Μικρογραφίες προεπισκόπησης προσωρινά μη διαθέσιμες: %s</string>
</patch>

View file

@ -33,7 +33,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
<string name="revanced_settings_title">ReVanced</string>
<string name="revanced_settings_confirm_user_dialog_title">¿Desea continuar?</string>
<string name="revanced_settings_reset">Restablecer</string>
<string name="revanced_settings_restart_title">Actualizar y reiniciar</string>
@ -94,7 +93,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_debug_toast_on_error_user_dialog_message">Desactivar los errores toasts oculta todas las notificaciones de error ReVanced\n\nNo se le notificará de ningún evento inesperado.</string>
</patch>
<patch id="layout.hide.general.HideLayoutComponentsPatch">
<string name="revanced_disable_like_subscribe_glow_title">Desactivar como / botón de suscripción brillo</string>
<string name="revanced_disable_like_subscribe_glow_title">Desactivar el brillo del botón de like / suscripción</string>
<string name="revanced_disable_like_subscribe_glow_summary_on">El botón de \"Me gusta\" y \"suscribir\" no brillará cuando se mencione</string>
<string name="revanced_disable_like_subscribe_glow_summary_off">El botón de \"Me gusta\" y \"Suscribir\" brillará cuando se mencione</string>
<string name="revanced_hide_gray_separator_title">Ocultar separador gris</string>
@ -213,6 +212,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_info_cards_section_title">Ocultar sección de tarjetas de información</string>
<string name="revanced_hide_info_cards_section_summary_on">La sección de tarjetas de información está oculta</string>
<string name="revanced_hide_info_cards_section_summary_off">Se muestra la sección de tarjetas de información</string>
<string name="revanced_hide_key_concepts_section_title">Ocultar sección \'Conceptos clave\'</string>
<string name="revanced_hide_key_concepts_section_summary_on">la sección \'Conceptos clave\' está oculta</string>
<string name="revanced_hide_key_concepts_section_summary_off">la sección \'Conceptos clave\' se muestra</string>
<string name="revanced_hide_transcript_section_title">Ocultar sección de transcripción</string>
<string name="revanced_hide_transcript_section_summary_on">La sección transcripción está oculta</string>
<string name="revanced_hide_transcript_section_summary_off">Se muestra la sección transcripción</string>
@ -248,7 +250,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">Palabra clave inválida. No se puede usar: \'%s\' como filtro</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">Palabra clave no válida. \'%1$s\' es menor de %2$d caracteres</string>
<string name="revanced_hide_keyword_toast_invalid_broad">La palabra clave \'$s\' oculta todos los vídeos</string>
<string name="revanced_hide_keyword_toast_invalid_broad">La palabra clave \'%s\' oculta todos los vídeos</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Ocultar anuncios generales</string>
@ -283,7 +285,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_merchandise_banners_summary_on">Los banners de mercadería están ocultos</string>
<string name="revanced_hide_merchandise_banners_summary_off">Se muestran los banners de Mercandise</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_fullscreen_ads_feature_not_available_toast">Ocultar anuncios a pantalla completa sólo funciona con dispositivos antiguos</string>
<string name="revanced_hide_fullscreen_ads_feature_not_available_toast">Ocultar anuncio solo con dispositivos viejos</string>
</patch>
<patch id="ad.getpremium.HideGetPremiumPatch">
<string name="revanced_hide_get_premium_title">Ocultar promociones de YouTube Premium</string>
@ -674,7 +676,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
<string name="revanced_player_overlay_opacity_title">Opacidad de superposición del jugador</string>
<string name="revanced_player_overlay_opacity_summary">Valor de potencia entre 0-100, donde 0 es transparente</string>
<string name="revanced_player_overlay_opacity_invalid_toast">Opacidad de superposición del jugador debe estar entre 0-100</string>
<string name="revanced_player_overlay_opacity_invalid_toast">Opacidad del reproductor debe estar entre 0 y 100</string>
</patch>
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Oculto</string>
@ -866,9 +868,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">Elige la categoría del segmento</string>
<string name="revanced_sb_new_segment_disabled_category">Categoría está desactivada en la configuración. Habilitar la categoría para enviar.</string>
<string name="revanced_sb_new_segment_title">Nuevo segmento de SponsorBlock</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">¿Establecer %1$02d:%2$02d:%3$03d como inicio o final de un nuevo segmento?</string>
<string name="revanced_sb_new_segment_mark_time_as_question">¿Establecer %s como inicio o fin de un nuevo segmento?</string>
<string name="revanced_sb_new_segment_mark_start">comienzo</string>
<string name="revanced_sb_new_segment_mark_end">final</string>
<string name="revanced_sb_new_segment_now">ahora</string>
@ -978,7 +978,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_miniplayer_hide_rewind_forward_summary_off">Saltar adelante y atrás se muestran</string>
<string name="revanced_miniplayer_opacity_title">Overlay opacity</string>
<string name="revanced_miniplayer_opacity_summary">Valor de potencia entre 0-100, donde 0 es transparente</string>
<string name="revanced_miniplayer_opacity_invalid_toast">La opacidad de superposición del Miniplayer debe estar entre 0-100</string>
<string name="revanced_miniplayer_opacity_invalid_toast"> Opacidad de reproductor debe estar en 0 -100</string>
</patch>
<patch id="layout.theme.ThemeBytecodePatch">
<string name="revanced_gradient_loading_screen_title">Activar la pantalla de carga del degradado</string>
@ -993,6 +993,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">El color de la barra de ajustes</string>
<string name="revanced_seekbar_custom_color_invalid">Valor de color de la barra de búsqueda inválido. Usando el valor por defecto.</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
<string name="revanced_bypass_image_region_restrictions_title">Restricciones de región de imagen bypass</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Usando host de imagen yt4.ggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">Utilizando el host de imágenes original\n\nHabilitar esto puede arreglar las imágenes faltantes que están bloqueadas en algunas regiones</string>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Pestaña</string>
@ -1115,8 +1120,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">El cliente no está falsificado\n\nEs posible que la reproducción de vídeo no funcione</string>
<string name="revanced_spoof_client_user_dialog_message">Desactivar esta configuración puede causar problemas de reproducción de vídeo.</string>
<string name="revanced_spoof_client_use_ios_title">Spoof cliente a iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">El cliente está actualmente falseado a iOS\n\nLos efectos laterales incluyen:\n• No hay vídeo HDR\n• Es posible que el historial del reloj no funcione\n• Puede que falten más calidad de vídeo\n• Los streams en vivo no pueden reproducirse como audio solo\n• streams en vivo no disponibles en Android 8.0</string>
<string name="revanced_spoof_client_use_ios_summary_off">El cliente está actualmente falseado a Android VR\n\nLos efectos laterales incluyen:\n• No hay vídeo HDR\n• Los vídeos para niños no se reproducirán\n• Los vídeos en pausa pueden reanudarse aleatoriamente\n• Las miniaturas de barras cortas de baja calidad\n• El botón de descarga siempre está oculto\n• Las tarjetas de la pantalla final siempre están ocultas</string>
<string name="revanced_spoof_client_use_ios_summary_on">El cliente está actualmente falseado a iOS\n\nLos efectos laterales incluyen:\n• El vídeo HDR puede no estar disponible\n• El historial del reloj puede no funcionar</string>
<string name="revanced_spoof_client_use_ios_summary_off">El cliente está actualmente falseado a Android VR. Los efectos laterales incluyen:\n• No hay vídeo HDR\n• Los niños vídeos no se reproducirán\n• Los vídeos en pausa pueden reanudarse aleatoriamente\n• Las miniaturas de barras cortas de baja calidad\n• El botón de acción está oculto\n• Las tarjetas de pantalla final están ocultas</string>
<string name="revanced_spoof_client_storyboard_timeout">Las miniaturas del cliente de Spoof no están disponibles (API)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Las miniaturas del cliente Spoof temporalmente no están disponibles: %s</string>
</patch>

View file

@ -33,7 +33,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
<string name="revanced_settings_reset">Lähtesta</string>
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
@ -43,8 +42,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="misc.settings.SettingsResourcePatch">
</patch>
<patch id="misc.settings.SettingsPatch">
<string name="revanced_settings_screen_00_about_title">Teave</string>
<string name="revanced_settings_screen_11_misc_title">Muud</string>
</patch>
<patch id="misc.debugging.DebuggingPatch">
</patch>
@ -140,8 +137,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.hide.fullscreenambientmode.DisableFullscreenAmbientModePatch">
</patch>
<patch id="layout.hide.infocards.HideInfocardsResourcePatch">
<string name="revanced_hide_info_cards_summary_on">Infokaardid on peidetud</string>
<string name="revanced_hide_info_cards_summary_off">Infokaardid on nähtaval</string>
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
</patch>
@ -167,7 +162,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<string name="revanced_ryd_about">Teave</string>
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.WideSearchbarPatch">
@ -175,17 +169,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<string name="revanced_sb_appearance_category">Välimus</string>
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
<string name="revanced_sb_stats_loading">Laen...</string>
<string name="revanced_sb_reset">Lähtesta</string>
<string name="revanced_sb_about">Teave</string>
</patch>
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
@ -193,7 +181,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- 'RYD' is 'Return YouTube Dislike' -->
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<string name="revanced_start_page_entry_0">Vaikimisi</string>
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
@ -209,6 +196,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
@ -216,7 +205,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.AnnouncementsPatch">
<string name="revanced_announcements_dialog_dismiss">Loobu</string>
</patch>
<patch id="misc.autorepeat.AutoRepeatPatch">
</patch>
@ -257,7 +245,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="ad.audio.AudioAdsPatch">
</patch>
<patch id="ad.embedded.EmbeddedAdsPatch">
<string name="revanced_block_embedded_ads_entry_1">Väljas</string>
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
@ -269,7 +256,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
</patch>
<patch id="misc.settings.SettingsPatch">
<string name="revanced_misc_screen_title">Muud</string>
</patch>
</app>
</resources>

View file

@ -171,8 +171,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
@ -198,6 +196,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->

View file

@ -33,7 +33,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
<string name="revanced_settings_reset">تنظیم مجدد</string>
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
@ -41,10 +40,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</app>
<app id="youtube">
<patch id="misc.settings.SettingsResourcePatch">
<string name="revanced_settings_about_links_header">لینک های رسمی</string>
</patch>
<patch id="misc.settings.SettingsPatch">
<string name="revanced_settings_screen_00_about_title">درباره ما</string>
</patch>
<patch id="misc.debugging.DebuggingPatch">
</patch>
@ -165,7 +162,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<string name="revanced_ryd_about">درباره ما</string>
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.WideSearchbarPatch">
@ -173,17 +169,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<string name="revanced_sb_appearance_category">ظاهر</string>
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
<string name="revanced_sb_stats_loading">درحال بارگذاری...</string>
<string name="revanced_sb_reset">تنظیم مجدد</string>
<string name="revanced_sb_about">درباره ما</string>
</patch>
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
@ -191,7 +181,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- 'RYD' is 'Return YouTube Dislike' -->
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<string name="revanced_start_page_entry_0">پیشفرض</string>
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
@ -207,6 +196,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
@ -254,7 +245,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="ad.audio.AudioAdsPatch">
</patch>
<patch id="ad.embedded.EmbeddedAdsPatch">
<string name="revanced_block_embedded_ads_entry_1">غیرفعال شده</string>
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>

File diff suppressed because it is too large Load diff

View file

@ -243,7 +243,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">Di-wastong keyword. Hindi magagamit ang: \"%s\" bilang filter</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">Di-wastong keyword. Ang \"%1$s\" ay mas mababa sa %2$d character</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Itatago ng keyword na \"$s\" ang lahat ng video</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Itatago ng keyword na \"%s\" ang lahat ng video</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Itago ang mga pangkalahatang ad</string>
@ -854,9 +854,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">Piliin ang kategorya ng segment</string>
<string name="revanced_sb_new_segment_disabled_category">Naka-disable ang kategorya sa mga setting. Paganahin ang kategoryang isumite.</string>
<string name="revanced_sb_new_segment_title">Bagong segment ng SponsorBlock</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">Itakda ang %1$02d:%2$02d:%3$03d bilang simula o pagtatapos ng isang bagong segment?</string>
<string name="revanced_sb_new_segment_mark_start">simulan</string>
<string name="revanced_sb_new_segment_mark_end">wakas</string>
<string name="revanced_sb_new_segment_now">ngayon</string>
@ -974,6 +971,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">Ang kulay ng seekbar</string>
<string name="revanced_seekbar_custom_color_invalid">Di-wastong halaga ng kulay ng seekbar. Gamit ang default na halaga.</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Tab ng tahanan</string>
@ -1091,8 +1090,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">Hindi na-spoof ang kliyente\n\nMaaaring hindi gumana ang pag-playback ng video</string>
<string name="revanced_spoof_client_user_dialog_message">Ang pag-off sa setting na ito ay maaaring magdulot ng mga isyu sa pag-playback ng video.</string>
<string name="revanced_spoof_client_use_ios_title">Spoof client sa iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">Kasalukuyang na-spoof ang kliyente sa iOS\n\nKabilang sa mga side effect ang:\n• Walang HDR video\n• Maaaring hindi gumana ang history ng panonood\n• Maaaring nawawala ang mas matataas na katangian ng video\n• Hindi puwedeng mag-play bilang audio lang ang mga live stream\n• Live hindi available ang mga stream sa Android 8.0</string>
<string name="revanced_spoof_client_use_ios_summary_off">Kasalukuyang na-spoof ang kliyente sa Android VR\n\nKabilang sa mga side effect ang:\n• Walang HDR na video\n• Hindi nagpe-playback ang mga pambata na video\n• Ang mga naka-pause na video ay maaaring random na ipagpatuloy\n• Mga thumbnail ng shorts seekbar na may mababang kalidad\n• I-download ang action button ay laging nakatago\n• Palaging nakatago ang mga end screen card</string>
<string name="revanced_spoof_client_storyboard_timeout">Hindi available ang mga thumbnail ng spoof client (nag-time out ang API)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Pansamantalang hindi available ang mga thumbnail ng spoof client: %s</string>
</patch>

View file

@ -72,7 +72,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_settings_screen_04_player_title">Lecteur</string>
<string name="revanced_settings_screen_05_general_title">Disposition générale</string>
<string name="revanced_settings_screen_07_seekbar_title">Barre de progression</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Commandes de balayage</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Contrôle par gestes</string>
<string name="revanced_settings_screen_11_misc_title">Divers</string>
<string name="revanced_settings_screen_12_video_title">Vidéo</string>
</patch>
@ -121,7 +121,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_notify_me_button_summary_on">Le bouton est masqué</string>
<string name="revanced_hide_notify_me_button_summary_off">Le bouton est affiché</string>
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
<string name="revanced_hide_search_result_recommendations_title">Cacher les recommandations \"Personnes également suivies\"</string>
<string name="revanced_hide_search_result_recommendations_title">Masquer les recommandations \'Les gens ont aussi regardé\'</string>
<string name="revanced_hide_search_result_recommendations_summary_on">Les recommandations sont masquées</string>
<string name="revanced_hide_search_result_recommendations_summary_off">Les recommandations sont affichées</string>
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
@ -177,9 +177,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_channel_bar_title">Masquer la barre de la chaîne</string>
<string name="revanced_hide_channel_bar_summary_on">La barre de la chaîne est masquée</string>
<string name="revanced_hide_channel_bar_summary_off">La barre de la chaîne est affichée</string>
<string name="revanced_hide_playables_title">Cacher les Playables</string>
<string name="revanced_hide_playables_summary_on">Les jouables sont masqués</string>
<string name="revanced_hide_playables_summary_off">Les jouables sont affichés</string>
<string name="revanced_hide_playables_title">Masquer les jeux intégrés</string>
<string name="revanced_hide_playables_summary_on">Les jeux sont masqués</string>
<string name="revanced_hide_playables_summary_off">Les jeux sont affichés</string>
<string name="revanced_hide_quick_actions_title">Masquer les actions rapides en plein écran</string>
<string name="revanced_hide_quick_actions_summary_on">Les actions rapides en plein écran sont masquées</string>
<string name="revanced_hide_quick_actions_summary_off">Les actions rapides en plein écran sont affichées</string>
@ -213,6 +213,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_info_cards_section_title">Masquer les cartes d\'information</string>
<string name="revanced_hide_info_cards_section_summary_on">Les cartes d\'information sont masquées</string>
<string name="revanced_hide_info_cards_section_summary_off">Les cartes d\'information sont affichées</string>
<string name="revanced_hide_key_concepts_section_title">Masquer la section \"concepts clés\"</string>
<string name="revanced_hide_key_concepts_section_summary_on">La section \"Concepts clé\" est masquée</string>
<string name="revanced_hide_key_concepts_section_summary_off">La section \"Concepts clé\" est affichée</string>
<string name="revanced_hide_transcript_section_title">Masquer la section de transcription</string>
<string name="revanced_hide_transcript_section_summary_on">La section transcription est masquée</string>
<string name="revanced_hide_transcript_section_summary_off">La section transcription est affichée</string>
@ -248,7 +251,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_toast_invalid_common">Mot-clé invalide. Impossible d\'utiliser : \'%s\' comme filtre</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_length">Mot-clé invalide. \'%1$s\' contient moins de %2$d caractères</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Le mot-clé \'$s\' masquera toutes les vidéos</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Le mot-clé «%s» masquera toutes les vidéos</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Masquer les pubs générales</string>
@ -260,9 +263,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_buttoned_ads_title">Masquer les boutons de publicité</string>
<string name="revanced_hide_buttoned_ads_summary_on">Les boutons de publicité sont masqués</string>
<string name="revanced_hide_buttoned_ads_summary_off">Les boutons de publicité sont affichés</string>
<string name="revanced_hide_paid_promotion_label_title">Masquer le libellé de la promotion payante</string>
<string name="revanced_hide_paid_promotion_label_summary_on">Le libellé de la promotion payante est masqué</string>
<string name="revanced_hide_paid_promotion_label_summary_off">Le libellé de la promotion payée est affiché</string>
<string name="revanced_hide_paid_promotion_label_title">Masquer la bannière \"Communication commerciale\"</string>
<string name="revanced_hide_paid_promotion_label_summary_on">La bannière \"Communication commerciale\" est masquée</string>
<string name="revanced_hide_paid_promotion_label_summary_off">La bannière \"Communication commerciale\" est affichée</string>
<string name="revanced_hide_self_sponsor_ads_title">Masquer les cartes auto-sponsorisées</string>
<string name="revanced_hide_self_sponsor_ads_summary_on">Les cartes auto-sponsorisées sont masquées</string>
<string name="revanced_hide_self_sponsor_ads_summary_off">Les cartes auto-sponsorisées sont affichées</string>
@ -273,7 +276,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_shopping_links_summary_on">Les liens d\'achat sont masqués</string>
<string name="revanced_hide_shopping_links_summary_off">Les liens d\'achat sont affichés</string>
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_visit_store_button_title">Masquer le bouton \'Visiter la boutique\' sur les pages des canaux</string>
<string name="revanced_hide_visit_store_button_title">Masquer le bouton \'Visiter la boutique\' sur la page de la chaîne</string>
<string name="revanced_hide_visit_store_button_summary_on">Le bouton est masqué</string>
<string name="revanced_hide_visit_store_button_summary_off">Le bouton est affiché</string>
<string name="revanced_hide_web_search_results_title">Masquer les résultats de la recherche web</string>
@ -306,9 +309,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_copy_video_url_timestamp_summary_off">Le bouton n\'est pas affiché</string>
</patch>
<patch id="interaction.dialog.RemoveViewerDiscretionDialogPatch">
<string name="revanced_remove_viewer_discretion_dialog_title">Supprimer la boîte de dialogue de discrétion de la visionneuse</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_on">La boîte de dialogue sera supprimée</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">La boîte de dialogue sera affichée</string>
<string name="revanced_remove_viewer_discretion_dialog_title">Supprimer la confirmation pour les vidéos avec limite d\'âge</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_on">Le dialogue de confirmation sera supprimé</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">Le dialogue de confirmation sera affiché</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">Cela ne contourne pas la restriction d\'âge. Il l\'accepte tout simplement automatiquement.</string>
</patch>
<patch id="interaction.downloads.DownloadsResourcePatch">
@ -321,8 +324,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_external_downloader_action_button_title">Remplacer l\'action du bouton téléchargement</string>
<string name="revanced_external_downloader_action_button_summary_on">Le bouton de téléchargement ouvre votre téléchargeur externe</string>
<string name="revanced_external_downloader_action_button_summary_off">Le bouton de téléchargement ouvre le téléchargeur natif dans l\'application</string>
<string name="revanced_external_downloader_name_title">Nom du paquet du téléchargeur</string>
<string name="revanced_external_downloader_name_summary">Nom du paquet de votre application de téléchargement externe installée, comme NewPipe ou Seal</string>
<string name="revanced_external_downloader_name_title">Nom du package du téléchargeur</string>
<string name="revanced_external_downloader_name_summary">Nom du package de votre application de téléchargement externe installée, comme NewPipe ou Seal</string>
<string name="revanced_external_downloader_not_installed_warning">%s n\'est pas installé. Veuillez l\'installer.</string>
</patch>
<patch id="interaction.seekbar.DisablePreciseSeekingGesturePatch">
@ -331,23 +334,23 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_disable_precise_seeking_gesture_summary_off">Le geste est activé</string>
</patch>
<patch id="interaction.seekbar.EnableSeekbarTappingPatch">
<string name="revanced_seekbar_tapping_title">Activer le tapping de la barre de recherche</string>
<string name="revanced_seekbar_tapping_summary_on">Le tapping de la barre de recherche est activé</string>
<string name="revanced_seekbar_tapping_summary_off">Le tapotage de la barre de recherche est désactivé</string>
<string name="revanced_seekbar_tapping_title">Activer l\'appui dans la barre de lecture</string>
<string name="revanced_seekbar_tapping_summary_on">L\'appui dans la barre de lecture est activé</string>
<string name="revanced_seekbar_tapping_summary_off">L\'appui dans la barre de lecture est désactivé</string>
</patch>
<patch id="interaction.swipecontrols.SwipeControlsResourcePatch">
<string name="revanced_swipe_brightness_title">Activer le geste de luminosité</string>
<string name="revanced_swipe_brightness_summary_on">Le balayage de la luminosité est activé</string>
<string name="revanced_swipe_brightness_summary_off">Le balayage de la luminosité est désactivé</string>
<string name="revanced_swipe_volume_title">Activer le mouvement de volume</string>
<string name="revanced_swipe_volume_summary_on">Le balayage du volume est activé</string>
<string name="revanced_swipe_volume_summary_off">Le balayage du volume est désactivé</string>
<string name="revanced_swipe_brightness_title">Activer les gestes pour la luminosité</string>
<string name="revanced_swipe_brightness_summary_on">Les gestes de contrôle de la luminosité sont activés</string>
<string name="revanced_swipe_brightness_summary_off">Les gestes de contrôle de la luminosité sont désactivés</string>
<string name="revanced_swipe_volume_title">Activer les gestes pour le volume</string>
<string name="revanced_swipe_volume_summary_on">Les gestes de contrôle du volume sont activés</string>
<string name="revanced_swipe_volume_summary_off">Les gestes de contrôle du volume sont désactivés</string>
<string name="revanced_swipe_press_to_engage_title">Activer le geste d\'appui pour glisser</string>
<string name="revanced_swipe_press_to_engage_summary_on">Appui pour glisser est activé</string>
<string name="revanced_swipe_press_to_engage_summary_off">Appui pour glisser est désactivé</string>
<string name="revanced_swipe_haptic_feedback_title">Activer le retour haptique</string>
<string name="revanced_swipe_haptic_feedback_summary_on">Le retour tactile est activé</string>
<string name="revanced_swipe_haptic_feedback_summary_off">Le retour tactile est désactivé</string>
<string name="revanced_swipe_haptic_feedback_summary_on">Le retour haptique est activé</string>
<string name="revanced_swipe_haptic_feedback_summary_off">Le retour haptique est désactivé</string>
<string name="revanced_swipe_save_and_restore_brightness_title">Enregistrer et restaurer la luminosité</string>
<string name="revanced_swipe_save_and_restore_brightness_summary_on">Enregistrer et restaurer la luminosité en quittant ou en entrant en plein écran</string>
<string name="revanced_swipe_save_and_restore_brightness_summary_off">Ne pas enregistrer et restaurer la luminosité en quittant ou en entrant en plein écran</string>
@ -355,12 +358,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Glisser vers le bas vers la valeur la plus basse du geste de luminosité active la luminosité automatique</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Glisser vers le bas vers la valeur la plus basse n\'active pas la luminosité automatique</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automatique</string>
<string name="revanced_swipe_overlay_timeout_title">Délai d\'overlay du balayage</string>
<string name="revanced_swipe_overlay_timeout_summary">La quantité de millisecondes que la superposition est visible</string>
<string name="revanced_swipe_text_overlay_size_title">Glisser la taille du texte de la superposition</string>
<string name="revanced_swipe_text_overlay_size_summary">La taille du texte pour la superposition de balayage</string>
<string name="revanced_swipe_overlay_timeout_title">Délai de l\'overlay des gestes</string>
<string name="revanced_swipe_overlay_timeout_summary">Le temps en millisecondes où l\'overlay est visible</string>
<string name="revanced_swipe_text_overlay_size_title">Taille du texte de l\'overlay des gestes</string>
<string name="revanced_swipe_text_overlay_size_summary">La taille du texte pour l\'overlay des gestes</string>
<string name="revanced_swipe_overlay_background_alpha_title">Visibilité de l\'arrière-plan en glissant</string>
<string name="revanced_swipe_overlay_background_alpha_summary">La visibilité de l\'arrière-plan de la superposition de balayage</string>
<string name="revanced_swipe_overlay_background_alpha_summary">La visibilité de l\'arrière-plan de l\'overlay des gestes</string>
<string name="revanced_swipe_threshold_title">Seuil de magnitude de balayage</string>
<string name="revanced_swipe_threshold_summary">La quantité de seuil pour que le balayage se produise</string>
</patch>
@ -866,9 +869,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_choose_category">Choisissez la catégorie du segment</string>
<string name="revanced_sb_new_segment_disabled_category">La catégorie est désactivée dans les paramètres. Activez la catégorie pour soumettre.</string>
<string name="revanced_sb_new_segment_title">Nouveau segment SponsorBlock</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_time_as_question">Utiliser %1$02d:%2$02d:%3$03d comme début ou fin d\'un nouveau segment ?</string>
<string name="revanced_sb_new_segment_mark_time_as_question">Définir %s comme début ou fin d\'un nouveau segment ?</string>
<string name="revanced_sb_new_segment_mark_start">démarrer</string>
<string name="revanced_sb_new_segment_mark_end">fin</string>
<string name="revanced_sb_new_segment_now">maintenant</string>
@ -957,25 +958,25 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_tablet_layout_user_dialog_message">Les messages de la communauté n\'apparaissent pas sur la disposition de la tablette</string>
</patch>
<patch id="layout.miniplayer.MiniplayerPatch">
<string name="revanced_miniplayer_screen_title">Minijoueur</string>
<string name="revanced_miniplayer_screen_title">Lecteur réduit</string>
<string name="revanced_miniplayer_screen_summary">Changer le style du lecteur réduit dans l\'application</string>
<string name="revanced_miniplayer_type_title">Type de Miniplayer</string>
<string name="revanced_miniplayer_type_entry_1">Originale</string>
<string name="revanced_miniplayer_type_entry_1">Original</string>
<string name="revanced_miniplayer_type_entry_2">Téléphone</string>
<string name="revanced_miniplayer_type_entry_3">Tablette</string>
<string name="revanced_miniplayer_type_entry_4">Moderne 1</string>
<string name="revanced_miniplayer_type_entry_5">Moderne 2</string>
<string name="revanced_miniplayer_type_entry_6">Moderne 3</string>
<string name="revanced_miniplayer_hide_expand_close_title">Masquer les boutons d\'extension et de fermeture</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">Les boutons sont cachés\n(glisser le minijoueur pour l\'expansion ou la fermeture)</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">Développer et fermer les boutons sont affichés</string>
<string name="revanced_miniplayer_hide_expand_close_title">Masquer les boutons d\'agrandissement et de fermeture</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">Les boutons sont cachés\n(glisser le lecteur réduit pour l\'agrandir ou le fermer)</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">Les boutons agrandir et fermer sont affichés</string>
<string name="revanced_miniplayer_hide_subtext_title">Masquer les sous-textes</string>
<string name="revanced_miniplayer_hide_subtext_summary_on">Les sous-textes sont cachés</string>
<string name="revanced_miniplayer_hide_subtext_summary_off">Les sous-textes sont affichés</string>
<string name="revanced_miniplayer_hide_rewind_forward_title">Masquer les boutons avant et retour</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_on">Sauter en avant et en arrière sont masqués</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_off">Sauter en avant et en arrière sont affichés</string>
<string name="revanced_miniplayer_opacity_title">Overlay opacity</string>
<string name="revanced_miniplayer_hide_rewind_forward_title">Masquer les boutons avancer et reculer</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_on">Les boutons avancer et reculer sont masqués</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_off">Les boutons avancer et reculer sont affichés</string>
<string name="revanced_miniplayer_opacity_title">Opacité de l\'overlay</string>
<string name="revanced_miniplayer_opacity_summary">Valeur d\'opacité entre 0 et 100, où 0 est transparent</string>
<string name="revanced_miniplayer_opacity_invalid_toast">L\'opacité de l\'overlay du Miniplayer doit être comprise entre 0 et 100</string>
</patch>
@ -992,6 +993,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_value_summary">La couleur de la barre de recherche</string>
<string name="revanced_seekbar_custom_color_invalid">Valeur de couleur de la barre de recherche invalide.</string>
</patch>
<patch id="layout.thumbnails.BypassImageRegionRestrictions">
<string name="revanced_bypass_image_region_restrictions_title">Ignorer les restrictions de région de l\'image</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Utiliser l\'hôte d\'image yt4.ggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">Utiliser l\'hôte d\'image original\n\nActiver ceci peut corriger les images manquantes qui sont bloquées dans certaines régions</string>
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_alt_thumbnail_home_title">Onglet d\'accueil</string>
@ -1114,8 +1120,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_client_summary_off">Le client n\'est pas trompé\n\nLa lecture de la vidéo peut ne pas fonctionner</string>
<string name="revanced_spoof_client_user_dialog_message">Désactiver ce paramètre peut causer des problèmes de lecture vidéo.</string>
<string name="revanced_spoof_client_use_ios_title">Falsifier le client vers iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">Le client simule actuellement iOS\n\nLes effets secondaires incluent :\n• Aucune vidéo HDR\n• L\'historique de lecture peut ne pas fonctionner\n• Des qualités vidéo plus élevées peuvent manquer\n• Les vidéos en direct ne peuvent pas être lancées uniquement en tant que audio\n• Les vidéos en direct ne sont pas disponibles sur Android 8.0</string>
<string name="revanced_spoof_client_use_ios_summary_off">Le client est actuellement usurpé à Android VR\n\nEffets secondaires incluant:\n• Aucune vidéo HDR\n• Les vidéos des enfants ne lisent pas\n• Les vidéos en pause peuvent reprendre aléatoirement\n• Les Shorts de basse qualité sur les vignettes de la barre de recherche\n• Le bouton d\'action de téléchargement est toujours caché\n• Les cartes d\'écran de fin sont toujours cachées</string>
<string name="revanced_spoof_client_use_ios_summary_on">Le client est actuellement falsifié vers iOS\n\nLes effets secondaires incluent :\n• La vidéo HDR peut ne pas être disponible\n• L\'historique de la montre peut ne pas fonctionner</string>
<string name="revanced_spoof_client_use_ios_summary_off">Le client est actuellement falsifié vers Android VR. Effets secondaires incluant:\n• Aucune vidéo HDR\n• Les vidéos des enfants ne lisent pas\n• Les vidéos en pause peuvent reprendre aléatoirement\n• Les Shorts de basse qualité sur les vignettes de la barre de recherche\n• Le bouton d\'action de téléchargement est caché\n• Les cartes d\'écran de fin sont cachées</string>
<string name="revanced_spoof_client_storyboard_timeout">Tromper les vignettes du client non disponibles (API expirée)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Tromper les vignettes du client temporairement indisponibles : %s</string>
</patch>

Some files were not shown because too many files have changed in this diff Show more