mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-15 21:52:27 +01:00
Compare commits
59 Commits
v4.11.0-de
...
v4.12.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4085d1f9dc | ||
|
|
eadbf5f459 | ||
|
|
b12b3a73a6 | ||
|
|
572a310589 | ||
|
|
2e8d5c61f8 | ||
|
|
025766bb42 | ||
|
|
e31966159a | ||
|
|
337bdc3d39 | ||
|
|
13ed4a2f39 | ||
|
|
d2afc53c2b | ||
|
|
1fab0ae59a | ||
|
|
5c8c597d19 | ||
|
|
33800801a3 | ||
|
|
62c47665e4 | ||
|
|
7acb6cdc96 | ||
|
|
a5d32c3da3 | ||
|
|
a4b0e76755 | ||
|
|
0a7b2c5ec2 | ||
|
|
eed856d64c | ||
|
|
e8d481397f | ||
|
|
d0eceb3e36 | ||
|
|
8886fc4f54 | ||
|
|
fb4256f17c | ||
|
|
98f9bba7ed | ||
|
|
8e72067dcb | ||
|
|
d87f36e7e2 | ||
|
|
4432fe65df | ||
|
|
8b0d8ee9f4 | ||
|
|
250cc7cbde | ||
|
|
1af65de1f6 | ||
|
|
6e87e3044c | ||
|
|
273af26274 | ||
|
|
2b1b081051 | ||
|
|
f2bf2da9a5 | ||
|
|
15317003b1 | ||
|
|
6c81a5b65f | ||
|
|
9ef51abde7 | ||
|
|
1d31565d47 | ||
|
|
b944fb7bf1 | ||
|
|
dfd46d8e8f | ||
|
|
f64e03a1f6 | ||
|
|
a65970bdc2 | ||
|
|
cfbc4aa6b2 | ||
|
|
b04652890e | ||
|
|
4fe1dbe9e0 | ||
|
|
32acfbaee7 | ||
|
|
d02a490f36 | ||
|
|
f0fb2fa3ba | ||
|
|
4885d4ef00 | ||
|
|
a036c1fa0a | ||
|
|
a81a6bf5b2 | ||
|
|
81836119c0 | ||
|
|
def1ec4de6 | ||
|
|
72f02c8d2f | ||
|
|
5e0dd932cd | ||
|
|
dd6a9f977f | ||
|
|
7d4c2e820c | ||
|
|
e4ae9ccd26 | ||
|
|
d469604f0f |
2
.github/workflows/pull_strings.yml
vendored
2
.github/workflows/pull_strings.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/push_strings.yml
vendored
2
.github/workflows/push_strings.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -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:
|
||||
|
||||
190
CHANGELOG.md
190
CHANGELOG.md
@@ -1,3 +1,193 @@
|
||||
# [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Boost for reddit - Fix missing audio in video downloads:** Replace correct strings ([#3379](https://github.com/ReVanced/revanced-patches/issues/3379)) ([b43db98](https://github.com/ReVanced/revanced-patches/commit/b43db98e8483e2939d8fb9cd02443f24aeaae3c3))
|
||||
* **Windy - Unlock pro:** Revert changing package name ([#3402](https://github.com/ReVanced/revanced-patches/issues/3402)) ([541f1e7](https://github.com/ReVanced/revanced-patches/commit/541f1e702665630a3737f67a4cc0c4f7b4899f8f))
|
||||
* **Windy - Unlock pro:** Use correct package name ([#3397](https://github.com/ReVanced/revanced-patches/issues/3397)) ([1d8459a](https://github.com/ReVanced/revanced-patches/commit/1d8459ac992b12371c41df3c25b6386119770d15))
|
||||
* **YouTube - Hide layout components:** Detect if a keyword filter hides all videos ([#3365](https://github.com/ReVanced/revanced-patches/issues/3365)) ([6aa47ec](https://github.com/ReVanced/revanced-patches/commit/6aa47ec1050cf32158ab608441c0649501184971))
|
||||
* **YouTube - Settings:** Move some settings to different menus, adjust default setting values ([#3415](https://github.com/ReVanced/revanced-patches/issues/3415)) ([7201ac4](https://github.com/ReVanced/revanced-patches/commit/7201ac45c158682413c8584aac7bb37b770fc924))
|
||||
* **YouTube - SponsorBlock:** Skip segments when casting ([#3331](https://github.com/ReVanced/revanced-patches/issues/3331)) ([d9395fd](https://github.com/ReVanced/revanced-patches/commit/d9395fdbca45cf68fbc63469e228eefbd6c2bf2e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Remove share targets` patch ([#3334](https://github.com/ReVanced/revanced-patches/issues/3334)) ([9414122](https://github.com/ReVanced/revanced-patches/commit/94141228163aee8d051491db51fc1e4c8b86f0e6))
|
||||
* Add translations ([#2963](https://github.com/ReVanced/revanced-patches/issues/2963)) ([69ea6f3](https://github.com/ReVanced/revanced-patches/commit/69ea6f3bc2b5f419320f17c150489dcb9eed76ce))
|
||||
* **Bandcamp:** Add `Remove play limits` patch ([#3366](https://github.com/ReVanced/revanced-patches/issues/3366)) ([ad8d3bb](https://github.com/ReVanced/revanced-patches/commit/ad8d3bb1c86f1324234e890f1171ec4a18e56dd9))
|
||||
* **Instagram:** Add `Hide ads` patch ([#3380](https://github.com/ReVanced/revanced-patches/issues/3380)) ([c6b2f8c](https://github.com/ReVanced/revanced-patches/commit/c6b2f8c0172b4fd142927d9448ed855831c86ae4))
|
||||
* **RAR:** Add `Hide purchase reminder` patch ([#3321](https://github.com/ReVanced/revanced-patches/issues/3321)) ([8fbe7e3](https://github.com/ReVanced/revanced-patches/commit/8fbe7e3d38c43adfa0755bcbe87f8c6b7696da3a))
|
||||
* **Soundcloud:** Add `Hide ads` and `Disable telemetry` patch ([#3386](https://github.com/ReVanced/revanced-patches/issues/3386)) ([3c79f3d](https://github.com/ReVanced/revanced-patches/commit/3c79f3d34d978aead60de19e64d05fbebc1bc73a))
|
||||
* **Stocard:** Add `Hide offers tab` and `Hide story bubbles` patch ([#3359](https://github.com/ReVanced/revanced-patches/issues/3359)) ([fbd0507](https://github.com/ReVanced/revanced-patches/commit/fbd0507ce5cdeb93a1f661aa8097139c61e643a0))
|
||||
|
||||
# [4.11.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.6...v4.11.0-dev.7) (2024-07-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Bandcamp:** Add `Remove play limits` patch ([#3366](https://github.com/ReVanced/revanced-patches/issues/3366)) ([ad8d3bb](https://github.com/ReVanced/revanced-patches/commit/ad8d3bb1c86f1324234e890f1171ec4a18e56dd9))
|
||||
|
||||
# [4.11.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.5...v4.11.0-dev.6) (2024-07-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - SponsorBlock:** Skip segments when casting ([#3331](https://github.com/ReVanced/revanced-patches/issues/3331)) ([d9395fd](https://github.com/ReVanced/revanced-patches/commit/d9395fdbca45cf68fbc63469e228eefbd6c2bf2e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Soundcloud:** Add `Hide ads` and `Disable telemetry` patch ([#3386](https://github.com/ReVanced/revanced-patches/issues/3386)) ([3c79f3d](https://github.com/ReVanced/revanced-patches/commit/3c79f3d34d978aead60de19e64d05fbebc1bc73a))
|
||||
|
||||
# [4.11.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.4...v4.11.0-dev.5) (2024-07-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Settings:** Move some settings to different menus, adjust default setting values ([#3415](https://github.com/ReVanced/revanced-patches/issues/3415)) ([7201ac4](https://github.com/ReVanced/revanced-patches/commit/7201ac45c158682413c8584aac7bb37b770fc924))
|
||||
|
||||
# [4.11.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.3...v4.11.0-dev.4) (2024-07-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Windy - Unlock pro:** Revert changing package name ([#3402](https://github.com/ReVanced/revanced-patches/issues/3402)) ([541f1e7](https://github.com/ReVanced/revanced-patches/commit/541f1e702665630a3737f67a4cc0c4f7b4899f8f))
|
||||
|
||||
# [4.11.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.2...v4.11.0-dev.3) (2024-06-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Windy - Unlock pro:** Use correct package name ([#3397](https://github.com/ReVanced/revanced-patches/issues/3397)) ([1d8459a](https://github.com/ReVanced/revanced-patches/commit/1d8459ac992b12371c41df3c25b6386119770d15))
|
||||
|
||||
# [4.11.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.1...v4.11.0-dev.2) (2024-06-27)
|
||||
|
||||
|
||||
|
||||
@@ -83,6 +83,10 @@ Features can be requested by opening an issue using the
|
||||
If you encounter a bug while using ReVanced Patches, open an issue using the
|
||||
[Bug report issue template](https://github.com/ReVanced/revanced-patches/issues/new?assignees=&labels=Bug+report&projects=&template=bug_report.yml&title=bug%3A+).
|
||||
|
||||
## 🌐 Submitting translations
|
||||
|
||||
You can contribute translations at [translate.revanced.app](https://translate.revanced.app).
|
||||
|
||||
## 🧑⚖️ Guidelines for requesting or contributing patches
|
||||
|
||||
To maintain a high-quality and ethical collection of patches, the following guidelines for requesting
|
||||
@@ -107,7 +111,6 @@ are unaffected by this change.
|
||||
* Payment circumvention: We do not accept patches that exist solely to bypass payment for the app or any of its features
|
||||
* Malicious patches: Patches that are malicious in nature are not allowed
|
||||
|
||||
|
||||
## 📝 How to contribute
|
||||
|
||||
1. Before contributing, it is recommended to open an issue to discuss your change
|
||||
@@ -120,6 +123,6 @@ described in the [ReVanced Patcher documentation](https://github.com/ReVanced/re
|
||||
that your pull request closes in the description of your pull request
|
||||
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
|
||||
it will be merged into the `dev` branch and will be included in the next release of ReVanced Patches
|
||||
|
||||
|
||||
❤️ Thank you for considering contributing to ReVanced Patches,
|
||||
ReVanced
|
||||
|
||||
@@ -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
|
||||
@@ -181,6 +225,12 @@ public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatch : app/
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch;
|
||||
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/candylinkvpn/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/candylinkvpn/UnlockProPatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
@@ -211,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
|
||||
@@ -343,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
|
||||
@@ -604,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
|
||||
@@ -1032,6 +1124,24 @@ public final class app/revanced/patches/songpal/badge/RemoveNotificationBadgePat
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/soundcloud/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/soundcloud/ad/HideAdsPatch;
|
||||
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/soundcloud/analytics/DisableTelemetryPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/soundcloud/analytics/DisableTelemetryPatch;
|
||||
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/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
|
||||
@@ -1080,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
|
||||
@@ -1700,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
|
||||
@@ -1766,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;
|
||||
}
|
||||
@@ -1978,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;
|
||||
@@ -1993,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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
kotlin.code.style = official
|
||||
version = 4.11.0-dev.2
|
||||
version = 4.12.0-dev.16
|
||||
|
||||
@@ -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]
|
||||
|
||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
2152
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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;"
|
||||
}
|
||||
}
|
||||
@@ -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."
|
||||
)
|
||||
}
|
||||
@@ -347,7 +347,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 ->
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package app.revanced.patches.bandcamp.limitations
|
||||
|
||||
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.bandcamp.limitations.fingerprints.HandlePlaybackLimitsPatch
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Remove play limits",
|
||||
description = "Disables purchase nagging and playback limits of not purchased tracks.",
|
||||
compatiblePackages = [CompatiblePackage("com.bandcamp.android")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object RemovePlayLimitsPatch : BytecodePatch(
|
||||
setOf(HandlePlaybackLimitsPatch),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) =
|
||||
HandlePlaybackLimitsPatch.result?.mutableMethod?.addInstructions(0, "return-void")
|
||||
?: throw HandlePlaybackLimitsPatch.exception
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package app.revanced.patches.bandcamp.limitations.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object HandlePlaybackLimitsPatch : MethodFingerprint(
|
||||
strings = listOf("play limits processing track", "found play_count"),
|
||||
)
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;") },
|
||||
)
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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",
|
||||
)
|
||||
@@ -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;")
|
||||
},
|
||||
)
|
||||
@@ -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"),
|
||||
)
|
||||
@@ -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),
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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",
|
||||
)
|
||||
@@ -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;")
|
||||
},
|
||||
)
|
||||
@@ -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),
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
@@ -12,7 +12,7 @@ import app.revanced.util.resultOrThrow
|
||||
|
||||
@Patch(
|
||||
name = "Remove background playback restrictions",
|
||||
description = "Removes restrictions on background playback.",
|
||||
description = "Removes restrictions on background playback, including playing kids videos in the background.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
)
|
||||
@@ -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"),
|
||||
)
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
)
|
||||
@@ -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")
|
||||
)
|
||||
@@ -22,5 +22,5 @@ object UnlockSubscriptionPatch : BytecodePatch(
|
||||
setOf(StartSubscriptionActivityFingerprint, BillingClientOnServiceConnected),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) =
|
||||
listOf(StartSubscriptionActivityFingerprint, BillingClientOnServiceConnected).returnEarly()
|
||||
setOf(StartSubscriptionActivityFingerprint, BillingClientOnServiceConnected).returnEarly()
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -53,7 +56,7 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
) : BytecodePatch(
|
||||
name = "GmsCore support",
|
||||
description = "Allows patched Google apps to run without root and under a different package name " +
|
||||
"by using GmsCore instead of Google Play Services.",
|
||||
"by using GmsCore instead of Google Play Services.",
|
||||
dependencies = setOf(
|
||||
ChangePackageNamePatch::class,
|
||||
gmsCoreSupportResourcePatch::class,
|
||||
@@ -62,6 +65,9 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
compatiblePackages = compatiblePackages,
|
||||
fingerprints = setOf(
|
||||
GmsCoreSupportFingerprint,
|
||||
GooglePlayUtilityFingerprint,
|
||||
ServiceCheckFingerprint,
|
||||
CastDynamiteModuleFingerprint,
|
||||
mainActivityOnCreateFingerprint,
|
||||
) + fingerprints,
|
||||
requiresIntegrations = true,
|
||||
@@ -91,16 +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 + ServiceCheckFingerprint + CastDynamiteModuleFingerprint).returnEarly()
|
||||
if (GooglePlayUtilityFingerprint.result != null) {
|
||||
GooglePlayUtilityFingerprint.returnEarly()
|
||||
}
|
||||
|
||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||
mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstructions(
|
||||
0,
|
||||
"invoke-static/range { p0 .. p0 }, Lapp/revanced/integrations/shared/GmsCoreSupport;->" +
|
||||
"checkGmsCore(Landroid/app/Activity;)V",
|
||||
"checkGmsCore(Landroid/app/Activity;)V",
|
||||
) ?: throw mainActivityOnCreateFingerprint.exception
|
||||
|
||||
// Change the vendor of GmsCore in ReVanced Integrations.
|
||||
@@ -192,7 +201,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 +314,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",
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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"),
|
||||
)
|
||||
@@ -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")
|
||||
)
|
||||
@@ -0,0 +1,72 @@
|
||||
package app.revanced.patches.soundcloud.ad
|
||||
|
||||
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.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
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.ad.fingerprints.InterceptFingerprint
|
||||
import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint
|
||||
import app.revanced.patches.soundcloud.ad.fingerprints.UserConsumerPlanConstructorFingerprint
|
||||
import app.revanced.util.resultOrThrow
|
||||
|
||||
@Patch(
|
||||
name = "Hide ads",
|
||||
compatiblePackages = [CompatiblePackage("com.soundcloud.android")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object HideAdsPatch : BytecodePatch(
|
||||
setOf(FeatureConstructorFingerprint, UserConsumerPlanConstructorFingerprint, InterceptFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// Enable a preset feature to disable audio ads 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, "no_audio_ads"
|
||||
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)),
|
||||
)
|
||||
}
|
||||
|
||||
// Overwrite the JSON response from the server to a paid plan, which hides all ads in the app.
|
||||
// This does not enable paid features, as they are all checked for on the backend.
|
||||
// This method is the constructor of a class representing a "UserConsumerPlan" object parsed from JSON data.
|
||||
// p1 is the "currentTier" value, dictating which features to enable in the app.
|
||||
// p4 is the "consumerPlanUpsells" value, a list of plans to try to sell to the user.
|
||||
// p5 is the "currentConsumerPlan" value, the type of plan currently subscribed to.
|
||||
// p6 is the "currentConsumerPlanTitle" value, the name of the plan currently subscribed to, shown to the user.
|
||||
UserConsumerPlanConstructorFingerprint.resultOrThrow().mutableMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
const-string p1, "high_tier"
|
||||
new-instance p4, Ljava/util/ArrayList;
|
||||
invoke-direct {p4}, Ljava/util/ArrayList;-><init>()V
|
||||
const-string p5, "go-plus"
|
||||
const-string p6, "SoundCloud Go+"
|
||||
""",
|
||||
)
|
||||
|
||||
// Prevent verification of an HTTP header containing the user's current plan, which would contradict the previous patch.
|
||||
InterceptFingerprint.resultOrThrow().let { result ->
|
||||
val conditionIndex = result.scanResult.patternScanResult!!.endIndex
|
||||
result.mutableMethod.addInstruction(
|
||||
conditionIndex,
|
||||
"return-object p1",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package app.revanced.patches.soundcloud.ad.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object InterceptFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC.value,
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
),
|
||||
strings = listOf("SC-Mob-UserPlan", "Configuration"),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "ApiUserPlanInterceptor.java"
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.soundcloud.ad.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object UserConsumerPlanConstructorFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("Ljava/lang/String;", "Z", "Ljava/lang/String;", "Ljava/util/List;", "Ljava/lang/String;", "Ljava/lang/String;"),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "UserConsumerPlan.kt"
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package app.revanced.patches.soundcloud.analytics
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.soundcloud.analytics.fingerprints.CreateTrackingApiFingerprint
|
||||
import app.revanced.util.resultOrThrow
|
||||
|
||||
@Patch(
|
||||
name = "Disable telemetry",
|
||||
description = "Disables SoundCloud's telemetry system.",
|
||||
compatiblePackages = [CompatiblePackage("com.soundcloud.android")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object DisableTelemetryPatch : BytecodePatch(
|
||||
setOf(CreateTrackingApiFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) =
|
||||
// Empty the "backend" argument to abort the initializer.
|
||||
CreateTrackingApiFingerprint.resultOrThrow()
|
||||
.mutableMethod.addInstruction(0, "const-string p1, \"\"")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package app.revanced.patches.soundcloud.analytics.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object CreateTrackingApiFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC.value,
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
classDef.sourceFile == "DefaultTrackingApiFactory.kt" && methodDef.name == "create"
|
||||
},
|
||||
)
|
||||
@@ -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, \"\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
)
|
||||
@@ -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"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.soundcloud.shared.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object FeatureConstructorFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("Ljava/lang/String;", "Z", "Ljava/util/List;"),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "Feature.kt"
|
||||
},
|
||||
)
|
||||
@@ -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
|
||||
""",
|
||||
)
|
||||
}
|
||||
@@ -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")
|
||||
)
|
||||
@@ -97,12 +97,12 @@ 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"),
|
||||
),
|
||||
),
|
||||
SwitchPreference("revanced_hide_emergency_box"),
|
||||
SwitchPreference("revanced_hide_expandable_chip"),
|
||||
SwitchPreference("revanced_hide_info_panels"),
|
||||
SwitchPreference("revanced_hide_join_membership_button"),
|
||||
SwitchPreference("revanced_disable_like_subscribe_glow"),
|
||||
@@ -143,6 +143,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
)
|
||||
|
||||
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
SwitchPreference("revanced_hide_expandable_chip"),
|
||||
SwitchPreference("revanced_hide_gray_separator"),
|
||||
PreferenceScreen(
|
||||
key = "revanced_custom_filter_screen",
|
||||
@@ -155,10 +156,6 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
),
|
||||
)
|
||||
|
||||
SettingsPatch.PreferenceScreen.VIDEO.addPreferences(
|
||||
SwitchPreference("revanced_hide_video_quality_menu_footer"),
|
||||
)
|
||||
|
||||
LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR)
|
||||
LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME)
|
||||
LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME)
|
||||
|
||||
@@ -74,6 +74,7 @@ object HidePlayerFlyoutMenuPatch : ResourcePatch() {
|
||||
SwitchPreference("revanced_hide_player_flyout_more_info"),
|
||||
SwitchPreference("revanced_hide_player_flyout_audio_track"),
|
||||
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
|
||||
SwitchPreference("revanced_hide_video_quality_menu_footer"),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,10 @@ 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.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint
|
||||
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackManagerFingerprint
|
||||
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackSettingsFingerprint
|
||||
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||
@@ -29,7 +27,6 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
PlayerTypeHookPatch::class,
|
||||
VideoInformationPatch::class,
|
||||
SettingsPatch::class,
|
||||
AddResourcesPatch::class
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
@@ -69,12 +66,6 @@ object BackgroundPlaybackPatch : BytecodePatch(
|
||||
"Lapp/revanced/integrations/youtube/patches/BackgroundPlaybackPatch;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AddResourcesPatch(this::class)
|
||||
|
||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||
NonInteractivePreference("revanced_background_playback")
|
||||
)
|
||||
|
||||
BackgroundPlaybackManagerFingerprint.resultOrThrow().mutableMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
@@ -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;"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
@@ -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
|
||||
""",
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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(
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -7,13 +7,18 @@ 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
|
||||
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.BuilderInstruction
|
||||
@@ -24,6 +29,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@Patch(
|
||||
description = "Hooks YouTube to get information about the current playing video.",
|
||||
@@ -32,22 +40,29 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
object VideoInformationPatch : BytecodePatch(
|
||||
setOf(
|
||||
PlayerInitFingerprint,
|
||||
MdxPlayerDirectorSetVideoStageFingerprint,
|
||||
CreateVideoPlayerSeekbarFingerprint,
|
||||
PlayerControllerSetTimeReferenceFingerprint,
|
||||
OnPlaybackSpeedItemClickFingerprint
|
||||
)
|
||||
) {
|
||||
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
|
||||
private var mdxInitInsertRegister = -1
|
||||
|
||||
private lateinit var timeMethod: MutableMethod
|
||||
private var timeInitInsertIndex = 2
|
||||
|
||||
private lateinit var speedSelectionInsertMethod: MutableMethod
|
||||
private var speedSelectionInsertIndex = 0
|
||||
private var speedSelectionValueRegister = 0
|
||||
private var speedSelectionInsertIndex = -1
|
||||
private var speedSelectionValueRegister = -1
|
||||
|
||||
// Used by other patches.
|
||||
internal lateinit var setPlaybackSpeedContainerClassFieldReference: String
|
||||
@@ -55,42 +70,47 @@ object VideoInformationPatch : BytecodePatch(
|
||||
internal lateinit var setPlaybackSpeedMethodReference: String
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
with(PlayerInitFingerprint.result!!) {
|
||||
|
||||
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) }.result!!.method
|
||||
val seekFingerprintResultMethod =
|
||||
SeekFingerprint.alsoResolve(context, PlayerInitFingerprint).method
|
||||
val seekRelativeFingerprintResultMethod =
|
||||
SeekRelativeFingerprint.alsoResolve(context, PlayerInitFingerprint).method
|
||||
|
||||
// create helper method
|
||||
val seekHelperMethod = ImmutableMethod(
|
||||
seekFingerprintResultMethod.definingClass,
|
||||
"seekTo",
|
||||
listOf(ImmutableMethodParameter("J", null, "time")),
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
null, null,
|
||||
MutableMethodImplementation(4)
|
||||
).toMutable()
|
||||
// Create integrations interface methods.
|
||||
addSeekInterfaceMethods(mutableClass, seekFingerprintResultMethod, seekRelativeFingerprintResultMethod)
|
||||
}
|
||||
|
||||
// get enum type for the seek helper method
|
||||
val seekSourceEnumType = seekFingerprintResultMethod.parameterTypes[1].toString()
|
||||
with(MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow()) {
|
||||
mdxInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
|
||||
|
||||
// insert helper method instructions
|
||||
seekHelperMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
||||
invoke-virtual {p0, p1, p2, v0}, ${seekFingerprintResultMethod.definingClass}->${seekFingerprintResultMethod.name}(J$seekSourceEnumType)Z
|
||||
move-result p1
|
||||
return p1
|
||||
"""
|
||||
)
|
||||
val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
|
||||
}
|
||||
mdxInitInsertRegister = mdxInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
|
||||
mdxInitInsertIndex = initThisIndex + 1
|
||||
|
||||
// add the seekTo method to the class for the integrations to call
|
||||
mutableClass.methods.add(seekHelperMethod)
|
||||
// Hook the MDX director for use through integrations.
|
||||
onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx")
|
||||
|
||||
val mdxSeekFingerprintResultMethod =
|
||||
MdxSeekFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
|
||||
val mdxSeekRelativeFingerprintResultMethod =
|
||||
MdxSeekRelativeFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
|
||||
|
||||
addSeekInterfaceMethods(mutableClass, mdxSeekFingerprintResultMethod, mdxSeekRelativeFingerprintResultMethod)
|
||||
}
|
||||
|
||||
with(CreateVideoPlayerSeekbarFingerprint.result!!) {
|
||||
@@ -103,7 +123,7 @@ object VideoInformationPatch : BytecodePatch(
|
||||
|
||||
addInstruction(
|
||||
videoLengthMethodResult.scanResult.patternScanResult!!.endIndex,
|
||||
"invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V"
|
||||
"invoke-static { v$videoLengthRegister, v$dummyRegisterForLong }, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -158,6 +178,44 @@ object VideoInformationPatch : BytecodePatch(
|
||||
userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
|
||||
}
|
||||
|
||||
private fun addSeekInterfaceMethods(targetClass: MutableClass, seekToMethod: Method, seekToRelativeMethod: Method) {
|
||||
// Add the interface and methods that integrations calls.
|
||||
targetClass.interfaces.add(INTEGRATIONS_PLAYER_INTERFACE)
|
||||
|
||||
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()
|
||||
|
||||
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.
|
||||
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) =
|
||||
addInstruction(insertIndex, "invoke-static { $register }, $descriptor")
|
||||
|
||||
@@ -176,8 +234,21 @@ 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"
|
||||
)
|
||||
|
||||
/**
|
||||
* Hook the MDX player director. Called when playing videos while casting to a big screen device.
|
||||
*
|
||||
* @param targetMethodClass The descriptor for the class to invoke when the player controller is created.
|
||||
* @param targetMethodName The name of the static method to invoke when the player controller is created.
|
||||
*/
|
||||
internal fun onCreateHookMdx(targetMethodClass: String, targetMethodName: String) =
|
||||
mdxInitMethod.insert(
|
||||
mdxInitInsertIndex++,
|
||||
"v$mdxInitInsertRegister",
|
||||
"$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package app.revanced.patches.youtube.video.information.fingerprints
|
||||
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object MdxPlayerDirectorSetVideoStageFingerprint : MethodFingerprint(
|
||||
strings = listOf("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state ")
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
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 MdxSeekFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Z",
|
||||
parameters = listOf("J", "L"),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.RETURN,
|
||||
),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
// The instruction count is necessary here to avoid matching the relative version
|
||||
// of the seek method we're after, which has the same function signature as the
|
||||
// regular one, is in the same class, and even has the exact same 3 opcodes pattern.
|
||||
methodDef.implementation!!.instructions.count() == 3
|
||||
}
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
@@ -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")
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
@@ -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()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -54,9 +57,12 @@
|
||||
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
||||
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
|
||||
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<!-- 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="ad.general.HideAdsResourcePatch">
|
||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 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="ad.getpremium.HideGetPremiumPatch">
|
||||
</patch>
|
||||
@@ -165,8 +171,6 @@
|
||||
<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. -->
|
||||
@@ -192,6 +196,8 @@
|
||||
</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. -->
|
||||
@@ -210,8 +216,6 @@
|
||||
</patch>
|
||||
<patch id="misc.links.OpenLinksExternallyPatch">
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
</patch>
|
||||
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -54,9 +57,12 @@
|
||||
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
||||
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
|
||||
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<!-- 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="ad.general.HideAdsResourcePatch">
|
||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 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="ad.getpremium.HideGetPremiumPatch">
|
||||
</patch>
|
||||
@@ -165,8 +171,6 @@
|
||||
<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. -->
|
||||
@@ -192,6 +196,8 @@
|
||||
</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. -->
|
||||
@@ -210,8 +216,6 @@
|
||||
</patch>
|
||||
<patch id="misc.links.OpenLinksExternallyPatch">
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
</patch>
|
||||
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -208,6 +211,9 @@
|
||||
<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>
|
||||
@@ -239,9 +245,11 @@
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">الكلمات المفتاحية والعبارات المراد إخفاؤها، مفصولة بأسطر جديد\n\nالكلمات التي تحتوي على أحرف كبيرة في الوسط يجب إدخالها مع التغليف (مثل: iPhone, TikTok, Leblanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">حول تصفية الكلمات المفتاحية</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">الصفحة الرئيسية/الاشتراك/نتائج البحث يتم تصفيتها لإخفاء المحتوى الذي يطابق عبارات الكلمات المفتاحية\n\nالتقييد\n• بعض المقاطع القصيرة قد لا تكون مخفية\n• بعض مكونات واجهة المستخدم قد لا تكون مخفية\n• البحث عن كلمة رئيسية قد لا يظهر أي نتائج</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_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>
|
||||
@@ -275,6 +283,7 @@
|
||||
<string name="revanced_hide_merchandise_banners_title">إخفاء لافتات البضائع</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">تم إخفاء لافتات البضائع</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">يتم عرض لافتات البضائع</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">إخفاء إعلانات ملء الشاشة يعمل فقط مع الأجهزة القديمة</string>
|
||||
</patch>
|
||||
<patch id="ad.getpremium.HideGetPremiumPatch">
|
||||
@@ -858,9 +867,7 @@
|
||||
<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>
|
||||
@@ -984,6 +991,11 @@
|
||||
<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>
|
||||
@@ -1049,10 +1061,6 @@
|
||||
<string name="revanced_external_browser_summary_on">فتح الروابط خارجيًا</string>
|
||||
<string name="revanced_external_browser_summary_off">فتح الروابط في التطبيق</string>
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
<string name="revanced_background_playback_title">التشغيل في الخلفية</string>
|
||||
<string name="revanced_background_playback_summary">يمكن العثور على هذا الإعداد في الإعدادات -> الخلفية</string>
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
<string name="revanced_remove_tracking_query_parameter_title">إزالة معلمة تتبع الاستعلام</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_on">يتم إزالة معلمة استعلام التتبع من الروابط</string>
|
||||
@@ -1110,8 +1118,8 @@
|
||||
<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• قد تكون جودة الفيديو الأعلى مفقودة\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>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -54,9 +57,12 @@
|
||||
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
||||
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
|
||||
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<!-- 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="ad.general.HideAdsResourcePatch">
|
||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 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="ad.getpremium.HideGetPremiumPatch">
|
||||
</patch>
|
||||
@@ -165,8 +171,6 @@
|
||||
<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. -->
|
||||
@@ -192,6 +196,8 @@
|
||||
</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. -->
|
||||
@@ -210,8 +216,6 @@
|
||||
</patch>
|
||||
<patch id="misc.links.OpenLinksExternallyPatch">
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
</patch>
|
||||
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -207,6 +210,8 @@
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">Ключови думи и фрази, които да бъдат скрити, разделени с нови редове\n\nДуми с главни букви в средата трябва да бъдат въведени с големи букви (например: iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">За филтриране с ключови думи</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Начало/Абонамент/Резултатите от търсенето се филтрират, за да се скрие съдържанието, което съответства на ключовите фрази\n\nОграничения\n• Някои кратки видеоклипове може да не са скрити\n• Някои компоненти на потребителския интерфейс може да не са скрити\n• Търсенето на ключова дума може да не покаже резултати</string>
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<!-- 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="ad.general.HideAdsResourcePatch">
|
||||
<string name="revanced_hide_general_ads_title">Скриване на общите реклами</string>
|
||||
@@ -234,6 +239,7 @@
|
||||
<string name="revanced_hide_merchandise_banners_title">Скриване на банерите за стоки</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">Банерите за стоки са скрити</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Банерите за стоки са показани</string>
|
||||
<!-- 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="ad.getpremium.HideGetPremiumPatch">
|
||||
<string name="revanced_hide_get_premium_title">Скриване на YouTube Premium промоции</string>
|
||||
@@ -417,8 +423,6 @@
|
||||
<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>
|
||||
@@ -457,6 +461,8 @@
|
||||
</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. -->
|
||||
@@ -476,8 +482,6 @@
|
||||
</patch>
|
||||
<patch id="misc.links.OpenLinksExternallyPatch">
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
</patch>
|
||||
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -52,7 +55,7 @@
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
<string name="revanced_settings_about_links_body">আপনি ReVanced প্যাচ সংস্করণ <i><i>%s</i></i> ব্যবহার করছেন</string>
|
||||
<string name="revanced_settings_about_links_body">আপনি ReVanced প্যাচ সংস্করণ <i>%s</i> ব্যবহার করছেন</string>
|
||||
<string name="revanced_settings_about_links_dev_header">দ্রষ্টব্য</string>
|
||||
<string name="revanced_settings_about_links_dev_body">এই সংস্করণ একটি প্রাক-প্রকাশনা এবং এতে আপনি অনাকাঙ্খিত সমস্যার সম্মুখিন হতে পারেন</string>
|
||||
<string name="revanced_settings_about_links_header">অফিশ্যাল লিংকসমূহ</string>
|
||||
@@ -66,7 +69,6 @@
|
||||
<string name="revanced_settings_screen_03_feed_title">ফিড</string>
|
||||
<string name="revanced_settings_screen_04_player_title">প্লেয়ার</string>
|
||||
<string name="revanced_settings_screen_05_general_title">সাধারণ লে-আউট</string>
|
||||
<string name="revanced_settings_screen_06_shorts_title">শর্টস</string>
|
||||
<string name="revanced_settings_screen_07_seekbar_title">সিকবার</string>
|
||||
<string name="revanced_settings_screen_08_swipe_controls_title">সোয়াইপ কন্ট্রোল</string>
|
||||
<string name="revanced_settings_screen_11_misc_title">বিবিধ</string>
|
||||
@@ -197,6 +199,8 @@
|
||||
<string name="revanced_hide_chips_shelf_title">চিপ শেলপ লুকান</string>
|
||||
<string name="revanced_hide_chips_shelf_summary_on">চিপ শেলপ লুকিয়ে রয়েছে</string>
|
||||
<string name="revanced_hide_chips_shelf_summary_off">চিপ শেলপ প্রদর্শিত হয়েছে</string>
|
||||
<string name="revanced_hide_attributes_section_title">বৈশিষ্ট্য বিভাগ লুকান</string>
|
||||
<string name="revanced_hide_attributes_section_summary_on">\'বৈশিষ্ট্যযুক্ত স্থান\', গেম এবং সঙ্গীত বিভাগগুলি লুকানো আছে</string>
|
||||
<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>
|
||||
@@ -230,7 +234,9 @@
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">লুকানোর জন্য কীওয়ার্ড এবং বাক্যাংশ, নতুন লাইনে পৃথক করা\n\nশব্দের মাঝে বড় হাতের অক্ষর থাকলে তা অবশ্যই সঠিক আবরণে লিখতে হবে (উদাহরণ: iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">কীওয়ার্ড ফিল্টারিং সম্পর্কে</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">প্রধান পাতা/সাবস্ক্রিপশন/অনুসন্ধান ফলাফল গুলো কীওয়ার্ড বাক্যাংশের সাথে মিলিয়ে লুকানোর জন্য ফিল্টার করা হয়েছে\n\nসীমাবদ্ধতা\n• কিছু Shorts নাও লুকানো হতে পারে\n• কিছু ইউআই উপাদান নাও লুকানো হতে পারে\n• কোন কীওয়ার্ড সার্চ করলে কোন ফলাফল নাও দেখাতে পারে</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_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>
|
||||
</patch>
|
||||
<patch id="ad.general.HideAdsResourcePatch">
|
||||
@@ -265,6 +271,7 @@
|
||||
<string name="revanced_hide_merchandise_banners_title">পণ্যদ্রব্য ব্যানার লুকান</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">পণ্যদ্রব্যের ব্যানার লুকিয়ে রয়েছে</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">পণ্যদ্রব্যের ব্যানার প্রদর্শিত হয়েছে</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">পূর্ণস্ক্রীন বিজ্ঞাপন লুকানো পুরোনো ডিভাইসে কাজ করে</string>
|
||||
</patch>
|
||||
<patch id="ad.getpremium.HideGetPremiumPatch">
|
||||
@@ -334,44 +341,24 @@
|
||||
<string name="revanced_swipe_save_and_restore_brightness_summary_on">পূর্ণস্ক্রিনে প্রবেশ বা পূর্ণস্ক্রিন থেকে বাহির হওয়ার সময়, উজ্জ্বলতা সংরক্ষণ এবং পুনরুদ্ধার করবে</string>
|
||||
<string name="revanced_swipe_save_and_restore_brightness_summary_off">পূর্ণস্ক্রিনে প্রবেশ বা পূর্ণস্ক্রিন থেকে বাহির হওয়ার সময়, উজ্জ্বলতা সংরক্ষণ এবং পুনরুদ্ধার করবে না</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">স্বয়ংক্রিয়-উজ্জ্বলতার অঙ্গভঙ্গি সক্রিয় করুন</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on"></string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off"></string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Hell</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title"></string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">The amount of milliseconds the overlay is visible</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">Swipe overlay text size</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">The text size for swipe overlay</string>
|
||||
<string name="revanced_swipe_overlay_background_alpha_title">Swipe background visibility</string>
|
||||
<string name="revanced_swipe_overlay_background_alpha_summary">The visibility of swipe overlay background</string>
|
||||
<string name="revanced_swipe_threshold_title">Swipe magnitude threshold</string>
|
||||
<string name="revanced_swipe_threshold_summary">The amount of threshold for swipe to occur</string>
|
||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">স্বতস্ফূর্তভাবে</string>
|
||||
<string name="revanced_swipe_overlay_timeout_title">ওভার-লে টাইম আউট</string>
|
||||
<string name="revanced_swipe_overlay_timeout_summary">কত মিলিসেকেন্ডের জন্য ওভারলে দৃশ্যমান হবে</string>
|
||||
<string name="revanced_swipe_text_overlay_size_title">সোয়াইপ ওভারলে টেক্সট সাইজ</string>
|
||||
<string name="revanced_swipe_text_overlay_size_summary">সোয়াইপ ওভারলে এর টেক্সট সাইজ</string>
|
||||
<string name="revanced_swipe_overlay_background_alpha_title">সোয়াইপ ব্যাকগ্রাউন্ডের দৃশ্যমানতা</string>
|
||||
<string name="revanced_swipe_overlay_background_alpha_summary">সোয়াইপ ওভারলে ব্যাকগ্রাউন্ডের দৃশ্যমানতা</string>
|
||||
<string name="revanced_swipe_threshold_title">সোয়াইপ থ্রেশহোল্ড এর মাত্রা</string>
|
||||
</patch>
|
||||
<patch id="layout.autocaptions.AutoCaptionsPatch">
|
||||
<string name="revanced_auto_captions_title">Disable auto captions</string>
|
||||
<string name="revanced_auto_captions_summary_on">Auto captions are disabled</string>
|
||||
<string name="revanced_auto_captions_summary_off">Auto captions are enabled</string>
|
||||
<string name="revanced_auto_captions_title">স্বয়ংক্রিয় ক্যাপশন বন্ধ করুন</string>
|
||||
</patch>
|
||||
<patch id="layout.buttons.action.HideButtonsPatch">
|
||||
<string name="revanced_hide_buttons_screen_title">Action buttons</string>
|
||||
<string name="revanced_hide_buttons_screen_summary">Hide or show buttons under videos</string>
|
||||
<string name="revanced_hide_like_dislike_button_title">Hide Like and Dislike</string>
|
||||
<string name="revanced_hide_like_dislike_button_summary_on">Like and Dislike buttons are hidden</string>
|
||||
<string name="revanced_hide_like_dislike_button_summary_off">Like and Dislike buttons are shown</string>
|
||||
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_share_button_title">Hide Share</string>
|
||||
<string name="revanced_hide_share_button_summary_on">Share button is hidden</string>
|
||||
<string name="revanced_hide_share_button_summary_off">Share button is hidden</string>
|
||||
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
|
||||
This button usually appears only on live streams. -->
|
||||
<string name="revanced_hide_report_button_title">Hide Report</string>
|
||||
<string name="revanced_hide_report_button_summary_on">Report button is hidden</string>
|
||||
<string name="revanced_hide_report_button_summary_off">Report button is shown</string>
|
||||
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_remix_button_title">Hide Remix</string>
|
||||
<string name="revanced_hide_remix_button_summary_on">Remix button is hidden</string>
|
||||
<string name="revanced_hide_remix_button_summary_off">Remix button is shown</string>
|
||||
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_download_button_title">Hide Download </string>
|
||||
<string name="revanced_hide_download_button_summary_on">ডাউনলোড বোতাম লুকানো আছে</string>
|
||||
<string name="revanced_hide_download_button_summary_off">ডাউনলোড বোতাম দেখানো হয়েছে</string>
|
||||
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
|
||||
@@ -379,29 +366,17 @@
|
||||
<string name="revanced_hide_thanks_button_summary_on">ধন্যবাদ বোতাম লুকানো আছে</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">ধন্যবাদ বোতাম দেখানো হয়</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Hide Clip</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Clip button is hidden</string>
|
||||
<string name="revanced_hide_clip_button_summary_off">Clip button is shown</string>
|
||||
<string name="revanced_hide_clip_button_title">ক্লিপ লুকান</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">ক্লিপ বোতাম লুকিয়ে রয়েছে</string>
|
||||
<string name="revanced_hide_clip_button_summary_off">ক্লিপ বোতাম প্রদর্শিত হয়েছে</string>
|
||||
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_playlist_button_title">Hide Save to playlist</string>
|
||||
<string name="revanced_hide_playlist_button_summary_on">Save to playlist button is hidden</string>
|
||||
<string name="revanced_hide_playlist_button_summary_off">Save to playlist button is shown</string>
|
||||
</patch>
|
||||
<patch id="layout.buttons.autoplay.HideAutoplayButtonPatch">
|
||||
<string name="revanced_hide_autoplay_button_title">Hide autoplay button</string>
|
||||
<string name="revanced_hide_autoplay_button_summary_on">Autoplay button is hidden</string>
|
||||
<string name="revanced_hide_autoplay_button_summary_off">Autoplay button is shown</string>
|
||||
</patch>
|
||||
<patch id="layout.buttons.captions.HideCaptionsButtonPatch">
|
||||
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
|
||||
<string name="revanced_hide_captions_button_title">Hide captions button</string>
|
||||
<string name="revanced_hide_captions_button_summary_on">Captions button is hidden</string>
|
||||
<string name="revanced_hide_captions_button_summary_off">Captions button is shown</string>
|
||||
</patch>
|
||||
<patch id="layout.buttons.cast.HideCastButtonPatch">
|
||||
<string name="revanced_hide_cast_button_title">Hide cast button</string>
|
||||
<string name="revanced_hide_cast_button_summary_on">Cast button is hidden</string>
|
||||
<string name="revanced_hide_cast_button_summary_off">Cast button is shown</string>
|
||||
</patch>
|
||||
<patch id="layout.buttons.navigation.NavigationButtonsPatch">
|
||||
<string name="revanced_navigation_buttons_screen_title">নেভিগেশন বোতাম</string>
|
||||
@@ -417,46 +392,20 @@
|
||||
<!-- The Create button has no display name. Translate normally. -->
|
||||
<string name="revanced_hide_create_button_title">লুকান তৈরি করুন</string>
|
||||
<string name="revanced_hide_create_button_summary_on">তৈরি বোতাম লুকানো আছে</string>
|
||||
<string name="revanced_hide_create_button_summary_off">Create button is shown</string>
|
||||
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
|
||||
<string name="revanced_hide_subscriptions_button_title">Hide Subscriptions</string>
|
||||
<string name="revanced_hide_subscriptions_button_summary_on">Subscriptions button is hidden</string>
|
||||
<string name="revanced_hide_subscriptions_button_summary_off">Subscriptions button is shown</string>
|
||||
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
|
||||
<string name="revanced_switch_create_with_notifications_button_title">Switch Create with Notifications</string>
|
||||
<string name="revanced_switch_create_with_notifications_button_summary_on">তৈরি করুন বোতাম বিজ্ঞপ্তিগুলো বোতাম দ্বারা স্থান পরিবর্তিত হয়েছে\n\nবিঃদ্রঃ এটি সক্রিয় করার ফলে ভিডিও বিজ্ঞাপন জোরপূর্বক বন্ধ থাকবে</string>
|
||||
<string name="revanced_switch_create_with_notifications_button_summary_off">Create button is not switched with Notifications button</string>
|
||||
<string name="revanced_hide_navigation_button_labels_title">নেভিগেশন বোতাম লেবেল লুকান</string>
|
||||
<string name="revanced_hide_navigation_button_labels_summary_on">লেবেল লুকিয়ে রয়েছে</string>
|
||||
<string name="revanced_hide_navigation_button_labels_summary_off">লেবেল প্রদর্শিত হয়েছে</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
|
||||
<string name="revanced_hide_player_flyout_title">Flyout menu</string>
|
||||
<string name="revanced_hide_player_flyout_summary">Hide or show player flyout menu items</string>
|
||||
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_captions_title">Hide Captions</string>
|
||||
<string name="revanced_hide_player_flyout_captions_summary_on">Captions menu is hidden</string>
|
||||
<string name="revanced_hide_player_flyout_captions_summary_off">Captions menu is shown</string>
|
||||
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_additional_settings_title">Hide Additional settings</string>
|
||||
<string name="revanced_hide_player_flyout_additional_settings_summary_on">Additional settings menu is hidden</string>
|
||||
<string name="revanced_hide_player_flyout_additional_settings_summary_off">Additional settings menu is shown</string>
|
||||
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_loop_video_title">Hide Loop video</string>
|
||||
<string name="revanced_hide_player_flyout_loop_video_summary_on">Loop video menu is hidden</string>
|
||||
<string name="revanced_hide_player_flyout_loop_video_summary_off">Loop video menu is shown</string>
|
||||
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_ambient_mode_title">Hide Ambient mode</string>
|
||||
<string name="revanced_hide_player_flyout_ambient_mode_summary_on">Ambient mode menu is hidden</string>
|
||||
<string name="revanced_hide_player_flyout_ambient_mode_summary_off">Ambient mode menu is shown</string>
|
||||
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_help_title">Hide Help & feedback</string>
|
||||
<string name="revanced_hide_player_flyout_help_summary_on">Help & feedback menu is hidden</string>
|
||||
<string name="revanced_hide_player_flyout_help_summary_off">Help & feedback menu is shown</string>
|
||||
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
|
||||
<string name="revanced_hide_player_flyout_speed_title">Hide Playback speed</string>
|
||||
<string name="revanced_hide_player_flyout_speed_summary_on">Playback speed menu is hidden</string>
|
||||
<string name="revanced_hide_player_flyout_speed_summary_off">Playback speed menu is shown</string>
|
||||
<!-- 'More info' should be translated using the same localized wording YouTube displays for the menu item.
|
||||
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
|
||||
<string name="revanced_hide_player_flyout_more_info_title">আরো তথ্য লুকান</string>
|
||||
@@ -481,9 +430,7 @@
|
||||
<string name="revanced_hide_player_buttons_summary_off">বোতাম দেখানো হয়</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.albumcards.AlbumCardsResourcePatch">
|
||||
<string name="revanced_hide_album_cards_title">Hide album cards</string>
|
||||
<string name="revanced_hide_album_cards_summary_on">Album cards are hidden</string>
|
||||
<string name="revanced_hide_album_cards_summary_off">Album cards are shown</string>
|
||||
<string name="revanced_hide_album_cards_summary_off">অ্যালবাম কার্ড প্রদর্শিত হয়েছে</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.comments.CommentsPatch">
|
||||
<string name="revanced_comments_screen_title">মন্তব্য</string>
|
||||
@@ -845,9 +792,6 @@
|
||||
<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>
|
||||
@@ -902,11 +846,11 @@
|
||||
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
|
||||
<string name="revanced_spoof_app_version_target_title">স্পুফ অ্যাপ সংস্করণ লক্ষ্য</string>
|
||||
<!-- 'RYD' is 'Return YouTube Dislike' -->
|
||||
<string name="revanced_spoof_app_version_target_entry_1">১৮.৩৩.৪০ - ছদ্মবেশি মোডে 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_1">18.33.40 - ছদ্মবেশি মোডে RYD পুনরুদ্ধার করে</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>
|
||||
@@ -972,6 +916,8 @@
|
||||
<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>
|
||||
@@ -1037,10 +983,6 @@
|
||||
<string name="revanced_external_browser_summary_on">লিংক বাহিরে খুলুন</string>
|
||||
<string name="revanced_external_browser_summary_off">অ্যাপের মধ্যে লিংক খুলছে</string>
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
<string name="revanced_background_playback_title">ব্যাকগ্রাউন্ড প্লেব্যাক</string>
|
||||
<string name="revanced_background_playback_summary">এই সেটিংটি পাওয়া যাবে সেটিং -> ব্যাকগ্রাউন্ড -এ</string>
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
<string name="revanced_remove_tracking_query_parameter_title">ট্র্যাকিং করার প্যারামিটার মুছুন</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_on">লিংক থেকে ট্র্যাকিং করার প্যারামিটার মুছে ফেলা হয়েছে</string>
|
||||
@@ -1098,7 +1040,6 @@
|
||||
<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_storyboard_timeout">ক্লায়েন্ট স্পুফ থাম্বনেইল সাময়িকভাবে উপলভ্য নয় (API সময় শেষ হয়েছে)</string>
|
||||
<string name="revanced_spoof_client_storyboard_io_exception">স্পুফ ক্লায়েন্ট থাম্বনেইল সাময়িকভাবে উপলভ্য নয়: %s</string>
|
||||
</patch>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -54,9 +57,12 @@
|
||||
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
||||
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
|
||||
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<!-- 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="ad.general.HideAdsResourcePatch">
|
||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 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="ad.getpremium.HideGetPremiumPatch">
|
||||
</patch>
|
||||
@@ -165,8 +171,6 @@
|
||||
<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. -->
|
||||
@@ -192,6 +196,8 @@
|
||||
</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. -->
|
||||
@@ -210,8 +216,6 @@
|
||||
</patch>
|
||||
<patch id="misc.links.OpenLinksExternallyPatch">
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
</patch>
|
||||
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -56,9 +59,12 @@
|
||||
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
|
||||
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
|
||||
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<!-- 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="ad.general.HideAdsResourcePatch">
|
||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||
<!-- 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="ad.getpremium.HideGetPremiumPatch">
|
||||
</patch>
|
||||
@@ -169,8 +175,6 @@
|
||||
<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. -->
|
||||
@@ -200,6 +204,8 @@
|
||||
</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. -->
|
||||
@@ -218,8 +224,6 @@
|
||||
</patch>
|
||||
<patch id="misc.links.OpenLinksExternallyPatch">
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
</patch>
|
||||
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -211,6 +214,8 @@
|
||||
<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_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>
|
||||
@@ -242,9 +247,11 @@
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">klíčová slova a fráze ke skrytí, odděleno novými řádky\n\nSlova s velkými písmeny uprostřed musí být zadána s pláštěm (ie: iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">O filtrování klíčových slov</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Výsledky domovského/předplatného/vyhledávání jsou filtrovány pro skrytí obsahu, který odpovídá výrazům klíčových slov\n\nOmezení\n• Některé krátkosti nemusí být skryty\n• Některé komponenty uživatelského rozhraní nemusí být skryté\n• Hledání klíčových slov nemusí zobrazovat žádné výsledky</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_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>
|
||||
@@ -278,6 +285,7 @@
|
||||
<string name="revanced_hide_merchandise_banners_title">Skrýt bannery zboží</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">Bannery zboží jsou skryty</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Bannery zboží jsou zobrazeny</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">Skrýt reklamy na celou obrazovku fungují pouze se staršími zařízeními</string>
|
||||
</patch>
|
||||
<patch id="ad.getpremium.HideGetPremiumPatch">
|
||||
@@ -861,9 +869,7 @@
|
||||
<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>
|
||||
@@ -988,6 +994,11 @@
|
||||
<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>
|
||||
@@ -1053,10 +1064,6 @@
|
||||
<string name="revanced_external_browser_summary_on">Otevírací odkazy vně</string>
|
||||
<string name="revanced_external_browser_summary_off">Otevírání odkazů v aplikaci</string>
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
<string name="revanced_background_playback_title">Přehrávání na pozadí</string>
|
||||
<string name="revanced_background_playback_summary">Toto nastavení lze nalézt v Nastavení -> Pozadí</string>
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
<string name="revanced_remove_tracking_query_parameter_title">Odstranit parametr dotaz pro sledování</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_on">Parametr dotaz na sledování je odstraněn z odkazů</string>
|
||||
@@ -1114,8 +1121,8 @@
|
||||
<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• Žádné HDR video\n• Vyšší vlastnosti videa mohou chybět\n• Živé streamy nemohou přehrávat pouze zvuk</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 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>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -210,6 +213,9 @@
|
||||
<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>
|
||||
@@ -241,9 +247,11 @@
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">Nøgleord og sætninger at skjule, adskilt af nye linjer\n\nOrd med store bogstaver i midten skal indtastes med casing (dvs. iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">Om søgeord filtrering</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Hjem/Abonnement/Søgeresultater filtreres for at skjule indhold, der matcher søgeordssætninger\n\nBegrænsninger\n• Nogle Shorts er muligvis ikke skjult\n• Nogle UI-komponenter er muligvis ikke skjult\n• Søger efter et søgeord, kan ikke vise nogen resultater</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_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>
|
||||
@@ -277,6 +285,7 @@
|
||||
<string name="revanced_hide_merchandise_banners_title">Skjul merchandise bannere</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">Varer bannere er skjult</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Varer bannere er vist</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">Skjul reklamer på fuld skærm virker kun med ældre enheder</string>
|
||||
</patch>
|
||||
<patch id="ad.getpremium.HideGetPremiumPatch">
|
||||
@@ -391,7 +400,6 @@
|
||||
<string name="revanced_hide_thanks_button_summary_on">Tak knappen er skjult</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Tak knappen er vist</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Hide Clip</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Klip knappen er skjult</string>
|
||||
<string name="revanced_hide_clip_button_summary_off">Klip knappen er vist</string>
|
||||
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
|
||||
@@ -574,11 +582,11 @@
|
||||
<!-- '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>
|
||||
@@ -860,9 +868,7 @@
|
||||
<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>
|
||||
@@ -986,6 +992,11 @@
|
||||
<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>
|
||||
@@ -1051,10 +1062,6 @@
|
||||
<string name="revanced_external_browser_summary_on">Åbning af links eksternt</string>
|
||||
<string name="revanced_external_browser_summary_off">Åbner links i appen</string>
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
<string name="revanced_background_playback_title">Baggrunds afspilning</string>
|
||||
<string name="revanced_background_playback_summary">Denne indstilling kan findes i Indstillinger -> Baggrund</string>
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
<string name="revanced_remove_tracking_query_parameter_title">Fjern sporingsforespørgselsparameter</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_on">Sporingsparameteren er fjernet fra links</string>
|
||||
@@ -1112,8 +1119,8 @@
|
||||
<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• Ingen HDR-video\n• Højere videokvaliteter mangler muligvis\n• Live streams kan ikke kun afspilles som lyd</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 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>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -211,6 +214,9 @@
|
||||
<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>
|
||||
@@ -242,9 +248,11 @@
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">Stichwörter und zu versteckende Phrasen durch neue Zeilen\n\ngetrennte Wörter mit Großbuchstaben in der Mitte müssen mit dem Gehäuse eingegeben werden (z.B. iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">Über Keyword-Filterung</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Home/Abonnement/Suchergebnisse werden gefiltert, um Inhalte auszublenden, die den Schlüsselwörtern\n\nBeschränkungen\nentsprechen • Einige Shorts können nicht versteckt werden\n• Einige UI-Komponenten können nicht versteckt werden\n• Suche nach einem Schlüsselwort kann keine Ergebnisse zeigen</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_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>
|
||||
@@ -278,6 +286,7 @@
|
||||
<string name="revanced_hide_merchandise_banners_title">Merchandise-Banner ausblenden</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">Warenbanner sind versteckt</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Warenbanner werden angezeigt</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">Vollbild-Werbung ausblenden funktioniert nur mit älteren Geräten</string>
|
||||
</patch>
|
||||
<patch id="ad.getpremium.HideGetPremiumPatch">
|
||||
@@ -674,11 +683,12 @@
|
||||
<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>
|
||||
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
|
||||
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">Video neu laden, um mit Return YouTube Abgleich zu stimmen</string>
|
||||
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">Video neu laden, um mit Return YouTube Dislike abzustimmen</string>
|
||||
<string name="revanced_ryd_enable_summary_on">Dislikes werden angezeigt</string>
|
||||
<string name="revanced_ryd_enable_summary_off">Dislikes werden nicht angezeigt</string>
|
||||
<string name="revanced_ryd_shorts_title">Dislikes auf Shorts anzeigen</string>
|
||||
@@ -858,9 +868,7 @@
|
||||
<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>
|
||||
@@ -985,6 +993,11 @@
|
||||
<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>
|
||||
@@ -1050,10 +1063,6 @@
|
||||
<string name="revanced_external_browser_summary_on">Links extern öffnen</string>
|
||||
<string name="revanced_external_browser_summary_off">Öffne Links in der App</string>
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
<string name="revanced_background_playback_title">Hintergrundwiedergabe</string>
|
||||
<string name="revanced_background_playback_summary">Diese Einstellung kann in den Einstellungen gefunden werden -> Hintergrund</string>
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
<string name="revanced_remove_tracking_query_parameter_title">Tracking-Abfrageparameter entfernen</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_on">Tracking-Abfrageparameter wird von Links entfernt</string>
|
||||
@@ -1111,8 +1120,8 @@
|
||||
<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• Kein HDR-Video\n• Höhere Video-Qualitäten fehlen möglicherweise\n• Live-Streams können nicht nur als Audio abspielen</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 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>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Note: All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
|
||||
|
||||
# General guidelines and information for translating
|
||||
|
||||
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
|
||||
@@ -209,6 +212,9 @@
|
||||
<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>
|
||||
@@ -240,9 +246,11 @@
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">Λέξεις-κλειδιά και φράσεις προς απόκρυψη, διαχωρισμένες η μία κάτω από την άλλη\n\nΔώστε προσοχή στα πεζά και κεφάλαια γράμματα διότι ισχύει ευαισθησία πεζότητας (π.χ.: iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">Σχετικά με το φιλτράρισμα λέξεων-κλειδιών</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Οι καρτέλες «Αρχική», «Εγγραφές» και τα αποτελέσματα αναζήτησης φιλτράρονται για απόκρυψη περιεχομένου που ταιριάζει με τις λέξεις-κλειδιά\n\nΠεριορισμοί\n• Κάποια Shorts ενδέχεται να μην κρύβονται\n• Κάποια στοιχεία UI ενδέχεται να μην κρύβονται\n• Η αναζήτηση για μια λέξη-κλειδί ενδέχεται να μην εμφανίζει κανένα αποτέλεσμα</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_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>
|
||||
@@ -276,6 +284,7 @@
|
||||
<string name="revanced_hide_merchandise_banners_title">Εμβλήματα εμπορευμάτων</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">Κρυμμένα</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Εμφανίζονται</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">Η απόκρυψη διαφημίσεων πλήρους οθόνης λειτουργεί μόνο με παλαιότερες συσκευές</string>
|
||||
</patch>
|
||||
<patch id="ad.getpremium.HideGetPremiumPatch">
|
||||
@@ -859,9 +868,7 @@
|
||||
<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>
|
||||
@@ -986,6 +993,11 @@
|
||||
<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>
|
||||
@@ -1051,10 +1063,6 @@
|
||||
<string name="revanced_external_browser_summary_on">Οι σύνδεσμοι ανοίγουν εξωτερικά</string>
|
||||
<string name="revanced_external_browser_summary_off">Οι σύνδεσμοι ανοίγουν εντός της εφαρμογής</string>
|
||||
</patch>
|
||||
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
|
||||
<string name="revanced_background_playback_title">Αναπαραγωγή στο παρασκήνιο</string>
|
||||
<string name="revanced_background_playback_summary">Αυτή η ρύθμιση μπορεί να βρεθεί στις Ρυθμίσεις -> Παρασκήνιο</string>
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
<string name="revanced_remove_tracking_query_parameter_title">Καθαρισμός συνδέσμων κοινοποίησης</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_on">Η παράμετρος παρακολούθησης αφαιρείται από τους συνδέσμους στην κοινοποίηση</string>
|
||||
@@ -1112,7 +1120,7 @@
|
||||
<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_on">Ο πελάτης αυτή τη στιγμή παραποιείται ως iOS\n\nΟι παρενέργειες περιλαμβάνουν:\n• Δεν υπάρχει η ποιότητα HDR στα βίντεο\n• Μπορεί να λείπουν επιλογές για υψηλές αναλύσεις βίντεο\n• Δε γίνεται στις ζωντανές ροές η αναπαραγωγή μόνο του ήχου</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">Μικρογραφίες προεπισκόπησης μη διαθέσιμες (καθυστέρηση API)</string>
|
||||
<string name="revanced_spoof_client_storyboard_io_exception">Μικρογραφίες προεπισκόπησης προσωρινά μη διαθέσιμες: %s</string>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user