mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-25 18:34:07 +01:00
Compare commits
82 Commits
v4.12.0-de
...
v4.14.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5b6ef5d6f | ||
|
|
5b1e07d861 | ||
|
|
e3220cc10a | ||
|
|
02db9378ea | ||
|
|
f83e314dff | ||
|
|
d5e383b78a | ||
|
|
395e18d830 | ||
|
|
887684e7c7 | ||
|
|
2f7d751f9f | ||
|
|
4886a95713 | ||
|
|
58719239cf | ||
|
|
fcb68cc65e | ||
|
|
16217f012e | ||
|
|
d6f20ee67d | ||
|
|
bccd62e593 | ||
|
|
1322403698 | ||
|
|
a64270514f | ||
|
|
f5de555adf | ||
|
|
4c2ec2870c | ||
|
|
a73e2458e9 | ||
|
|
96e6f43ca0 | ||
|
|
f667d5a238 | ||
|
|
ff2c4564a0 | ||
|
|
b568207e49 | ||
|
|
70470a9162 | ||
|
|
9922f47a49 | ||
|
|
ed532eb528 | ||
|
|
74f3f82927 | ||
|
|
6544cd5fc6 | ||
|
|
4e323aa206 | ||
|
|
c1cee281ff | ||
|
|
0779f9fc5e | ||
|
|
0ee5cf98ab | ||
|
|
6bb848b991 | ||
|
|
188b66ffe7 | ||
|
|
a276425d83 | ||
|
|
e556c3f692 | ||
|
|
cb30248eab | ||
|
|
c5ce742ab4 | ||
|
|
bdd2f7cb0f | ||
|
|
b7600f448d | ||
|
|
5c4bf7676d | ||
|
|
fcd2f9b4c4 | ||
|
|
aa3487aa92 | ||
|
|
ada642f4a7 | ||
|
|
eac758588a | ||
|
|
5d047eae77 | ||
|
|
ed92bf1be6 | ||
|
|
c6318e890f | ||
|
|
7f3b1c54da | ||
|
|
3d0d94b6c8 | ||
|
|
b84e6afebd | ||
|
|
3eab130276 | ||
|
|
95f8e9b3a9 | ||
|
|
d7be94a193 | ||
|
|
e4e20bec6c | ||
|
|
b8bd63a34c | ||
|
|
8b602ca6be | ||
|
|
87eb83607c | ||
|
|
567121d641 | ||
|
|
45e4f70137 | ||
|
|
f814d87c17 | ||
|
|
d0e92b225e | ||
|
|
c64757f80a | ||
|
|
58b6f1fba0 | ||
|
|
630857ba16 | ||
|
|
82ae367946 | ||
|
|
95a7118dcf | ||
|
|
26449cf7c6 | ||
|
|
e4232b6c74 | ||
|
|
1cf25f9dc9 | ||
|
|
8038bd2e98 | ||
|
|
1e81d0c9f8 | ||
|
|
c48cedaddf | ||
|
|
4085d1f9dc | ||
|
|
eadbf5f459 | ||
|
|
b12b3a73a6 | ||
|
|
572a310589 | ||
|
|
2e8d5c61f8 | ||
|
|
025766bb42 | ||
|
|
e31966159a | ||
|
|
337bdc3d39 |
266
CHANGELOG.md
266
CHANGELOG.md
@@ -1,3 +1,269 @@
|
||||
# [4.14.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.14...v4.14.0-dev.15) (2024-09-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Fix issues related to playback by replace streaming data ([#3582](https://github.com/ReVanced/revanced-patches/issues/3582)) ([dfa94d7](https://github.com/ReVanced/revanced-patches/commit/dfa94d70f65150d6ef24ea6378b8e6a317055186))
|
||||
|
||||
# [4.14.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.13...v4.14.0-dev.14) (2024-09-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube Music:** Make working patches compatible with latest versions ([#3556](https://github.com/ReVanced/revanced-patches/issues/3556)) ([12f6f19](https://github.com/ReVanced/revanced-patches/commit/12f6f1966ad04631451940f7b64d785c3ef481a0))
|
||||
|
||||
# [4.14.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.12...v4.14.0-dev.13) (2024-09-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide Shorts components:** Hide 'Use this sound' button ([#3647](https://github.com/ReVanced/revanced-patches/issues/3647)) ([33fc090](https://github.com/ReVanced/revanced-patches/commit/33fc09061431d4aa457d743c09a0de31ec566df1))
|
||||
|
||||
# [4.14.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.11...v4.14.0-dev.12) (2024-09-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Soundcloud - Hide ads:** Support latest version ([#3628](https://github.com/ReVanced/revanced-patches/issues/3628)) ([66e7e33](https://github.com/ReVanced/revanced-patches/commit/66e7e33efce9b702fdfcc2b9803e9da8491c1f08))
|
||||
|
||||
# [4.14.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.10...v4.14.0-dev.11) (2024-09-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Sync for Reddit:** Rename patch to `Use /user/ endpoint` ([98ead49](https://github.com/ReVanced/revanced-patches/commit/98ead493380932cb105530f4ba992673fd364d82))
|
||||
|
||||
# [4.14.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.9...v4.14.0-dev.10) (2024-09-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Sync for Reddit:** Add `Fix /user/ endpoint` patch ([46d11f3](https://github.com/ReVanced/revanced-patches/commit/46d11f3530fcdae9ed08b7e93aac235638a92dff))
|
||||
|
||||
# [4.14.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.8...v4.14.0-dev.9) (2024-09-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Add donation link to settings about screen ([#3626](https://github.com/ReVanced/revanced-patches/issues/3626)) ([0684ab5](https://github.com/ReVanced/revanced-patches/commit/0684ab5f183631de5720352049cfd293daa58eb0))
|
||||
|
||||
# [4.14.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.7...v4.14.0-dev.8) (2024-09-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - SponsorBlock:** Add summary text to 'view my segments' button ([df80b9f](https://github.com/ReVanced/revanced-patches/commit/df80b9f92f0d981b9a40b7756d74f8ccc3dcb1e9))
|
||||
|
||||
# [4.14.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.6...v4.14.0-dev.7) (2024-09-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Check environment` patch ([#3610](https://github.com/ReVanced/revanced-patches/issues/3610)) ([fbcbdaf](https://github.com/ReVanced/revanced-patches/commit/fbcbdafa4938a35b5fdec46aae7b250a84b9c139))
|
||||
|
||||
# [4.14.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.5...v4.14.0-dev.6) (2024-09-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Change data directory location` patch ([#3602](https://github.com/ReVanced/revanced-patches/issues/3602)) ([5998029](https://github.com/ReVanced/revanced-patches/commit/59980292809cc0626bf49a160eeb05a1523c4eda))
|
||||
|
||||
# [4.14.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.4...v4.14.0-dev.5) (2024-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Pixiv - Hide ads:** Fix for latest version ([#3616](https://github.com/ReVanced/revanced-patches/issues/3616)) ([98956e8](https://github.com/ReVanced/revanced-patches/commit/98956e8f1a41347bb435720bbf984969469a7110))
|
||||
|
||||
# [4.14.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.3...v4.14.0-dev.4) (2024-09-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - ReturnYouTubeDislike:** Show estimated like count for videos with hidden likes ([#3601](https://github.com/ReVanced/revanced-patches/issues/3601)) ([005be82](https://github.com/ReVanced/revanced-patches/commit/005be82d71b2a42387b1b57035930b20f4663794))
|
||||
|
||||
# [4.14.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.2...v4.14.0-dev.3) (2024-08-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Keyword filter:** Add syntax to match whole keywords and not substrings ([#3592](https://github.com/ReVanced/revanced-patches/issues/3592)) ([f5fb351](https://github.com/ReVanced/revanced-patches/commit/f5fb3512cfafe214ba6a6d25ba0825ae1884a0ff))
|
||||
|
||||
# [4.14.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.14.0-dev.1...v4.14.0-dev.2) (2024-08-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Duolingo:** Add `Disable ads` and `Enable debug menu` patch ([#3422](https://github.com/ReVanced/revanced-patches/issues/3422)) ([d0a8599](https://github.com/ReVanced/revanced-patches/commit/d0a8599f76ce653e5d7c98069ad3c58b9ab9c5eb))
|
||||
|
||||
# [4.14.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.13.4-dev.2...v4.14.0-dev.1) (2024-08-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Spoof client:** Allow forcing AVC codec with iOS ([#3570](https://github.com/ReVanced/revanced-patches/issues/3570)) ([1a49d1f](https://github.com/ReVanced/revanced-patches/commit/1a49d1f3c2a343d05d0abc07c143add486246fd0))
|
||||
|
||||
## [4.13.4-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.13.4-dev.1...v4.13.4-dev.2) (2024-08-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - SponsorBlock:** Handle if the user enters an invalid number into any SB settings ([37b3dd1](https://github.com/ReVanced/revanced-patches/commit/37b3dd1e789f8bb16fa1b9dd582e39c89dbe730c))
|
||||
|
||||
## [4.13.4-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.13.3...v4.13.4-dev.1) (2024-08-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SwissID:** Rename `Remove Google Play Integrity Integrity check` to `Remove Google Play Integrity check` ([#3558](https://github.com/ReVanced/revanced-patches/issues/3558)) ([0f5a771](https://github.com/ReVanced/revanced-patches/commit/0f5a771a5cff5684b4a8fd317f4938fe2cf3cbbe))
|
||||
|
||||
## [4.13.3](https://github.com/ReVanced/revanced-patches/compare/v4.13.2...v4.13.3) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Remove translated string that breaks patching ([a48c2db](https://github.com/ReVanced/revanced-patches/commit/a48c2db53d84767c8fd5d569f9ce1c46c2bfd9a1))
|
||||
|
||||
## [4.13.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.13.2...v4.13.3-dev.1) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Remove translated string that breaks patching ([a48c2db](https://github.com/ReVanced/revanced-patches/commit/a48c2db53d84767c8fd5d569f9ce1c46c2bfd9a1))
|
||||
|
||||
## [4.13.2](https://github.com/ReVanced/revanced-patches/compare/v4.13.1...v4.13.2) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - GmsCore Support:** Fix patch exception by using correct patch offset ([#3543](https://github.com/ReVanced/revanced-patches/issues/3543)) ([b2b8454](https://github.com/ReVanced/revanced-patches/commit/b2b8454aa992bcb217fb03eb4de5532e0a9bd354))
|
||||
|
||||
## [4.13.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.13.1...v4.13.2-dev.1) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - GmsCore Support:** Fix patch exception by using correct patch offset ([#3543](https://github.com/ReVanced/revanced-patches/issues/3543)) ([b2b8454](https://github.com/ReVanced/revanced-patches/commit/b2b8454aa992bcb217fb03eb4de5532e0a9bd354))
|
||||
|
||||
## [4.13.1](https://github.com/ReVanced/revanced-patches/compare/v4.13.0...v4.13.1) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Check watch history domain name resolution:** Add compatibility field ([6c598f0](https://github.com/ReVanced/revanced-patches/commit/6c598f084ed90ee1318e4c66d8c1751c797b8e3b))
|
||||
|
||||
## [4.13.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.13.0...v4.13.1-dev.1) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Check watch history domain name resolution:** Add compatibility field ([6c598f0](https://github.com/ReVanced/revanced-patches/commit/6c598f084ed90ee1318e4c66d8c1751c797b8e3b))
|
||||
|
||||
# [4.13.0](https://github.com/ReVanced/revanced-patches/compare/v4.12.0...v4.13.0) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - GmsCore support:** Fix notifications not working by using the correct permissions ([19ddae2](https://github.com/ReVanced/revanced-patches/commit/19ddae2d15e513e18eb1556c468cd94bd197685b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Google Photos:** Add `Spoof features` patch ([#3459](https://github.com/ReVanced/revanced-patches/issues/3459)) ([7c218cd](https://github.com/ReVanced/revanced-patches/commit/7c218cd168aa72eb99bcb47d12dfa45616e8ad88))
|
||||
* **SCB Easy:** Remove broken `Remove debugging detection` patch ([#3518](https://github.com/ReVanced/revanced-patches/issues/3518)) ([f4e23cb](https://github.com/ReVanced/revanced-patches/commit/f4e23cbb8a24638318d8cee20a1991c51855d9d2))
|
||||
* **YouTube:** Add `Check watch history domain name resolution` patch ([#3537](https://github.com/ReVanced/revanced-patches/issues/3537)) ([2af1425](https://github.com/ReVanced/revanced-patches/commit/2af142525cda07a131335faadd4b3889979fd077))
|
||||
|
||||
# [4.13.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.13.0-dev.1...v4.13.0-dev.2) (2024-08-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Add `Check watch history domain name resolution` patch ([#3537](https://github.com/ReVanced/revanced-patches/issues/3537)) ([2af1425](https://github.com/ReVanced/revanced-patches/commit/2af142525cda07a131335faadd4b3889979fd077))
|
||||
|
||||
# [4.13.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.12.1-dev.1...v4.13.0-dev.1) (2024-08-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Google Photos:** Add `Spoof features` patch ([#3459](https://github.com/ReVanced/revanced-patches/issues/3459)) ([7c218cd](https://github.com/ReVanced/revanced-patches/commit/7c218cd168aa72eb99bcb47d12dfa45616e8ad88))
|
||||
* **SCB Easy:** Remove broken `Remove debugging detection` patch ([#3518](https://github.com/ReVanced/revanced-patches/issues/3518)) ([f4e23cb](https://github.com/ReVanced/revanced-patches/commit/f4e23cbb8a24638318d8cee20a1991c51855d9d2))
|
||||
|
||||
## [4.12.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.12.0...v4.12.1-dev.1) (2024-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - GmsCore support:** Fix notifications not working by using the correct permissions ([19ddae2](https://github.com/ReVanced/revanced-patches/commit/19ddae2d15e513e18eb1556c468cd94bd197685b))
|
||||
|
||||
# [4.12.0](https://github.com/ReVanced/revanced-patches/compare/v4.11.0...v4.12.0) (2024-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos - GmsCore support:** Fix by checking first if a method exists before trying to patch it ([acf38ca](https://github.com/ReVanced/revanced-patches/commit/acf38cafae5eb9896b43f3a6cbd808ac273cd081))
|
||||
* **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))
|
||||
* **Messenger - Disable switching emoji to sticker:** Constrain to last working version `439.0.0.29.119` ([6207c31](https://github.com/ReVanced/revanced-patches/commit/6207c314c657a1188d1081b0a196a61e49cad83b))
|
||||
* **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))
|
||||
* **YouTube - Bypass image region restrictions:** Move setting to `Misc` menu ([094ae59](https://github.com/ReVanced/revanced-patches/commit/094ae59fc92663fff6c5d6f5cbece41822a326f9))
|
||||
* **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))
|
||||
* **YouTube - Hide keyword content:** Do not hide flyout menu ([687c9f7](https://github.com/ReVanced/revanced-patches/commit/687c9f7eb03cca5f7b3486f07f2e3453ebc77faf))
|
||||
* **YouTube - SponsorBlock:** Correctly show minute timestamp when creating a new segment ([d74c366](https://github.com/ReVanced/revanced-patches/commit/d74c366dbf5f25c20fbfc5a0157c3c15dda82a16))
|
||||
* **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))
|
||||
* **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))
|
||||
* **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))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Hide mock location` patch ([#3417](https://github.com/ReVanced/revanced-patches/issues/3417)) ([5f81b40](https://github.com/ReVanced/revanced-patches/commit/5f81b40e7d5567fb5689d08ccc9caeaa267c3143))
|
||||
* Add `Spoof build info` patch ([e7829b4](https://github.com/ReVanced/revanced-patches/commit/e7829b41e782c9feda23b9d6acf48bae277d24d9))
|
||||
* **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))
|
||||
* **CandyLink:** Remove non-functional `Unlock pro` patch ([7ae9f8f](https://github.com/ReVanced/revanced-patches/commit/7ae9f8fa0a349b91853e9554f18e564ca6ff887c))
|
||||
* **Expense Manager:** Remove non-functional `Unlock pro` patch ([ebbcac7](https://github.com/ReVanced/revanced-patches/commit/ebbcac74fd8598daebb4be0bd7c430c41333e2d4))
|
||||
* **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))
|
||||
* **Google Photos:** Add `GmsCore support` patch ([#3414](https://github.com/ReVanced/revanced-patches/issues/3414)) ([24528e0](https://github.com/ReVanced/revanced-patches/commit/24528e0a6eec17ce0a3c52f8862585933615ad28))
|
||||
* **Instagram:** Remove unnecessary `Hide timeline ads` patch ([5e1d001](https://github.com/ReVanced/revanced-patches/commit/5e1d001056df68e1e2b39f1365215c91bcc9e46b))
|
||||
* **SoundCloud:** Add `Enable offline sync` patch ([#3407](https://github.com/ReVanced/revanced-patches/issues/3407)) ([4de86c6](https://github.com/ReVanced/revanced-patches/commit/4de86c6407376bcd3cc0513a2f0707410b8d7ccd))
|
||||
* **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))
|
||||
* **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))
|
||||
* **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.17](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.16...v4.12.0-dev.17) (2024-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos - GmsCore support:** Fix by checking first if a method exists before trying to patch it ([acf38ca](https://github.com/ReVanced/revanced-patches/commit/acf38cafae5eb9896b43f3a6cbd808ac273cd081))
|
||||
* **Messenger - Disable switching emoji to sticker:** Constrain to last working version `439.0.0.29.119` ([6207c31](https://github.com/ReVanced/revanced-patches/commit/6207c314c657a1188d1081b0a196a61e49cad83b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **CandyLink:** Remove non-functional `Unlock pro` patch ([7ae9f8f](https://github.com/ReVanced/revanced-patches/commit/7ae9f8fa0a349b91853e9554f18e564ca6ff887c))
|
||||
* **Expense Manager:** Remove non-functional `Unlock pro` patch ([ebbcac7](https://github.com/ReVanced/revanced-patches/commit/ebbcac74fd8598daebb4be0bd7c430c41333e2d4))
|
||||
* **Instagram:** Remove unnecessary `Hide timeline ads` patch ([5e1d001](https://github.com/ReVanced/revanced-patches/commit/5e1d001056df68e1e2b39f1365215c91bcc9e46b))
|
||||
|
||||
# [4.12.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.15...v4.12.0-dev.16) (2024-08-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof client:** Restore livestream audio only playback with iOS spoofing ([#3504](https://github.com/ReVanced/revanced-patches/issues/3504)) ([90d3288](https://github.com/ReVanced/revanced-patches/commit/90d32880906787d82c4b9a7a1099b46dff3a0870))
|
||||
|
||||
# [4.12.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.14...v4.12.0-dev.15) (2024-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - SponsorBlock:** Improve create segment manual seek accuracy ([#3491](https://github.com/ReVanced/revanced-patches/issues/3491)) ([1544981](https://github.com/ReVanced/revanced-patches/commit/15449819ff74b636fb2fa6aacd770142c51d2e5d))
|
||||
|
||||
# [4.12.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.13...v4.12.0-dev.14) (2024-08-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Description components:** Add `Hide 'Key concepts' section` option ([#3495](https://github.com/ReVanced/revanced-patches/issues/3495)) ([d75b645](https://github.com/ReVanced/revanced-patches/commit/d75b64595a7ac26faca4c0ae21923b22f6783975))
|
||||
|
||||
# [4.12.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.12.0-dev.12...v4.12.0-dev.13) (2024-07-31)
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,14 @@ public final class app/revanced/patches/all/connectivity/wifi/spoof/SpoofWifiPat
|
||||
public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/directory/ChangeDataDirectoryLocationPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/all/directory/ChangeDataDirectoryLocationPatch;
|
||||
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)Ljava/lang/Integer;
|
||||
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 transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V
|
||||
public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/interaction/gestures/PredictiveBackGesturePatch : app/revanced/patcher/patch/ResourcePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/all/interaction/gestures/PredictiveBackGesturePatch;
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
@@ -243,6 +251,18 @@ public final class app/revanced/patches/cieid/restrictions/root/BypassRootChecks
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/duolingo/ad/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/duolingo/ad/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/duolingo/debug/EnableDebugMenuPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/duolingo/debug/EnableDebugMenuPatch;
|
||||
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/facebook/ads/story/HideStoryAdsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/facebook/ads/story/HideStoryAdsPatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
@@ -279,6 +299,16 @@ public final class app/revanced/patches/googlenews/misc/integrations/Integration
|
||||
public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/integrations/IntegrationsPatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/googlephotos/features/SpoofFeaturesPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/googlephotos/features/SpoofFeaturesPatch;
|
||||
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/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
|
||||
public static final field INSTANCE Lapp/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -423,6 +453,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
|
||||
@@ -792,6 +828,12 @@ public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/s
|
||||
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/user/UseUserEndpointPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/user/UseUserEndpointPatch;
|
||||
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/syncforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch;
|
||||
}
|
||||
@@ -826,6 +868,12 @@ public final class app/revanced/patches/serviceportalbund/detection/root/RootDet
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public fun <init> (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch;)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/shared/misc/fix/verticalscroll/VerticalScrollPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
@@ -1834,6 +1882,10 @@ public final class app/revanced/patches/youtube/misc/backgroundplayback/Backgrou
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch : app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/check/CheckEnvironmentPatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/debugging/DebuggingPatch : app/revanced/patcher/patch/ResourcePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/debugging/DebuggingPatch;
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
@@ -1864,6 +1916,12 @@ public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignature
|
||||
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch;
|
||||
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/fix/playback/UserAgentClientSpoofPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch;
|
||||
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;
|
||||
@@ -2104,6 +2162,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;
|
||||
|
||||
@@ -31,6 +31,8 @@ dependencies {
|
||||
implementation(libs.guava)
|
||||
// Used in JsonGenerator.
|
||||
implementation(libs.gson)
|
||||
// Android API stubs defined here.
|
||||
compileOnly(project(":stub"))
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
kotlin.code.style = official
|
||||
version = 4.12.0-dev.13
|
||||
version = 4.14.0-dev.15
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,3 +5,5 @@ buildCache {
|
||||
isEnabled = "CI" !in System.getenv()
|
||||
}
|
||||
}
|
||||
|
||||
include(":stub")
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package app.revanced.patches.all.directory
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
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.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.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
@Patch(
|
||||
name = "Change data directory location",
|
||||
description = "Changes the data directory in the application from " +
|
||||
"the app internal storage directory to /sdcard/android/data accessible by root-less devices." +
|
||||
"Using this patch can cause unexpected issues with some apps.",
|
||||
use = false,
|
||||
)
|
||||
@Suppress("unused")
|
||||
object ChangeDataDirectoryLocationPatch : BaseTransformInstructionsPatch<Int>() {
|
||||
override fun filterMap(
|
||||
classDef: ClassDef,
|
||||
method: Method,
|
||||
instruction: Instruction,
|
||||
instructionIndex: Int,
|
||||
): Int? {
|
||||
val reference = instruction.getReference<MethodReference>() ?: return null
|
||||
|
||||
if (!MethodUtil.methodSignaturesMatch(reference, MethodCall.GetDir.reference)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return instructionIndex
|
||||
}
|
||||
|
||||
override fun transform(
|
||||
mutableMethod: MutableMethod,
|
||||
entry: Int,
|
||||
) = transformMethodCall(entry, mutableMethod)
|
||||
|
||||
private fun transformMethodCall(
|
||||
instructionIndex: Int,
|
||||
mutableMethod: MutableMethod,
|
||||
) {
|
||||
val getDirInstruction = mutableMethod.getInstruction<Instruction35c>(instructionIndex)
|
||||
val contextRegister = getDirInstruction.registerC
|
||||
val dataRegister = getDirInstruction.registerD
|
||||
|
||||
mutableMethod.replaceInstruction(
|
||||
instructionIndex,
|
||||
"invoke-virtual { v$contextRegister, v$dataRegister }, " +
|
||||
"Landroid/content/Context;->getExternalFilesDir(Ljava/lang/String;)Ljava/io/File;",
|
||||
)
|
||||
}
|
||||
|
||||
private enum class MethodCall(
|
||||
val reference: MethodReference,
|
||||
) {
|
||||
GetDir(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/content/Context;",
|
||||
"getDir",
|
||||
listOf("Ljava/lang/String;", "I"),
|
||||
"Ljava/io/File;",
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,9 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||
"eu-rES" to "eu",
|
||||
"fa-rIR" to "fa",
|
||||
"fi-rFI" to "fi",
|
||||
"tl-rPH" to "tl",
|
||||
"fil-rPH" to "tl",
|
||||
"fr-rFR" to "fr",
|
||||
"ga-rIE" to "ga",
|
||||
"gl-rES" to "gl",
|
||||
"gu-rIN" to "gu",
|
||||
"hi-rIN" to "hi",
|
||||
@@ -125,6 +126,7 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||
"sk-rSK" to "sk",
|
||||
"sl-rSI" to "sl",
|
||||
"sq-rAL" to "sq",
|
||||
"sr-rCS" to "b+sr+Latn",
|
||||
"sr-rSP" to "sr",
|
||||
"sv-rSE" to "sv",
|
||||
"sw-rKE" to "sw",
|
||||
@@ -138,7 +140,6 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||
"uz-rUZ" to "uz",
|
||||
"vi-rVN" to "vi",
|
||||
"zh-rCN" to "zh-rCN",
|
||||
"zh-rHK" to "zh-rHK",
|
||||
"zh-rTW" to "zh-rTW",
|
||||
"zu-rZA" to "zu",
|
||||
)
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
package app.revanced.patches.candylinkvpn
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.candylinkvpn.fingerprints.IsPremiumPurchasedFingerprint
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Unlock pro",
|
||||
compatiblePackages = [CompatiblePackage("com.candylink.openvpn")]
|
||||
compatiblePackages = [CompatiblePackage("com.candylink.openvpn")],
|
||||
)
|
||||
@Deprecated(
|
||||
"This patch does not work anymore and will be removed in the future, " +
|
||||
"because the servers now check the purchase status.",
|
||||
)
|
||||
@Suppress("unused")
|
||||
object UnlockProPatch : BytecodePatch(
|
||||
setOf(IsPremiumPurchasedFingerprint)
|
||||
setOf(IsPremiumPurchasedFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
IsPremiumPurchasedFingerprint.result?.mutableMethod?.addInstructions(
|
||||
@@ -22,7 +25,7 @@ object UnlockProPatch : BytecodePatch(
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
"""
|
||||
""",
|
||||
) ?: throw IsPremiumPurchasedFingerprint.exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package app.revanced.patches.duolingo.ad
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
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.patches.duolingo.ad.fingerprints.InitializeMonetizationDebugSettingsFingerprint
|
||||
import app.revanced.util.resultOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
name = "Disable ads",
|
||||
compatiblePackages = [CompatiblePackage("com.duolingo")]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object DisableAdsPatch : BytecodePatch(
|
||||
setOf(InitializeMonetizationDebugSettingsFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// Couple approaches to remove ads exist:
|
||||
//
|
||||
// MonetizationDebugSettings has a boolean value for "disableAds".
|
||||
// OnboardingState has a getter to check if the user has any "adFreeSessions".
|
||||
// SharedPreferences has a debug boolean value with key "disable_ads", which maps to "DebugCategory.DISABLE_ADS".
|
||||
//
|
||||
// MonetizationDebugSettings seems to be the most general setting to work fine.
|
||||
InitializeMonetizationDebugSettingsFingerprint.resultOrThrow().let {
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"const/4 v$register, 0x1"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package app.revanced.patches.duolingo.ad.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 InitializeMonetizationDebugSettingsFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf(
|
||||
"Z", // disableAds
|
||||
"Z", // useDebugBilling
|
||||
"Z", // showManageSubscriptions
|
||||
"Z", // alwaysShowSuperAds
|
||||
"Lcom/duolingo/debug/FamilyQuestOverride;",
|
||||
),
|
||||
opcodes = listOf(
|
||||
Opcode.IPUT_BOOLEAN
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
package app.revanced.patches.duolingo.debug
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
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.patches.duolingo.debug.fingerprints.InitializeBuildConfigProviderFingerprint
|
||||
import app.revanced.util.resultOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
name = "Enable debug menu",
|
||||
compatiblePackages = [CompatiblePackage("com.duolingo", ["5.158.4"])],
|
||||
use = false
|
||||
)
|
||||
@Suppress("unused")
|
||||
object EnableDebugMenuPatch : BytecodePatch(
|
||||
setOf(InitializeBuildConfigProviderFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
InitializeBuildConfigProviderFingerprint.resultOrThrow().let {
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"const/4 v$register, 0x1"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package app.revanced.patches.duolingo.debug.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
|
||||
|
||||
/**
|
||||
* The `BuildConfigProvider` class has two booleans:
|
||||
*
|
||||
* - `isChina`: (usually) compares "play" with "china"...except for builds in China
|
||||
* - `isDebug`: compares "release" with "debug" <-- we want to force this to `true`
|
||||
*/
|
||||
internal object InitializeBuildConfigProviderFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
strings = listOf(
|
||||
"debug",
|
||||
"release",
|
||||
"china",
|
||||
),
|
||||
opcodes = listOf(
|
||||
Opcode.IPUT_BOOLEAN
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.googlephotos.features
|
||||
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.all.misc.build.BaseSpoofBuildInfoPatch
|
||||
|
||||
@Patch(description = "Spoof build info to Google Pixel XL.")
|
||||
internal class SpoofBuildInfoPatch : BaseSpoofBuildInfoPatch() {
|
||||
override val brand = "google"
|
||||
override val manufacturer = "Google"
|
||||
override val device = "marlin"
|
||||
override val product = "marlin"
|
||||
override val model = "Pixel XL"
|
||||
override val fingerprint = "google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys"
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package app.revanced.patches.googlephotos.features
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
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.patch.options.PatchOption.PatchExtensions.stringArrayPatchOption
|
||||
import app.revanced.patches.googlephotos.features.fingerprints.InitializeFeaturesEnumFingerprint
|
||||
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.StringReference
|
||||
|
||||
@Patch(
|
||||
name = "Spoof features",
|
||||
description = "Spoofs the device to enable Google Pixel exclusive features, including unlimited storage.",
|
||||
dependencies = [SpoofBuildInfoPatch::class],
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.photos")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprint)) {
|
||||
private val featuresToEnable by stringArrayPatchOption(
|
||||
"featuresToEnable",
|
||||
arrayOf(
|
||||
"com.google.android.apps.photos.NEXUS_PRELOAD",
|
||||
"com.google.android.apps.photos.nexus_preload",
|
||||
),
|
||||
title = "Features to enable",
|
||||
description = "Google Pixel exclusive features to enable. Features up to Pixel XL enable the unlimited storage feature.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
private val featuresToDisable by stringArrayPatchOption(
|
||||
"featuresToDisable",
|
||||
arrayOf(
|
||||
"com.google.android.apps.photos.PIXEL_2017_PRELOAD",
|
||||
"com.google.android.apps.photos.PIXEL_2018_PRELOAD",
|
||||
"com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD",
|
||||
"com.google.android.apps.photos.PIXEL_2019_PRELOAD",
|
||||
"com.google.android.feature.PIXEL_2020_MIDYEAR_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2020_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2021_MIDYEAR_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2021_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2022_MIDYEAR_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2022_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2023_MIDYEAR_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2023_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2024_MIDYEAR_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2024_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE",
|
||||
),
|
||||
title = "Features to disable",
|
||||
description = "Google Pixel exclusive features to disable." +
|
||||
"Features after Pixel XL may have to be disabled for unlimited storage depending on the device.",
|
||||
required = true,
|
||||
)
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val featuresToEnable = featuresToEnable!!.toSet()
|
||||
val featuresToDisable = featuresToDisable!!.toSet()
|
||||
|
||||
InitializeFeaturesEnumFingerprint.resultOrThrow().let { result ->
|
||||
result.mutableMethod.apply {
|
||||
getInstructions().filter { it.opcode == Opcode.CONST_STRING }.forEach {
|
||||
val feature = it.getReference<StringReference>()!!.string
|
||||
|
||||
val spoofedFeature = when (feature) {
|
||||
in featuresToEnable -> "android.hardware.wifi"
|
||||
in featuresToDisable -> "dummy"
|
||||
else -> return@forEach
|
||||
}
|
||||
|
||||
val constStringIndex = it.location.index
|
||||
val constStringRegister = (it as OneRegisterInstruction).registerA
|
||||
|
||||
replaceInstruction(
|
||||
constStringIndex,
|
||||
"const-string v$constStringRegister, \"$spoofedFeature\"",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package app.revanced.patches.googlephotos.features.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
object InitializeFeaturesEnumFingerprint : MethodFingerprint(
|
||||
strings = listOf("com.google.android.apps.photos.NEXUS_PRELOAD"),
|
||||
)
|
||||
@@ -10,7 +10,7 @@ import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Hide ads",
|
||||
description = "Hides ads in stories, discover, profile, etc." +
|
||||
description = "Hides ads in stories, discover, profile, etc. " +
|
||||
"An ad can still appear once when refreshing the home feed.",
|
||||
compatiblePackages = [CompatiblePackage("com.instagram.android")],
|
||||
)
|
||||
|
||||
@@ -14,9 +14,9 @@ import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
name = "Hide timeline ads",
|
||||
compatiblePackages = [CompatiblePackage("com.instagram.android")],
|
||||
)
|
||||
@Deprecated("This patch is not needed anymore.", replaceWith = ReplaceWith("HideAdsPatch"))
|
||||
@Suppress("unused")
|
||||
object HideTimelineAdsPatch : BytecodePatch(
|
||||
setOf(
|
||||
|
||||
@@ -13,7 +13,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
@Patch(
|
||||
name = "Disable switching emoji to sticker",
|
||||
description = "Disables switching from emoji to sticker search mode in message input field.",
|
||||
compatiblePackages = [CompatiblePackage("com.facebook.orca")],
|
||||
compatiblePackages = [CompatiblePackage("com.facebook.orca", ["439.0.0.29.119"])],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object DisableSwitchingEmojiToStickerPatch : BytecodePatch(
|
||||
|
||||
@@ -8,20 +8,20 @@ import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.moneymanager.fingerprints.UnlockProFingerprint
|
||||
|
||||
@Patch(
|
||||
name = "Unlock pro",
|
||||
compatiblePackages = [CompatiblePackage("com.ithebk.expensemanager")]
|
||||
compatiblePackages = [CompatiblePackage("com.ithebk.expensemanager")],
|
||||
)
|
||||
@Deprecated("This patch is not functional anymore and will be removed in the future.")
|
||||
@Suppress("unused")
|
||||
object UnlockProPatch : BytecodePatch(
|
||||
setOf(UnlockProFingerprint)
|
||||
){
|
||||
setOf(UnlockProFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
UnlockProFingerprint.result!!.mutableMethod.addInstructions(
|
||||
UnlockProFingerprint.result!!.mutableMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
"""
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,43 +6,43 @@ 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",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
],
|
||||
)
|
||||
@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,16 +12,7 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
@Patch(
|
||||
description = "Adds more audio codec options. The new audio codecs usually result in better audio quality.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
]
|
||||
)
|
||||
@Deprecated("This patch is no longer needed as the feature is now enabled by default.")
|
||||
|
||||
@@ -12,16 +12,7 @@ import app.revanced.util.exception
|
||||
name = "Enable exclusive audio playback",
|
||||
description = "Enables the option to play audio without video.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -14,16 +14,7 @@ import app.revanced.patches.music.interaction.permanentrepeat.fingerprints.Repea
|
||||
name = "Permanent repeat",
|
||||
description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
],
|
||||
use = false
|
||||
)
|
||||
|
||||
@@ -14,16 +14,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
name = "Hide category bar",
|
||||
description = "Hides the category bar at the top of the homepage.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
],
|
||||
use = false,
|
||||
)
|
||||
|
||||
@@ -17,16 +17,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
name = "Hide 'Get Music Premium' label",
|
||||
description = "Hides the \"Get Music Premium\" label from the account menu and settings.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -23,16 +23,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
name = "Remove upgrade button",
|
||||
description = "Removes the upgrade tab from the pivot bar.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -13,16 +13,7 @@ import app.revanced.patches.music.misc.androidauto.fingerprints.CheckCertificate
|
||||
name = "Bypass certificate checks",
|
||||
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -14,16 +14,7 @@ import app.revanced.util.resultOrThrow
|
||||
name = "Remove background playback restrictions",
|
||||
description = "Removes restrictions on background playback, including playing kids videos in the background.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
]
|
||||
)
|
||||
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -23,16 +23,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
||||
integrationsPatchDependency = IntegrationsPatch::class,
|
||||
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
||||
compatiblePackages = setOf(
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
setOf(
|
||||
"6.45.54",
|
||||
"6.51.53",
|
||||
"7.01.53",
|
||||
"7.02.52",
|
||||
"7.03.52",
|
||||
),
|
||||
),
|
||||
CompatiblePackage("com.google.android.apps.youtube.music"),
|
||||
),
|
||||
fingerprints = setOf(
|
||||
CastDynamiteModuleV2Fingerprint,
|
||||
|
||||
@@ -11,7 +11,7 @@ import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Spoof Android device ID",
|
||||
description = "Spoofs the Android device ID used by the app for account authentication." +
|
||||
description = "Spoofs the Android device ID used by the app for account authentication. " +
|
||||
"This can be used to copy the account to another device.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
package app.revanced.patches.pixiv.ads
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.pixiv.ads.fingerprints.IsNotPremiumFingerprint
|
||||
import app.revanced.patches.pixiv.ads.fingerprints.ShouldShowAdsFingerprint
|
||||
import app.revanced.util.exception
|
||||
|
||||
@Patch(
|
||||
name = "Hide ads",
|
||||
compatiblePackages = [CompatiblePackage("jp.pxv.android")]
|
||||
compatiblePackages = [CompatiblePackage("jp.pxv.android")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object HideAdsPatch : BytecodePatch(setOf(IsNotPremiumFingerprint)) {
|
||||
// Always return false in the "isNotPremium" method which normally returns !this.accountManager.isPremium.
|
||||
// However, this is not the method that controls the user's premium status.
|
||||
// Instead, this method is used to determine whether ads should be shown.
|
||||
object HideAdsPatch : BytecodePatch(setOf(ShouldShowAdsFingerprint)) {
|
||||
override fun execute(context: BytecodeContext) =
|
||||
IsNotPremiumFingerprint.result?.mutableClass?.virtualMethods?.first()?.addInstructions(
|
||||
ShouldShowAdsFingerprint.result?.mutableMethod?.addInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x0
|
||||
return v0
|
||||
"""
|
||||
) ?: throw IsNotPremiumFingerprint.exception
|
||||
}
|
||||
""",
|
||||
) ?: throw ShouldShowAdsFingerprint.exception
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package app.revanced.patches.pixiv.ads.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
|
||||
internal object IsNotPremiumFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
listOf("L"),
|
||||
strings = listOf("pixivAccountManager"),
|
||||
customFingerprint = custom@{ _, classDef ->
|
||||
// The "isNotPremium" method is the only method in the class.
|
||||
if (classDef.virtualMethods.count() != 1) return@custom false
|
||||
|
||||
classDef.virtualMethods.first().let { isNotPremiumMethod ->
|
||||
isNotPremiumMethod.parameterTypes.size == 0 && isNotPremiumMethod.returnType == "Z"
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.pixiv.ads.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
|
||||
internal object ShouldShowAdsFingerprint : MethodFingerprint(
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
classDef.type.endsWith("AdUtils;") && methodDef.name == "shouldShowAds"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,61 @@
|
||||
package app.revanced.patches.reddit.customclients.syncforreddit.fix.user
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
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.patches.reddit.customclients.syncforreddit.fix.user.fingerprints.*
|
||||
import app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints.OAuthFriendRequestFingerprint
|
||||
import app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints.OAuthSubredditInfoRequestHelperFingerprint
|
||||
import app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints.OAuthUnfriendRequestFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.resultOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
|
||||
@Patch(
|
||||
name = "Use /user/ endpoint",
|
||||
description = "Replaces the deprecated endpoint for viewing user profiles /u with /user, that used to fix a bug.",
|
||||
compatiblePackages = [
|
||||
CompatiblePackage("com.laurencedawson.reddit_sync"),
|
||||
CompatiblePackage("com.laurencedawson.reddit_sync.pro"),
|
||||
CompatiblePackage("com.laurencedawson.reddit_sync.dev"),
|
||||
],
|
||||
use = false,
|
||||
)
|
||||
@Suppress("unused")
|
||||
object UseUserEndpointPatch : BytecodePatch(
|
||||
fingerprints = setOf(
|
||||
OAuthFriendRequestFingerprint,
|
||||
OAuthSubredditInfoRequestConstructorFingerprint,
|
||||
OAuthSubredditInfoRequestHelperFingerprint,
|
||||
OAuthUnfriendRequestFingerprint,
|
||||
OAuthUserIdRequestFingerprint,
|
||||
OAuthUserInfoRequestFingerprint,
|
||||
),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
arrayOf(
|
||||
OAuthFriendRequestFingerprint,
|
||||
OAuthSubredditInfoRequestConstructorFingerprint,
|
||||
OAuthSubredditInfoRequestHelperFingerprint,
|
||||
OAuthUnfriendRequestFingerprint,
|
||||
OAuthUserIdRequestFingerprint,
|
||||
OAuthUserInfoRequestFingerprint,
|
||||
).map(MethodFingerprint::resultOrThrow).map {
|
||||
it.scanResult.stringsScanResult!!.matches.first().index to it.mutableMethod
|
||||
}.forEach { (userPathStringIndex, method) ->
|
||||
val userPathStringInstruction = method.getInstruction<OneRegisterInstruction>(userPathStringIndex)
|
||||
val userPathStringRegister = userPathStringInstruction.registerA
|
||||
val fixedUserPathString = userPathStringInstruction.getReference<StringReference>()!!.string.replace("u/", "user/")
|
||||
|
||||
method.replaceInstruction(
|
||||
userPathStringIndex,
|
||||
"const-string v$userPathStringRegister, \"${fixedUserPathString}\"",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal abstract class BaseUserEndpointFingerprint(source: String, accessFlags: Int? = null) :
|
||||
MethodFingerprint(
|
||||
accessFlags = accessFlags,
|
||||
strings = listOf("u/"),
|
||||
customFingerprint = { _, classDef -> classDef.sourceFile == source },
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
package app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints
|
||||
|
||||
internal object OAuthFriendRequestFingerprint : BaseUserEndpointFingerprint("OAuthFriendRequest.java")
|
||||
@@ -0,0 +1,10 @@
|
||||
package app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object OAuthSubredditInfoRequestConstructorFingerprint :
|
||||
BaseUserEndpointFingerprint(
|
||||
"OAuthSubredditInfoRequest.java",
|
||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object OAuthSubredditInfoRequestHelperFingerprint :
|
||||
BaseUserEndpointFingerprint(
|
||||
"OAuthSubredditInfoRequest.java",
|
||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
package app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints
|
||||
|
||||
internal object OAuthUnfriendRequestFingerprint : BaseUserEndpointFingerprint("OAuthUnfriendRequest.java")
|
||||
@@ -0,0 +1,3 @@
|
||||
package app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints
|
||||
|
||||
internal object OAuthUserIdRequestFingerprint : BaseUserEndpointFingerprint("OAuthUserIdRequest.java")
|
||||
@@ -0,0 +1,3 @@
|
||||
package app.revanced.patches.reddit.customclients.syncforreddit.fix.user.fingerprints
|
||||
|
||||
internal object OAuthUserInfoRequestFingerprint : BaseUserEndpointFingerprint("OAuthUserInfoRequest.java")
|
||||
@@ -10,11 +10,13 @@ import app.revanced.patches.scbeasy.detection.debugging.fingerprints.DebuggingDe
|
||||
|
||||
@Patch(
|
||||
use = false,
|
||||
name = "Remove debugging detection",
|
||||
description = "Removes the USB and wireless debugging checks.",
|
||||
compatiblePackages = [CompatiblePackage("com.scb.phone")]
|
||||
)
|
||||
@Suppress("unused")
|
||||
@Deprecated("This patch no longer work and will be removed in the future " +
|
||||
"due to the complexity of the application.\n" +
|
||||
"See https://github.com/ReVanced/revanced-patches/issues/3517 for more details.")
|
||||
object RemoveDebuggingDetectionPatch : BytecodePatch(
|
||||
setOf(DebuggingDetectionFingerprint)
|
||||
) {
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package app.revanced.patches.shared.misc.checks
|
||||
|
||||
import android.os.Build.*
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableLongEncodedValue
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableStringEncodedValue
|
||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.shared.misc.checks.fingerprints.PatchInfoBuildFingerprint
|
||||
import app.revanced.patches.shared.misc.checks.fingerprints.PatchInfoFingerprint
|
||||
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.resultOrThrow
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableLongEncodedValue
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableStringEncodedValue
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.MessageDigest
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
|
||||
abstract class BaseCheckEnvironmentPatch(
|
||||
private val mainActivityOnCreateFingerprint: MethodFingerprint,
|
||||
compatiblePackages: Set<CompatiblePackage>,
|
||||
integrationsPatch: BaseIntegrationsPatch,
|
||||
) : BytecodePatch(
|
||||
description = "Checks, if the application was patched by, otherwise warns the user.",
|
||||
compatiblePackages = compatiblePackages,
|
||||
dependencies = setOf(
|
||||
AddResourcesPatch::class,
|
||||
integrationsPatch::class,
|
||||
),
|
||||
fingerprints = setOf(
|
||||
PatchInfoFingerprint,
|
||||
PatchInfoBuildFingerprint,
|
||||
mainActivityOnCreateFingerprint,
|
||||
),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AddResourcesPatch(BaseCheckEnvironmentPatch::class)
|
||||
|
||||
setPatchInfo()
|
||||
invokeCheck()
|
||||
}
|
||||
|
||||
private fun setPatchInfo() {
|
||||
PatchInfoFingerprint.setClassFields(
|
||||
"PATCH_TIME" to System.currentTimeMillis().encoded,
|
||||
)
|
||||
|
||||
fun setBuildInfo() {
|
||||
PatchInfoBuildFingerprint.setClassFields(
|
||||
"PATCH_BOARD" to BOARD.encodedAndHashed,
|
||||
"PATCH_BOOTLOADER" to BOOTLOADER.encodedAndHashed,
|
||||
"PATCH_BRAND" to BRAND.encodedAndHashed,
|
||||
"PATCH_CPU_ABI" to CPU_ABI.encodedAndHashed,
|
||||
"PATCH_CPU_ABI2" to CPU_ABI2.encodedAndHashed,
|
||||
"PATCH_DEVICE" to DEVICE.encodedAndHashed,
|
||||
"PATCH_DISPLAY" to DISPLAY.encodedAndHashed,
|
||||
"PATCH_FINGERPRINT" to FINGERPRINT.encodedAndHashed,
|
||||
"PATCH_HARDWARE" to HARDWARE.encodedAndHashed,
|
||||
"PATCH_HOST" to HOST.encodedAndHashed,
|
||||
"PATCH_ID" to ID.encodedAndHashed,
|
||||
"PATCH_MANUFACTURER" to MANUFACTURER.encodedAndHashed,
|
||||
"PATCH_MODEL" to MODEL.encodedAndHashed,
|
||||
"PATCH_ODM_SKU" to ODM_SKU.encodedAndHashed,
|
||||
"PATCH_PRODUCT" to PRODUCT.encodedAndHashed,
|
||||
"PATCH_RADIO" to RADIO.encodedAndHashed,
|
||||
"PATCH_SKU" to SKU.encodedAndHashed,
|
||||
"PATCH_SOC_MANUFACTURER" to SOC_MANUFACTURER.encodedAndHashed,
|
||||
"PATCH_SOC_MODEL" to SOC_MODEL.encodedAndHashed,
|
||||
"PATCH_TAGS" to TAGS.encodedAndHashed,
|
||||
"PATCH_TYPE" to TYPE.encodedAndHashed,
|
||||
"PATCH_USER" to USER.encodedAndHashed,
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("android.os.Build")
|
||||
// This only works on Android,
|
||||
// because it uses Android APIs.
|
||||
setBuildInfo()
|
||||
} catch (_: ClassNotFoundException) { }
|
||||
}
|
||||
|
||||
private fun invokeCheck() = mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstructions(
|
||||
0,
|
||||
"invoke-static/range { p0 .. p0 },$INTEGRATIONS_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
|
||||
) ?: throw mainActivityOnCreateFingerprint.exception
|
||||
|
||||
private companion object {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/shared/checks/CheckEnvironmentPatch;"
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
private val String.encodedAndHashed
|
||||
get() = MutableStringEncodedValue(
|
||||
ImmutableStringEncodedValue(
|
||||
Base64.encode(MessageDigest.getInstance("SHA-1")
|
||||
.digest(this.toByteArray(StandardCharsets.UTF_8))),
|
||||
),
|
||||
)
|
||||
|
||||
private val Long.encoded get() = MutableLongEncodedValue(ImmutableLongEncodedValue(this))
|
||||
|
||||
private fun <T : MutableEncodedValue> MethodFingerprint.setClassFields(vararg fieldNameValues: Pair<String, T>) {
|
||||
val fieldNameValueMap = mapOf(*fieldNameValues)
|
||||
|
||||
resultOrThrow().mutableClass.fields.forEach { field ->
|
||||
field.initialValue = fieldNameValueMap[field.name] ?: return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package app.revanced.patches.shared.misc.checks.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object PatchInfoBuildFingerprint : MethodFingerprint(
|
||||
customFingerprint = { _, classDef -> classDef.type == "Lapp/revanced/integrations/shared/checks/PatchInfo\$Build;" },
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package app.revanced.patches.shared.misc.checks.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object PatchInfoFingerprint : MethodFingerprint(
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.type == "Lapp/revanced/integrations/shared/checks/PatchInfo;"
|
||||
},
|
||||
)
|
||||
@@ -18,11 +18,13 @@ import app.revanced.patches.shared.misc.gms.fingerprints.GooglePlayUtilityFinger
|
||||
import app.revanced.patches.shared.misc.gms.fingerprints.ServiceCheckFingerprint
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.returnEarly
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
@@ -56,7 +58,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,
|
||||
@@ -100,17 +102,33 @@ abstract class BaseGmsCoreSupportPatch(
|
||||
primeMethodFingerprint?.let { transformPrimeMethod(packageName) }
|
||||
|
||||
// Return these methods early to prevent the app from crashing.
|
||||
(earlyReturnFingerprints + ServiceCheckFingerprint + CastDynamiteModuleFingerprint).returnEarly()
|
||||
earlyReturnFingerprints.returnEarly()
|
||||
ServiceCheckFingerprint.returnEarly()
|
||||
// Not all apps have CastDynamiteModule, so we need to check if it's present.
|
||||
if (CastDynamiteModuleFingerprint.result != null) {
|
||||
CastDynamiteModuleFingerprint.returnEarly()
|
||||
}
|
||||
// Google Play Utility is not present in all apps, so we need to check if it's present.
|
||||
if (GooglePlayUtilityFingerprint.result != null) {
|
||||
GooglePlayUtilityFingerprint.returnEarly()
|
||||
}
|
||||
|
||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||
mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstructions(
|
||||
0,
|
||||
"invoke-static/range { p0 .. p0 }, Lapp/revanced/integrations/shared/GmsCoreSupport;->" +
|
||||
mainActivityOnCreateFingerprint.result?.mutableMethod?.apply {
|
||||
// Temporary fix for patches with an integrations patch that hook the onCreate method as well.
|
||||
val setContextIndex = indexOfFirstInstruction {
|
||||
val reference = getReference<MethodReference>() ?: return@indexOfFirstInstruction false
|
||||
|
||||
reference.toString() == "Lapp/revanced/integrations/shared/Utils;->setContext(Landroid/content/Context;)V"
|
||||
}
|
||||
|
||||
// Add after setContext call, because this patch needs the context.
|
||||
addInstructions(
|
||||
if (setContextIndex < 0) 0 else setContextIndex + 1,
|
||||
"invoke-static/range { p0 .. p0 }, Lapp/revanced/integrations/shared/GmsCoreSupport;->" +
|
||||
"checkGmsCore(Landroid/app/Activity;)V",
|
||||
) ?: throw mainActivityOnCreateFingerprint.exception
|
||||
)
|
||||
} ?: throw mainActivityOnCreateFingerprint.exception
|
||||
|
||||
// Change the vendor of GmsCore in ReVanced Integrations.
|
||||
GmsCoreSupportFingerprint.result?.mutableClass?.methods
|
||||
|
||||
@@ -101,8 +101,8 @@ abstract class BaseGmsCoreSupportResourcePatch(
|
||||
"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",
|
||||
"com.google.android.c2dm" to "$gmsCoreVendorGroupId.android.c2dm",
|
||||
"com.google.android.libraries.photos.api.mars" to "$gmsCoreVendorGroupId.android.apps.photos.api.mars",
|
||||
"</queries>" to "<package android:name=\"$gmsCoreVendorGroupId.android.gms\"/></queries>",
|
||||
)
|
||||
|
||||
@@ -110,9 +110,9 @@ abstract class BaseGmsCoreSupportResourcePatch(
|
||||
transformations.entries.fold(get("AndroidManifest.xml", false).readText()) { acc, (from, to) ->
|
||||
acc.replace(
|
||||
from,
|
||||
to
|
||||
to,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ object HideAdsPatch : BytecodePatch(
|
||||
|
||||
// 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
|
||||
val conditionIndex = result.scanResult.patternScanResult!!.endIndex + 1
|
||||
result.mutableMethod.addInstruction(
|
||||
conditionIndex,
|
||||
"return-object p1",
|
||||
|
||||
@@ -9,14 +9,13 @@ internal object InterceptFingerprint : MethodFingerprint(
|
||||
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,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
),
|
||||
strings = listOf("SC-Mob-UserPlan", "Configuration"),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "ApiUserPlanInterceptor.java"
|
||||
classDef.sourceFile == "ApiUserPlanInterceptor.java" ||
|
||||
classDef.sourceFile == "ApiUserPlanInterceptor.kt"
|
||||
},
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ import app.revanced.patches.swissid.integritycheck.fingerprints.CheckIntegrityFi
|
||||
import app.revanced.util.resultOrThrow
|
||||
|
||||
@Patch(
|
||||
name = "Remove Google Play Integrity Integrity check",
|
||||
name = "Remove Google Play 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")],
|
||||
|
||||
@@ -97,6 +97,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
SwitchPreference("revanced_hide_attributes_section"),
|
||||
SwitchPreference("revanced_hide_chapters_section"),
|
||||
SwitchPreference("revanced_hide_info_cards_section"),
|
||||
SwitchPreference("revanced_hide_key_concepts_section"),
|
||||
SwitchPreference("revanced_hide_podcast_section"),
|
||||
SwitchPreference("revanced_hide_transcript_section"),
|
||||
),
|
||||
@@ -137,8 +138,10 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
||||
SwitchPreference("revanced_hide_keyword_content_search"),
|
||||
TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE),
|
||||
NonInteractivePreference("revanced_hide_keyword_content_about"),
|
||||
),
|
||||
),
|
||||
NonInteractivePreference(key = "revanced_hide_keyword_content_about_whole_words",
|
||||
tag = "app.revanced.integrations.youtube.settings.preference.HtmlPreference")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
|
||||
@@ -38,6 +38,7 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
|
||||
SwitchPreference("revanced_hide_shorts_subscribe_button"),
|
||||
SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"),
|
||||
SwitchPreference("revanced_hide_shorts_save_sound_button"),
|
||||
SwitchPreference("revanced_hide_shorts_use_this_sound_button"),
|
||||
SwitchPreference("revanced_hide_shorts_shop_button"),
|
||||
SwitchPreference("revanced_hide_shorts_tagged_products"),
|
||||
SwitchPreference("revanced_hide_shorts_search_suggestions"),
|
||||
|
||||
@@ -12,7 +12,7 @@ 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," +
|
||||
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,
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.check
|
||||
|
||||
import app.revanced.patches.shared.misc.checks.BaseCheckEnvironmentPatch
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint
|
||||
|
||||
@Suppress("unused")
|
||||
object CheckEnvironmentPatch :
|
||||
BaseCheckEnvironmentPatch(
|
||||
mainActivityOnCreateFingerprint = MainActivityOnCreateFingerprint,
|
||||
integrationsPatch = IntegrationsPatch,
|
||||
compatiblePackages = setOf(CompatiblePackage("com.google.android.youtube")),
|
||||
)
|
||||
@@ -0,0 +1,69 @@
|
||||
package app.revanced.patches.youtube.misc.dns
|
||||
|
||||
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.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint
|
||||
import app.revanced.util.resultOrThrow
|
||||
|
||||
@Patch(
|
||||
name = "Check watch history domain name resolution",
|
||||
description = "Checks if the device DNS server is preventing user watch history from being saved.",
|
||||
dependencies = [IntegrationsPatch::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")
|
||||
internal object CheckWatchHistoryDomainNameResolutionPatch : BytecodePatch(
|
||||
setOf(MainActivityOnCreateFingerprint),
|
||||
) {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/CheckWatchHistoryDomainNameResolutionPatch;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AddResourcesPatch(this::class)
|
||||
|
||||
MainActivityOnCreateFingerprint.resultOrThrow().mutableMethod.addInstructions(
|
||||
// FIXME: Insert index must be greater than the insert index used by GmsCoreSupport,
|
||||
// as both patch the same method and GmsCoreSupport check should be first,
|
||||
// but the patch does not depend on GmsCoreSupport, so it should not be possible to enforce this
|
||||
// unless a third patch is added that this patch and GmsCoreSupport depend on to manage
|
||||
// the order of the patches.
|
||||
1,
|
||||
"invoke-static/range { p0 .. p0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->checkDnsResolver(Landroid/app/Activity;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,385 +1,11 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback
|
||||
|
||||
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.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
|
||||
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.PreferenceScreen
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.*
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
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.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
|
||||
@Patch(
|
||||
name = "Spoof client",
|
||||
description = "Spoofs the client to allow video playback.",
|
||||
dependencies = [
|
||||
SettingsPatch::class,
|
||||
AddResourcesPatch::class,
|
||||
UserAgentClientSpoofPatch::class,
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
[
|
||||
"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",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
@Deprecated("This patch is obsolete.", replaceWith = ReplaceWith("SpoofVideoStreamsPatch"))
|
||||
object SpoofClientPatch : BytecodePatch(
|
||||
setOf(
|
||||
// Client type spoof.
|
||||
BuildInitPlaybackRequestFingerprint,
|
||||
BuildPlayerRequestURIFingerprint,
|
||||
SetPlayerRequestClientTypeFingerprint,
|
||||
CreatePlayerRequestBodyFingerprint,
|
||||
CreatePlayerRequestBodyWithModelFingerprint,
|
||||
CreatePlayerRequestBodyWithVersionReleaseFingerprint,
|
||||
|
||||
// Player gesture config.
|
||||
PlayerGestureConfigSyntheticFingerprint,
|
||||
|
||||
// Player speed menu item.
|
||||
CreatePlaybackSpeedMenuItemFingerprint,
|
||||
|
||||
// Video qualities missing.
|
||||
BuildRequestFingerprint,
|
||||
|
||||
// Watch history.
|
||||
GetTrackingUriFingerprint,
|
||||
),
|
||||
dependencies = setOf(SpoofVideoStreamsPatch::class),
|
||||
) {
|
||||
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)
|
||||
|
||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||
PreferenceScreen(
|
||||
key = "revanced_spoof_client_screen",
|
||||
sorting = PreferenceScreen.Sorting.UNSORTED,
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_spoof_client"),
|
||||
SwitchPreference("revanced_spoof_client_use_ios"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// region Block /initplayback requests to fall back to /get_watch requests.
|
||||
|
||||
BuildInitPlaybackRequestFingerprint.resultOrThrow().let {
|
||||
val moveUriStringIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(moveUriStringIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
moveUriStringIndex + 1,
|
||||
"""
|
||||
invoke-static { v$targetRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$targetRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Block /get_watch requests to fall back to /player requests.
|
||||
|
||||
BuildPlayerRequestURIFingerprint.resultOrThrow().let {
|
||||
val invokeToStringIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val uriRegister = getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
|
||||
|
||||
addInstructions(
|
||||
invokeToStringIndex,
|
||||
"""
|
||||
invoke-static { v$uriRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
|
||||
move-result-object v$uriRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Get field references to be used below.
|
||||
|
||||
val (clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField) =
|
||||
SetPlayerRequestClientTypeFingerprint.resultOrThrow().let { result ->
|
||||
// Field in the player request object that holds the client info object.
|
||||
val clientInfoField = result.mutableMethod
|
||||
.getInstructions().find { instruction ->
|
||||
// requestMessage.clientInfo = clientInfoBuilder.build();
|
||||
instruction.opcode == Opcode.IPUT_OBJECT &&
|
||||
instruction.getReference<FieldReference>()?.type == CLIENT_INFO_CLASS_DESCRIPTOR
|
||||
}?.getReference<FieldReference>() ?: throw PatchException("Could not find clientInfoField")
|
||||
|
||||
// Client info object's client type field.
|
||||
val clientInfoClientTypeField = result.mutableMethod
|
||||
.getInstruction(result.scanResult.patternScanResult!!.endIndex)
|
||||
.getReference<FieldReference>() ?: throw PatchException("Could not find clientInfoClientTypeField")
|
||||
|
||||
// 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")
|
||||
|
||||
Triple(clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField)
|
||||
}
|
||||
|
||||
val clientInfoClientModelField = CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().let {
|
||||
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) {
|
||||
opcode == Opcode.IPUT_OBJECT
|
||||
}
|
||||
|
||||
it.mutableMethod.getInstruction(index).getReference<FieldReference>()
|
||||
?: 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.
|
||||
|
||||
CreatePlayerRequestBodyFingerprint.resultOrThrow().let { result ->
|
||||
val setClientInfoMethodName = "patch_setClientInfo"
|
||||
val checkCastIndex = result.scanResult.patternScanResult!!.startIndex
|
||||
var clientInfoContainerClassName: String
|
||||
|
||||
result.mutableMethod.apply {
|
||||
val checkCastInstruction = getInstruction<OneRegisterInstruction>(checkCastIndex)
|
||||
val requestMessageInstanceRegister = checkCastInstruction.registerA
|
||||
clientInfoContainerClassName = checkCastInstruction.getReference<TypeReference>()!!.type
|
||||
|
||||
addInstruction(
|
||||
checkCastIndex + 1,
|
||||
"invoke-static { v$requestMessageInstanceRegister }," +
|
||||
" ${result.classDef.type}->$setClientInfoMethodName($clientInfoContainerClassName)V",
|
||||
)
|
||||
}
|
||||
|
||||
// Change client info to use the spoofed values.
|
||||
// Do this in a helper method, to remove the need of picking out multiple free registers from the hooked code.
|
||||
result.mutableClass.methods.add(
|
||||
ImmutableMethod(
|
||||
result.mutableClass.type,
|
||||
setClientInfoMethodName,
|
||||
listOf(ImmutableMethodParameter(clientInfoContainerClassName, null, "clientInfoContainer")),
|
||||
"V",
|
||||
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(3),
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
"""
|
||||
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isClientSpoofingEnabled()Z
|
||||
move-result v0
|
||||
if-eqz v0, :disabled
|
||||
|
||||
iget-object v0, p0, $clientInfoField
|
||||
|
||||
# Set client type to the spoofed value.
|
||||
iget v1, v0, $clientInfoClientTypeField
|
||||
invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientTypeId(I)I
|
||||
move-result v1
|
||||
iput v1, v0, $clientInfoClientTypeField
|
||||
|
||||
# Set client model to the spoofed value.
|
||||
iget-object v1, v0, $clientInfoClientModelField
|
||||
invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientModel(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v1
|
||||
iput-object v1, v0, $clientInfoClientModelField
|
||||
|
||||
# Set client version to the spoofed value.
|
||||
iget-object v1, v0, $clientInfoClientVersionField
|
||||
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
|
||||
""",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Fix player gesture if spoofing to iOS.
|
||||
|
||||
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
|
||||
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
val downAndOutLandscapeAllowedIndex = endIndex - 3
|
||||
val downAndOutPortraitAllowedIndex = endIndex - 9
|
||||
|
||||
arrayOf(
|
||||
downAndOutLandscapeAllowedIndex,
|
||||
downAndOutPortraitAllowedIndex,
|
||||
).forEach { index ->
|
||||
val gestureAllowedMethod = context.toMethodWalker(it.mutableMethod)
|
||||
.nextMethod(index, true)
|
||||
.getMethod() as MutableMethod
|
||||
|
||||
gestureAllowedMethod.apply {
|
||||
val isAllowedIndex = getInstructions().lastIndex
|
||||
val isAllowed = getInstruction<OneRegisterInstruction>(isAllowedIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
isAllowedIndex,
|
||||
"""
|
||||
invoke-static { v$isAllowed }, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
|
||||
move-result v$isAllowed
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// Fix playback speed menu item if spoofing to iOS.
|
||||
|
||||
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
|
||||
val scanResult = it.scanResult.patternScanResult!!
|
||||
if (scanResult.startIndex != 0) throw PatchException("Unexpected start index: ${scanResult.startIndex}")
|
||||
|
||||
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 shouldCreateMenuRegister = getInstruction<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
shouldCreateMenuIndex,
|
||||
"""
|
||||
invoke-static { v$shouldCreateMenuRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenu(Z)Z
|
||||
move-result v$shouldCreateMenuRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// Fix watch history if spoofing to iOS.
|
||||
|
||||
GetTrackingUriFingerprint.resultOrThrow().let {
|
||||
it.mutableMethod.apply {
|
||||
val returnUrlIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
val urlRegister = getInstruction<OneRegisterInstruction>(returnUrlIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
returnUrlIndex,
|
||||
"""
|
||||
invoke-static { v$urlRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideTrackingUrl(Landroid/net/Uri;)Landroid/net/Uri;
|
||||
move-result-object v$urlRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
override fun execute(context: BytecodeContext) {}
|
||||
}
|
||||
@@ -1,239 +1,12 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
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.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
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.PreferenceScreen.Sorting
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
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.patches.youtube.video.information.VideoInformationPatch
|
||||
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
|
||||
@Patch(
|
||||
description = "Spoofs the signature to prevent playback issues.",
|
||||
dependencies = [
|
||||
SettingsPatch::class,
|
||||
PlayerTypeHookPatch::class,
|
||||
PlayerResponseMethodHookPatch::class,
|
||||
VideoInformationPatch::class,
|
||||
SpoofSignatureResourcePatch::class,
|
||||
AddResourcesPatch::class,
|
||||
],
|
||||
)
|
||||
@Deprecated("This patch will be removed in the future.")
|
||||
@Deprecated("This patch is obsolete.", replaceWith = ReplaceWith("SpoofVideoStreamsPatch"))
|
||||
object SpoofSignaturePatch : BytecodePatch(
|
||||
setOf(
|
||||
PlayerResponseModelImplGeneralFingerprint,
|
||||
PlayerResponseModelImplLiveStreamFingerprint,
|
||||
PlayerResponseModelImplRecommendedLevelFingerprint,
|
||||
StoryboardRendererSpecFingerprint,
|
||||
StoryboardRendererDecoderSpecFingerprint,
|
||||
StoryboardRendererDecoderRecommendedLevelFingerprint,
|
||||
StoryboardThumbnailParentFingerprint,
|
||||
SpoofSignaturePatchScrubbedPreviewLayoutFingerprint,
|
||||
StatsQueryParameterFingerprint,
|
||||
ParamsMapPutFingerprint,
|
||||
),
|
||||
dependencies = setOf(SpoofVideoStreamsPatch::class),
|
||||
) {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/spoof/SpoofSignaturePatch;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AddResourcesPatch(this::class)
|
||||
|
||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||
PreferenceScreen(
|
||||
key = "revanced_spoof_signature_verification_screen",
|
||||
sorting = Sorting.UNSORTED,
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_spoof_signature_verification_enabled"),
|
||||
SwitchPreference("revanced_spoof_signature_in_feed_enabled"),
|
||||
SwitchPreference("revanced_spoof_storyboard"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// Hook the player parameters.
|
||||
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter(
|
||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;",
|
||||
)
|
||||
|
||||
// Force the seekbar time and chapters to always show up.
|
||||
// This is used if the storyboard spec fetch fails, for viewing paid videos,
|
||||
// or if storyboard spoofing is turned off.
|
||||
StoryboardThumbnailParentFingerprint.result?.classDef?.let { classDef ->
|
||||
StoryboardThumbnailFingerprint.also {
|
||||
it.resolve(
|
||||
context,
|
||||
classDef,
|
||||
)
|
||||
}.result?.let {
|
||||
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
// Replace existing instruction to preserve control flow label.
|
||||
// The replaced return instruction always returns false
|
||||
// (it is the 'no thumbnails found' control path),
|
||||
// so there is no need to pass the existing return value to integrations.
|
||||
it.mutableMethod.replaceInstruction(
|
||||
endIndex,
|
||||
"""
|
||||
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarThumbnailOverrideValue()Z
|
||||
""",
|
||||
)
|
||||
// Since this is end of the method must replace one line then add the rest.
|
||||
it.mutableMethod.addInstructions(
|
||||
endIndex + 1,
|
||||
"""
|
||||
move-result v0
|
||||
return v0
|
||||
""",
|
||||
)
|
||||
} ?: throw StoryboardThumbnailFingerprint.exception
|
||||
}
|
||||
|
||||
// If storyboard spoofing is turned off, then hide the empty seekbar thumbnail view.
|
||||
SpoofSignaturePatchScrubbedPreviewLayoutFingerprint.result?.apply {
|
||||
val endIndex = scanResult.patternScanResult!!.endIndex
|
||||
mutableMethod.apply {
|
||||
val imageViewFieldName = getInstruction<ReferenceInstruction>(endIndex).reference
|
||||
addInstructions(
|
||||
implementation!!.instructions.lastIndex,
|
||||
"""
|
||||
iget-object v0, p0, $imageViewFieldName # copy imageview field to a register
|
||||
invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->seekbarImageViewCreated(Landroid/widget/ImageView;)V
|
||||
""",
|
||||
)
|
||||
}
|
||||
} ?: throw SpoofSignaturePatchScrubbedPreviewLayoutFingerprint.exception
|
||||
|
||||
/**
|
||||
* Hook StoryBoard renderer url
|
||||
*/
|
||||
arrayOf(
|
||||
PlayerResponseModelImplGeneralFingerprint,
|
||||
PlayerResponseModelImplLiveStreamFingerprint,
|
||||
).forEach { fingerprint ->
|
||||
fingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val getStoryBoardIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
val getStoryBoardRegister =
|
||||
getInstruction<OneRegisterInstruction>(getStoryBoardIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
getStoryBoardIndex,
|
||||
"""
|
||||
invoke-static { v$getStoryBoardRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$getStoryBoardRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
} ?: throw fingerprint.exception
|
||||
}
|
||||
|
||||
// Hook recommended seekbar thumbnails quality level.
|
||||
StoryboardRendererDecoderRecommendedLevelFingerprint.result?.let {
|
||||
val moveOriginalRecommendedValueIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
val originalValueRegister = it.mutableMethod
|
||||
.getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
|
||||
|
||||
it.mutableMethod.addInstructions(
|
||||
moveOriginalRecommendedValueIndex + 1,
|
||||
"""
|
||||
invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I
|
||||
move-result v$originalValueRegister
|
||||
""",
|
||||
)
|
||||
} ?: throw StoryboardRendererDecoderRecommendedLevelFingerprint.exception
|
||||
|
||||
// Hook the recommended precise seeking thumbnails quality level.
|
||||
PlayerResponseModelImplRecommendedLevelFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val moveOriginalRecommendedValueIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
val originalValueRegister =
|
||||
getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
moveOriginalRecommendedValueIndex,
|
||||
"""
|
||||
invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I
|
||||
move-result v$originalValueRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
} ?: throw PlayerResponseModelImplRecommendedLevelFingerprint.exception
|
||||
|
||||
StoryboardRendererSpecFingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val storyBoardUrlParams = 0
|
||||
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
if-nez p$storyBoardUrlParams, :ignore
|
||||
invoke-static { p$storyBoardUrlParams }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object p$storyBoardUrlParams
|
||||
""",
|
||||
ExternalLabel("ignore", getInstruction(0)),
|
||||
)
|
||||
}
|
||||
} ?: throw StoryboardRendererSpecFingerprint.exception
|
||||
|
||||
// Hook the seekbar thumbnail decoder and use a NULL spec for live streams.
|
||||
StoryboardRendererDecoderSpecFingerprint.result?.let {
|
||||
val storyBoardUrlIndex = it.scanResult.patternScanResult!!.startIndex + 1
|
||||
val storyboardUrlRegister =
|
||||
it.mutableMethod.getInstruction<OneRegisterInstruction>(storyBoardUrlIndex).registerA
|
||||
|
||||
it.mutableMethod.addInstructions(
|
||||
storyBoardUrlIndex + 1,
|
||||
"""
|
||||
invoke-static { v$storyboardUrlRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardDecoderRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$storyboardUrlRegister
|
||||
""",
|
||||
)
|
||||
} ?: throw StoryboardRendererDecoderSpecFingerprint.exception
|
||||
|
||||
// Fix stats not being tracked.
|
||||
// Due to signature spoofing "adformat" is present in query parameters made for /stats requests,
|
||||
// even though, for regular videos, it should not be.
|
||||
// This breaks stats tracking.
|
||||
// Replace the ad parameter with the video parameter in the query parameters.
|
||||
StatsQueryParameterFingerprint.result?.let {
|
||||
val putMethod = ParamsMapPutFingerprint.result?.method?.toString()
|
||||
?: throw ParamsMapPutFingerprint.exception
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val adParamIndex = it.scanResult.stringsScanResult!!.matches.first().index
|
||||
val videoParamIndex = adParamIndex + 3
|
||||
|
||||
// Replace the ad parameter with the video parameter.
|
||||
replaceInstruction(adParamIndex, getInstruction(videoParamIndex))
|
||||
|
||||
// Call paramsMap.put instead of paramsMap.putIfNotExist
|
||||
// because the key is already present in the map.
|
||||
val putAdParamIndex = adParamIndex + 1
|
||||
val putIfKeyNotExistsInstruction = getInstruction<FiveRegisterInstruction>(putAdParamIndex)
|
||||
replaceInstruction(
|
||||
putAdParamIndex,
|
||||
"invoke-virtual { " +
|
||||
"v${putIfKeyNotExistsInstruction.registerC}, " +
|
||||
"v${putIfKeyNotExistsInstruction.registerD}, " +
|
||||
"v${putIfKeyNotExistsInstruction.registerE} }, " +
|
||||
putMethod,
|
||||
)
|
||||
}
|
||||
} ?: throw StatsQueryParameterFingerprint.exception
|
||||
}
|
||||
override fun execute(context: BytecodeContext) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,8 @@ package app.revanced.patches.youtube.misc.fix.playback
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
|
||||
@Patch(dependencies = [ResourceMappingPatch::class])
|
||||
@Deprecated("This patch will be removed in the future.")
|
||||
object SpoofSignatureResourcePatch : ResourcePatch() {
|
||||
internal var scrubbedPreviewThumbnailResourceId: Long = -1
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
scrubbedPreviewThumbnailResourceId = ResourceMappingPatch[
|
||||
"id",
|
||||
"thumbnail",
|
||||
]
|
||||
}
|
||||
override fun execute(context: ResourceContext) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback
|
||||
|
||||
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.extensions.InstructionExtensions.getInstructions
|
||||
import app.revanced.patcher.extensions.or
|
||||
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.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.PreferenceScreen
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildInitPlaybackRequestFingerprint
|
||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildMediaDataSourceFingerprint
|
||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildPlayerRequestURIFingerprint
|
||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildRequestFingerprint
|
||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreateStreamingDataFingerprint
|
||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.ProtobufClassParseByteBufferFingerprint
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
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.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
|
||||
@Patch(
|
||||
name = "Spoof video streams",
|
||||
description = "Spoofs the client video streams to allow video playback.",
|
||||
dependencies = [
|
||||
SettingsPatch::class,
|
||||
AddResourcesPatch::class,
|
||||
UserAgentClientSpoofPatch::class,
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
[
|
||||
"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",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
object SpoofVideoStreamsPatch : BytecodePatch(
|
||||
setOf(
|
||||
BuildInitPlaybackRequestFingerprint,
|
||||
BuildPlayerRequestURIFingerprint,
|
||||
CreateStreamingDataFingerprint,
|
||||
BuildMediaDataSourceFingerprint,
|
||||
BuildRequestFingerprint,
|
||||
ProtobufClassParseByteBufferFingerprint,
|
||||
),
|
||||
) {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/spoof/SpoofVideoStreamsPatch;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AddResourcesPatch(this::class)
|
||||
|
||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||
PreferenceScreen(
|
||||
key = "revanced_spoof_video_streams_screen",
|
||||
sorting = PreferenceScreen.Sorting.UNSORTED,
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_spoof_video_streams"),
|
||||
ListPreference(
|
||||
"revanced_spoof_video_streams_client_type",
|
||||
summaryKey = null,
|
||||
entriesKey = "revanced_spoof_video_streams_client_type_entries",
|
||||
entryValuesKey = "revanced_spoof_video_streams_client_type_entry_values",
|
||||
),
|
||||
SwitchPreference(
|
||||
"revanced_spoof_video_streams_ios_force_avc",
|
||||
tag = "app.revanced.integrations.youtube.settings.preference.ForceAVCSpoofingPreference",
|
||||
),
|
||||
NonInteractivePreference("revanced_spoof_video_streams_about_ios"),
|
||||
NonInteractivePreference("revanced_spoof_video_streams_about_android_vr"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// region Block /initplayback requests to fall back to /get_watch requests.
|
||||
|
||||
BuildInitPlaybackRequestFingerprint.resultOrThrow().let {
|
||||
val moveUriStringIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(moveUriStringIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
moveUriStringIndex + 1,
|
||||
"""
|
||||
invoke-static { v$targetRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$targetRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Block /get_watch requests to fall back to /player requests.
|
||||
|
||||
BuildPlayerRequestURIFingerprint.resultOrThrow().let {
|
||||
val invokeToStringIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val uriRegister = getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
|
||||
|
||||
addInstructions(
|
||||
invokeToStringIndex,
|
||||
"""
|
||||
invoke-static { v$uriRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
|
||||
move-result-object v$uriRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Get replacement streams at player requests.
|
||||
|
||||
BuildRequestFingerprint.resultOrThrow().mutableMethod.apply {
|
||||
val newRequestBuilderIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
getReference<MethodReference>()?.name == "newUrlRequestBuilder"
|
||||
}
|
||||
val urlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
|
||||
val freeRegister = getInstruction<OneRegisterInstruction>(newRequestBuilderIndex + 1).registerA
|
||||
|
||||
addInstructions(
|
||||
newRequestBuilderIndex,
|
||||
"""
|
||||
move-object v$freeRegister, p1
|
||||
invoke-static { v$urlRegister, v$freeRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Replace the streaming data with the replacement streams.
|
||||
|
||||
CreateStreamingDataFingerprint.resultOrThrow().let { result ->
|
||||
result.mutableMethod.apply {
|
||||
val setStreamDataMethodName = "patch_setStreamingData"
|
||||
val resultMethodType = result.mutableClass.type
|
||||
val videoDetailsIndex = result.scanResult.patternScanResult!!.endIndex
|
||||
val videoDetailsRegister = getInstruction<TwoRegisterInstruction>(videoDetailsIndex).registerA
|
||||
val videoDetailsClass = getInstruction(videoDetailsIndex).getReference<FieldReference>()!!.type
|
||||
|
||||
addInstruction(
|
||||
videoDetailsIndex + 1,
|
||||
"invoke-direct { p0, v$videoDetailsRegister }, " +
|
||||
"$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V",
|
||||
)
|
||||
|
||||
val protobufClass = ProtobufClassParseByteBufferFingerprint.resultOrThrow().mutableMethod.definingClass
|
||||
val setStreamingDataIndex = result.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
val playerProtoClass = getInstruction(setStreamingDataIndex + 1)
|
||||
.getReference<FieldReference>()!!.definingClass
|
||||
|
||||
val setStreamingDataField = getInstruction(setStreamingDataIndex).getReference<FieldReference>()
|
||||
|
||||
val getStreamingDataField = getInstruction(
|
||||
indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.definingClass == playerProtoClass
|
||||
}
|
||||
).getReference<FieldReference>()
|
||||
|
||||
// Use a helper method to avoid the need of picking out multiple free registers from the hooked code.
|
||||
result.mutableClass.methods.add(
|
||||
ImmutableMethod(
|
||||
resultMethodType,
|
||||
setStreamDataMethodName,
|
||||
listOf(ImmutableMethodParameter(videoDetailsClass, null, "videoDetails")),
|
||||
"V",
|
||||
AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(9),
|
||||
).toMutable().apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isSpoofingEnabled()Z
|
||||
move-result v0
|
||||
if-eqz v0, :disabled
|
||||
|
||||
# Get video id.
|
||||
iget-object v2, p1, $videoDetailsClass->c:Ljava/lang/String;
|
||||
if-eqz v2, :disabled
|
||||
|
||||
# Get streaming data.
|
||||
invoke-static { v2 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer;
|
||||
move-result-object v3
|
||||
if-eqz v3, :disabled
|
||||
|
||||
# Parse streaming data.
|
||||
sget-object v4, $playerProtoClass->a:$playerProtoClass
|
||||
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass
|
||||
move-result-object v5
|
||||
check-cast v5, $playerProtoClass
|
||||
|
||||
# Set streaming data.
|
||||
iget-object v6, v5, $getStreamingDataField
|
||||
if-eqz v6, :disabled
|
||||
iput-object v6, p0, $setStreamingDataField
|
||||
|
||||
:disabled
|
||||
return-void
|
||||
""",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Remove /videoplayback request body to fix playback.
|
||||
// This is needed when using iOS client as streaming data source.
|
||||
|
||||
BuildMediaDataSourceFingerprint.resultOrThrow().let {
|
||||
it.mutableMethod.apply {
|
||||
val targetIndex = getInstructions().lastIndex
|
||||
|
||||
// Instructions are added just before the method returns,
|
||||
// so there's no concern of clobbering in-use registers.
|
||||
addInstructions(
|
||||
targetIndex,
|
||||
"""
|
||||
# Field a: Stream uri.
|
||||
# Field c: Http method.
|
||||
# Field d: Post data.
|
||||
move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register.
|
||||
iget-object v1, v0, $definingClass->a:Landroid/net/Uri;
|
||||
iget v2, v0, $definingClass->c:I
|
||||
iget-object v3, v0, $definingClass->d:[B
|
||||
invoke-static { v1, v2, v3 }, $INTEGRATIONS_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B
|
||||
move-result-object v1
|
||||
iput-object v1, v0, $definingClass->d:[B
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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
|
||||
|
||||
internal object BuildMediaDataSourceFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
returnType = "V",
|
||||
parameters = listOf(
|
||||
"Landroid/net/Uri;",
|
||||
"J",
|
||||
"I",
|
||||
"[B",
|
||||
"Ljava/util/Map;",
|
||||
"J",
|
||||
"J",
|
||||
"Ljava/lang/String;",
|
||||
"I",
|
||||
"Ljava/lang/Object;"
|
||||
)
|
||||
)
|
||||
@@ -3,13 +3,34 @@ 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
|
||||
)
|
||||
customFingerprint = { methodDef, _ ->
|
||||
// Different targets have slightly different parameters
|
||||
|
||||
// Earlier targets have parameters:
|
||||
//L
|
||||
//Ljava/util/Map;
|
||||
//[B
|
||||
//L
|
||||
//L
|
||||
//L
|
||||
//Lorg/chromium/net/UrlRequest$Callback;
|
||||
|
||||
// Later targets have parameters:
|
||||
//L
|
||||
//Ljava/util/Map;
|
||||
//[B
|
||||
//L
|
||||
//L
|
||||
//L
|
||||
//Lorg/chromium/net/UrlRequest\$Callback;
|
||||
//L
|
||||
|
||||
val parameterTypes = methodDef.parameterTypes
|
||||
(parameterTypes.size == 7 || parameterTypes.size == 8)
|
||||
&& parameterTypes[1] == "Ljava/util/Map;" // URL headers.
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
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 CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
opcodes = listOf(
|
||||
Opcode.IGET_OBJECT, // First instruction of the method
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
null // MOVE_RESULT or MOVE_RESULT_OBJECT, Return value controls the creation of the playback speed menu item.
|
||||
),
|
||||
// 19.01 and earlier is missing the second parameter.
|
||||
// Since this fingerprint is somewhat weak, work around by checking for both method parameter signatures.
|
||||
customFingerprint = custom@{ methodDef, _ ->
|
||||
// 19.01 and earlier parameters are: "[L"
|
||||
// 19.02+ parameters are "[L", "F"
|
||||
val parameterTypes = methodDef.parameterTypes
|
||||
val firstParameter = parameterTypes.firstOrNull()
|
||||
|
||||
if (firstParameter == null || !firstParameter.startsWith("[L")) {
|
||||
return@custom false
|
||||
}
|
||||
|
||||
parameterTypes.size == 1 || (parameterTypes.size == 2 && parameterTypes[1] == "F")
|
||||
}
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object CreatePlayerRequestBodyFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IGET,
|
||||
Opcode.AND_INT_LIT16,
|
||||
),
|
||||
strings = listOf("ms"),
|
||||
)
|
||||
@@ -1,31 +0,0 @@
|
||||
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.CreatePlayerRequestBodyWithModelFingerprint.indexOfBuildModelInstruction
|
||||
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 CreatePlayerRequestBodyWithModelFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf(),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.containsWideLiteralInstructionValue(1073741824) &&
|
||||
indexOfBuildModelInstruction(methodDef) >= 0
|
||||
},
|
||||
) {
|
||||
fun indexOfBuildModelInstruction(methodDef: Method) =
|
||||
methodDef.indexOfFirstInstruction {
|
||||
val reference = getReference<FieldReference>()
|
||||
reference?.definingClass == "Landroid/os/Build;" &&
|
||||
reference.name == "MODEL" &&
|
||||
reference.type == "Ljava/lang/String;"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
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,24 @@
|
||||
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 CreateStreamingDataFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
returnType = "V",
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IPUT_OBJECT
|
||||
),
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
classDef.fields.any { field ->
|
||||
field.name == "a" && field.type.endsWith("/StreamingDataOuterClass\$StreamingData;")
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -1,25 +0,0 @@
|
||||
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
|
||||
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object ParamsMapPutFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf(
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;",
|
||||
),
|
||||
opcodes = listOf(
|
||||
Opcode.CONST_4,
|
||||
Opcode.CONST_4,
|
||||
Opcode.CONST_4,
|
||||
Opcode.MOVE_OBJECT,
|
||||
Opcode.MOVE_OBJECT,
|
||||
Opcode.MOVE_OBJECT,
|
||||
Opcode.INVOKE_DIRECT_RANGE,
|
||||
),
|
||||
)
|
||||
@@ -1,49 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Ljava/lang/Object;"),
|
||||
opcodes = listOf(
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed.
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed.
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
Opcode.RETURN_VOID,
|
||||
),
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
|
||||
methodDef.indexOfFirstInstruction {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
|
||||
reference.parameterTypes.isEmpty() &&
|
||||
reference.returnType == "Z"
|
||||
}
|
||||
|
||||
// This method is always called "a" because this kind of class always has a single method.
|
||||
methodDef.name == "a" && classDef.methods.count() == 2 &&
|
||||
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
|
||||
},
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.util.containsWideLiteralInstructionValue
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object PlayerResponseModelImplGeneralFingerprint : MethodFingerprint(
|
||||
returnType = "Ljava/lang/String;",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = emptyList(),
|
||||
opcodes = listOf(
|
||||
Opcode.RETURN_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.RETURN_OBJECT,
|
||||
),
|
||||
customFingerprint = handler@{ methodDef, _ ->
|
||||
if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) return@handler false
|
||||
|
||||
methodDef.containsWideLiteralInstructionValue(55735497)
|
||||
},
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.util.containsWideLiteralInstructionValue
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object PlayerResponseModelImplLiveStreamFingerprint : MethodFingerprint(
|
||||
returnType = "Ljava/lang/String;",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = emptyList(),
|
||||
opcodes = listOf(
|
||||
Opcode.RETURN_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.RETURN_OBJECT,
|
||||
),
|
||||
customFingerprint = handler@{ methodDef, _ ->
|
||||
if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) return@handler false
|
||||
|
||||
methodDef.containsWideLiteralInstructionValue(70276274)
|
||||
},
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.util.containsWideLiteralInstructionValue
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object PlayerResponseModelImplRecommendedLevelFingerprint : MethodFingerprint(
|
||||
returnType = "I",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = emptyList(),
|
||||
opcodes = listOf(
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IGET,
|
||||
Opcode.RETURN,
|
||||
),
|
||||
customFingerprint = handler@{ methodDef, _ ->
|
||||
if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) return@handler false
|
||||
|
||||
methodDef.containsWideLiteralInstructionValue(55735497)
|
||||
},
|
||||
)
|
||||
@@ -5,17 +5,15 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object GetTrackingUriFingerprint : MethodFingerprint(
|
||||
returnType = "Landroid/net/Uri;",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = emptyList(),
|
||||
internal object ProtobufClassParseByteBufferFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.STATIC,
|
||||
parameters = listOf("L", "Ljava/nio/ByteBuffer;"),
|
||||
returnType = "L",
|
||||
opcodes = listOf(
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.RETURN_OBJECT
|
||||
Opcode.RETURN_OBJECT,
|
||||
),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.endsWith("TrackingUrlModel;")
|
||||
}
|
||||
)
|
||||
customFingerprint = { methodDef, _ -> methodDef.name == "parseFrom" },
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object SetPlayerRequestClientTypeFingerprint : LiteralValueFingerprint(
|
||||
opcodes = listOf(
|
||||
Opcode.IGET,
|
||||
Opcode.IPUT, // Sets ClientInfo.clientId.
|
||||
),
|
||||
strings = listOf("10.29"),
|
||||
literalSupplier = { 134217728 }
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patches.youtube.misc.fix.playback.SpoofSignatureResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object SpoofSignaturePatchScrubbedPreviewLayoutFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf("Landroid/content/Context;", "Landroid/util/AttributeSet;", "I", "I"),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.CONST,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IPUT_OBJECT, // preview imageview
|
||||
),
|
||||
// This resource is used in ~ 40 different locations, but this method has a distinct list of parameters to match to.
|
||||
literalSupplier = { SpoofSignatureResourcePatch.scrubbedPreviewThumbnailResourceId },
|
||||
)
|
||||
@@ -1,8 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object StatsQueryParameterFingerprint : MethodFingerprint(
|
||||
strings = listOf("adunit"),
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* Resolves to the same method as [StoryboardRendererDecoderSpecFingerprint].
|
||||
*/
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object StoryboardRendererDecoderRecommendedLevelFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
),
|
||||
strings = listOf("#-1#"),
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* Resolves to the same method as [StoryboardRendererDecoderRecommendedLevelFingerprint].
|
||||
*/
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object StoryboardRendererDecoderSpecFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_INTERFACE, // First instruction of the method.
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_NEZ,
|
||||
),
|
||||
strings = listOf("#-1#"),
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
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
|
||||
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object StoryboardRendererSpecFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
returnType = "L",
|
||||
parameters = listOf("Ljava/lang/String;", "J"),
|
||||
strings = listOf("\\|"),
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* Resolves using the class found in [StoryboardThumbnailParentFingerprint].
|
||||
*/
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object StoryboardThumbnailFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Z",
|
||||
parameters = listOf(),
|
||||
opcodes = listOf(
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_GTZ,
|
||||
Opcode.GOTO,
|
||||
Opcode.CONST_4,
|
||||
Opcode.RETURN,
|
||||
Opcode.RETURN, // Last instruction of method.
|
||||
),
|
||||
)
|
||||
@@ -1,18 +0,0 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* Here lies code that creates the seekbar thumbnails.
|
||||
*
|
||||
* An additional change here might force the thumbnails to be created,
|
||||
* or possibly a change somewhere else (maybe involving YouTube 18.23.35 class `hte`)
|
||||
*/
|
||||
@Deprecated("Fingerprint is obsolete and will be deleted soon")
|
||||
internal object StoryboardThumbnailParentFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Landroid/graphics/Bitmap;",
|
||||
strings = listOf("Storyboard regionDecoder.decodeRegion exception - "),
|
||||
)
|
||||
@@ -16,6 +16,7 @@ import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.reques
|
||||
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
|
||||
@@ -84,17 +85,14 @@ object CronetImageUrlHook : BytecodePatch(
|
||||
}
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =
|
||||
also { resolve(context, fingerprint.resultOrThrow().classDef) }.resultOrThrow()
|
||||
|
||||
loadImageUrlMethod = MessageDigestImageUrlFingerprint
|
||||
.alsoResolve(MessageDigestImageUrlParentFingerprint).mutableMethod
|
||||
.alsoResolve(context, MessageDigestImageUrlParentFingerprint).mutableMethod
|
||||
|
||||
loadImageSuccessCallbackMethod = OnSucceededFingerprint
|
||||
.alsoResolve(OnResponseStartedFingerprint).mutableMethod
|
||||
.alsoResolve(context, OnResponseStartedFingerprint).mutableMethod
|
||||
|
||||
loadImageErrorCallbackMethod = OnFailureFingerprint
|
||||
.alsoResolve(OnResponseStartedFingerprint).mutableMethod
|
||||
.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.
|
||||
|
||||
@@ -15,6 +15,7 @@ import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||
import app.revanced.patches.youtube.misc.check.CheckEnvironmentPatch
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.settings.fingerprints.LicenseActivityOnCreateFingerprint
|
||||
import app.revanced.patches.youtube.misc.settings.fingerprints.SetThemeFingerprint
|
||||
@@ -30,6 +31,9 @@ import java.io.Closeable
|
||||
IntegrationsPatch::class,
|
||||
SettingsResourcePatch::class,
|
||||
AddResourcesPatch::class,
|
||||
// Currently there is no easy way to make a mandatory patch,
|
||||
// so for now this is a dependent of this patch.
|
||||
CheckEnvironmentPatch::class,
|
||||
],
|
||||
)
|
||||
object SettingsPatch :
|
||||
|
||||
@@ -28,8 +28,6 @@ object SettingsResourcePatch : BaseSettingsResourcePatch(
|
||||
override fun execute(context: ResourceContext) {
|
||||
super.execute(context)
|
||||
|
||||
AddResourcesPatch(this::class)
|
||||
|
||||
// Used for a fingerprint from SettingsPatch.
|
||||
appearanceStringId = ResourceMappingPatch["string", "app_theme_appearance_dark"]
|
||||
|
||||
|
||||
@@ -7,12 +7,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.video.information.fingerprints.*
|
||||
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||
import app.revanced.util.alsoResolve
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
@@ -45,9 +47,11 @@ object VideoInformationPatch : BytecodePatch(
|
||||
)
|
||||
) {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/VideoInformation;"
|
||||
private const val INTEGRATIONS_PLAYER_INTERFACE = "Lapp/revanced/integrations/youtube/patches/VideoInformation${'$'}PlaybackController;"
|
||||
|
||||
private lateinit var playerInitMethod: MutableMethod
|
||||
private var playerInitInsertIndex = 4
|
||||
private var playerInitInsertIndex = -1
|
||||
private var playerInitInsertRegister = -1
|
||||
|
||||
private lateinit var mdxInitMethod: MutableMethod
|
||||
private var mdxInitInsertIndex = -1
|
||||
@@ -70,42 +74,43 @@ object VideoInformationPatch : BytecodePatch(
|
||||
with(PlayerInitFingerprint.resultOrThrow()) {
|
||||
playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
|
||||
|
||||
// hook the player controller for use through integrations
|
||||
// find the location of the first invoke-direct call and extract the register storing the 'this' object reference.
|
||||
val initThisIndex = playerInitMethod.indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
|
||||
}
|
||||
playerInitInsertRegister = playerInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
|
||||
playerInitInsertIndex = initThisIndex + 1
|
||||
|
||||
// Hook the player controller for use through integrations.
|
||||
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
|
||||
|
||||
// seek method
|
||||
val seekFingerprintResultMethod =
|
||||
SeekFingerprint.also { it.resolve(context, classDef) }.resultOrThrow().method
|
||||
SeekFingerprint.alsoResolve(context, PlayerInitFingerprint).method
|
||||
val seekRelativeFingerprintResultMethod =
|
||||
SeekRelativeFingerprint.alsoResolve(context, PlayerInitFingerprint).method
|
||||
|
||||
// create helper method
|
||||
val seekHelperMethod = generateSeekMethodHelper(seekFingerprintResultMethod)
|
||||
|
||||
// add the seekTo method to the class for the integrations to call
|
||||
mutableClass.methods.add(seekHelperMethod)
|
||||
// Create integrations interface methods.
|
||||
addSeekInterfaceMethods(mutableClass, seekFingerprintResultMethod, seekRelativeFingerprintResultMethod)
|
||||
}
|
||||
|
||||
with(MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow()) {
|
||||
mdxInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
|
||||
|
||||
// find the location of the first invoke-direct call and extract the register storing the 'this' object reference
|
||||
val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
|
||||
}
|
||||
mdxInitInsertRegister = mdxInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
|
||||
mdxInitInsertIndex = initThisIndex + 1
|
||||
|
||||
// hook the MDX director for use through integrations
|
||||
// Hook the MDX director for use through integrations.
|
||||
onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx")
|
||||
|
||||
// MDX seek method
|
||||
val mdxSeekFingerprintResultMethod =
|
||||
MdxSeekFingerprint.apply { resolve(context, classDef) }.resultOrThrow().method
|
||||
MdxSeekFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
|
||||
val mdxSeekRelativeFingerprintResultMethod =
|
||||
MdxSeekRelativeFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
|
||||
|
||||
// create helper method
|
||||
val mdxSeekHelperMethod = generateSeekMethodHelper(mdxSeekFingerprintResultMethod)
|
||||
|
||||
// add the seekTo method to the class for the integrations to call
|
||||
mutableClass.methods.add(mdxSeekHelperMethod)
|
||||
addSeekInterfaceMethods(mutableClass, mdxSeekFingerprintResultMethod, mdxSeekRelativeFingerprintResultMethod)
|
||||
}
|
||||
|
||||
with(CreateVideoPlayerSeekbarFingerprint.result!!) {
|
||||
@@ -173,33 +178,42 @@ object VideoInformationPatch : BytecodePatch(
|
||||
userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
|
||||
}
|
||||
|
||||
private fun generateSeekMethodHelper(seekMethod: Method): MutableMethod {
|
||||
private fun addSeekInterfaceMethods(targetClass: MutableClass, seekToMethod: Method, seekToRelativeMethod: Method) {
|
||||
// Add the interface and methods that integrations calls.
|
||||
targetClass.interfaces.add(INTEGRATIONS_PLAYER_INTERFACE)
|
||||
|
||||
// create helper method
|
||||
val generatedMethod = ImmutableMethod(
|
||||
seekMethod.definingClass,
|
||||
"seekTo",
|
||||
listOf(ImmutableMethodParameter("J", null, "time")),
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
null, null,
|
||||
MutableMethodImplementation(4)
|
||||
).toMutable()
|
||||
arrayOf(
|
||||
seekToMethod to "seekTo",
|
||||
seekToRelativeMethod to "seekToRelative"
|
||||
).forEach { (method, name) ->
|
||||
// Add interface method.
|
||||
// Get enum type for the seek helper method.
|
||||
val seekSourceEnumType = method.parameterTypes[1].toString()
|
||||
|
||||
// get enum type for the seek helper method
|
||||
val seekSourceEnumType = seekMethod.parameterTypes[1].toString()
|
||||
val interfaceImplementation = ImmutableMethod(
|
||||
targetClass.type,
|
||||
name,
|
||||
listOf(ImmutableMethodParameter("J", null, "time")),
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
null, null,
|
||||
MutableMethodImplementation(4)
|
||||
).toMutable()
|
||||
|
||||
// insert helper method instructions
|
||||
generatedMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
||||
invoke-virtual { p0, p1, p2, v0 }, $seekMethod
|
||||
move-result p1
|
||||
return p1
|
||||
"""
|
||||
)
|
||||
return generatedMethod
|
||||
// Insert helper method instructions.
|
||||
interfaceImplementation.addInstructions(
|
||||
0,
|
||||
"""
|
||||
# first enum (field a) is SEEK_SOURCE_UNKNOWN
|
||||
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
||||
invoke-virtual { p0, p1, p2, v0 }, $method
|
||||
move-result p1
|
||||
return p1
|
||||
"""
|
||||
)
|
||||
|
||||
targetClass.methods.add(interfaceImplementation)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) =
|
||||
@@ -220,8 +234,8 @@ object VideoInformationPatch : BytecodePatch(
|
||||
internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) =
|
||||
playerInitMethod.insert(
|
||||
playerInitInsertIndex++,
|
||||
"v0",
|
||||
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
|
||||
"v$playerInitInsertRegister",
|
||||
"$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -234,7 +248,7 @@ object VideoInformationPatch : BytecodePatch(
|
||||
mdxInitMethod.insert(
|
||||
mdxInitInsertIndex++,
|
||||
"v$mdxInitInsertRegister",
|
||||
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
|
||||
"$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,9 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Resolves using class found in [MdxPlayerDirectorSetVideoStageFingerprint].
|
||||
*/
|
||||
internal object MdxSeekFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Z",
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
@@ -249,3 +249,9 @@ fun Iterable<MethodFingerprint>.returnEarly(bool: Boolean = false) = forEach { f
|
||||
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()
|
||||
|
||||
@@ -32,15 +32,16 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
<!-- Settings about dialog. -->
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.SettingsPatch">
|
||||
</patch>
|
||||
<patch id="misc.debugging.DebuggingPatch">
|
||||
@@ -57,7 +58,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<!-- '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 _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<!-- 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">
|
||||
@@ -171,8 +172,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
|
||||
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
|
||||
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
|
||||
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
|
||||
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
|
||||
<!-- Shown in the settings preferences, and translations can be any text length. -->
|
||||
</patch>
|
||||
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
|
||||
@@ -206,6 +205,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
<patch id="misc.announcements.AnnouncementsPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.CheckWatchHistoryDomainNameResolutionPatch">
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
</patch>
|
||||
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
|
||||
|
||||
@@ -32,15 +32,16 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
<!-- Settings about dialog. -->
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.SettingsPatch">
|
||||
</patch>
|
||||
<patch id="misc.debugging.DebuggingPatch">
|
||||
@@ -57,7 +58,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<!-- '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 _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<!-- 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">
|
||||
@@ -171,8 +172,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
|
||||
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
|
||||
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
|
||||
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
|
||||
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
|
||||
<!-- Shown in the settings preferences, and translations can be any text length. -->
|
||||
</patch>
|
||||
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
|
||||
@@ -206,6 +205,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
<patch id="misc.announcements.AnnouncementsPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.CheckWatchHistoryDomainNameResolutionPatch">
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
</patch>
|
||||
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
|
||||
|
||||
@@ -32,6 +32,17 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">ŲØ´ŲØĒ ØšŲ
ŲŲØ§ØĒ Ø§ŲØĒØŲŲ</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">ŲØĒØ اŲŲ
Ųب𠨧ب਺Ų
Ų</string>
|
||||
<string name="revanced_check_environment_dialog_ignore_button">ØĒØŦاŲŲ</string>
|
||||
<string name="revanced_check_environment_failed_message"><h5>ŲØ§ ŲØ¨Ø¯Ų ØŖŲ ŲØ°Ø§ Ø§ŲØĒØˇØ¨ŲŲ ŲØ¯ ØĒŲ
ØĒؚدŲŲŲ Ų
Ų ŲØ¨ŲŲ.</h5><br>ŲØ¯ ŲØ§ ŲØšŲ
Ų ŲØ°Ø§ Ø§ŲØĒØˇØ¨ŲŲ Ø¨Ø´ŲŲ ØĩØŲØØ <b>ŲØ¯ ŲŲŲŲ ØļØ§ØąŲØ§ ØŖŲ ØØĒŲ ØŽØˇŲØąŲا ŲŲØ§ØŗØĒ؎داŲ
</b>.<br><br>ØĒØ´ŲØą ŲØ°Ų اŲŲØŲØĩاØĒ ØĨŲŲ ØŖŲ ŲØ°Ø§ Ø§ŲØĒØˇØ¨ŲŲ ØĒŲ
ØĒؚدŲŲŲ Ų
ØŗØ¨ŲŲØ§ ØŖŲ ØĒŲ
Ø§ŲØØĩŲŲ ØšŲŲŲ Ų
Ų Ø´ØŽØĩ ØĸØŽØą:<br><br><small>%1$s</small><br>ŲŲØĩŲ Ø¨Ø´Ø¯ØŠ Ø¨Ų <b>ØĨŲØēØ§ØĄ ØĒØĢØ¨ŲØĒ ŲØ°Ø§ Ø§ŲØĒØˇØ¨ŲŲ ŲØĒØšØ¯ŲŲŲ Ø¨ŲŲØŗŲ</b> ŲŲØĒØŖŲد Ų
Ų ØŖŲŲ ØĒØŗØĒ؎دŲ
ØĒØˇØ¨ŲŲŲØ§ Ų
ØšØĒŲ
Ø¯ŲØ§ ŲØĸŲ
ŲŲØ§.<p><br>ŲŲ ØØ§ŲØŠ ØĒØŦاŲŲ ŲØ°Ø§ Ø§ŲØĒØØ°ŲØąØ ØŗŲØĒŲ
ØšØąØļŲ Ų
ØąØĒŲŲ ŲŲØˇ.</string>
|
||||
<string name="revanced_check_environment_not_same_patching_device">ØĒŲ
ØĒؚدŲŲŲ ØšŲŲ ØŦŲØ§Ø˛ Ų
ØŽØĒŲŲ</string>
|
||||
<string name="revanced_check_environment_manager_not_expected_installer">ŲŲ
ŲØĒŲ
ØĒØĢØ¨ŲØĒŲ Ø¨ŲØ§ØŗØˇØŠ ReVanced Manager</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time">ØĒŲ
ØĒؚدŲŲŲ ŲØ¨Ų ØŖŲØĢØą Ų
Ų 10 Ø¯ŲØ§ØĻŲ</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_days">ØĒŲ
Ø§ŲØĒØšØ¯ŲŲ Ų
ŲØ° %s ŲŲŲ
</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_invalid">ØĒØ§ØąŲØŽ ØĨŲØ´Ø§ØĄ APK ØĒاŲŲ</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
<string name="revanced_settings_confirm_user_dialog_title">ŲŲ ØĒØąØēب ŲŲ Ø§ŲŲ
ØĒابؚ؊Ø</string>
|
||||
<string name="revanced_settings_reset">ØĨؚاد؊ Ø§ŲØĒØšŲŲŲ</string>
|
||||
@@ -42,6 +53,14 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_settings_import_reset">ØĨؚاد؊ ØĒØšŲŲŲ ØĨؚداداØĒ ReVanced ØĨŲŲ Ø§ŲŲØļØš Ø§ŲØ§ŲØĒØąØ§ØļŲ</string>
|
||||
<string name="revanced_settings_import_success">ØĒŲ
Ø§ØŗØĒŲØąØ§Ø¯ %d ØĨؚداداØĒ</string>
|
||||
<string name="revanced_settings_import_failure_parse">ŲØ´Ų Ø§ŲØ§ØŗØĒŲØąØ§Ø¯: %s</string>
|
||||
<string name="revanced_pref_import_export_title">Ø§ØŗØĒŲØąØ§Ø¯ / ØĒØĩØ¯ŲØą</string>
|
||||
<string name="revanced_pref_import_export_summary">Ø§ØŗØĒŲØąØ§Ø¯ / ØĒØĩØ¯ŲØą ØĨؚداداØĒ ReVanced</string>
|
||||
<!-- Settings about dialog. -->
|
||||
<string name="revanced_settings_about_links_body">ØŖŲØĒ ØĒØŗØĒ؎دŲ
ØĨØĩØ¯Ø§Øą ReVanced Patches <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>
|
||||
<string name="revanced_settings_about_links_donate">ØĒØ¨ØąØš</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
@@ -54,14 +73,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
<string name="revanced_settings_about_links_body">ØŖŲØĒ ØĒØŗØĒ؎دŲ
ØĨØĩØ¯Ø§Øą ReVanced Patches <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>
|
||||
<string name="revanced_pref_import_export_title">Ø§ØŗØĒŲØąØ§Ø¯ / ØĒØĩØ¯ŲØą</string>
|
||||
<string name="revanced_pref_import_export_summary">Ø§ØŗØĒŲØąØ§Ø¯ / ØĒØĩØ¯ŲØą ØĨؚداداØĒ ReVanced</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.SettingsPatch">
|
||||
<string name="revanced_settings_screen_00_about_title">ŲŲ
ØØŠ</string>
|
||||
<string name="revanced_settings_screen_01_ads_title">Ø§ŲØĨØšŲØ§ŲاØĒ</string>
|
||||
@@ -211,6 +222,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_info_cards_section_title">ØĨØŽŲØ§ØĄ ŲØŗŲ
Ø¨ØˇØ§ŲØ§ØĒ اŲŲ
ØšŲŲŲ
اØĒ</string>
|
||||
<string name="revanced_hide_info_cards_section_summary_on">ØĒŲ
ØĨØŽŲØ§ØĄ ŲØŗŲ
Ø¨ØˇØ§ŲØ§ØĒ اŲŲ
ØšŲŲŲ
اØĒ</string>
|
||||
<string name="revanced_hide_info_cards_section_summary_off">ŲØĒŲ
ØšØąØļ ŲØŗŲ
Ø¨ØˇØ§ŲØ§ØĒ اŲŲ
ØšŲŲŲ
اØĒ</string>
|
||||
<string name="revanced_hide_key_concepts_section_title">ØĨØŽŲØ§ØĄ ŲØŗŲ
\'اŲŲ
ŲØ§ŲŲŲ
Ø§ŲØŖØŗØ§ØŗŲØŠ\'</string>
|
||||
<string name="revanced_hide_key_concepts_section_summary_on">ØĒŲ
ØĨØŽŲØ§ØĄ ŲØŗŲ
\'اŲŲ
ŲØ§ŲŲŲ
Ø§ŲØŖØŗØ§ØŗŲØŠ\'</string>
|
||||
<string name="revanced_hide_key_concepts_section_summary_off">ŲØĒŲ
ØšØąØļ ŲØŗŲ
\'اŲŲ
ŲØ§ŲŲŲ
Ø§ŲØŖØŗØ§ØŗŲØŠ\'</string>
|
||||
<string name="revanced_hide_transcript_section_title">ØĨØŽŲØ§ØĄ ŲØŗŲ
اŲŲØĩ</string>
|
||||
<string name="revanced_hide_transcript_section_summary_on">ØĒŲ
ØĨØŽŲØ§ØĄ ŲØŗŲ
اŲŲØĩ</string>
|
||||
<string name="revanced_hide_transcript_section_summary_off">ŲØĒŲ
ØšØąØļ ŲØŗŲ
اŲŲØĩ</string>
|
||||
@@ -239,14 +253,18 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_keyword_content_phrases_title">اŲŲŲŲ
اØĒ اŲŲ
ŲØĒØ§ØŲØŠ اŲŲ
ØąØ§Ø¯ ØĨØŽŲØ§Ø¤Ųا</string>
|
||||
<!-- 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. -->
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">اŲŲŲŲ
اØĒ اŲŲ
ŲØĒØ§ØŲØŠ ŲØ§ŲØšØ¨Ø§ØąØ§ØĒ اŲŲ
ØąØ§Ø¯ ØĨØŽŲØ§Ø¤ŲØ§Ø Ų
ŲØĩŲŲØŠ Ø¨ØŖØŗØˇØą ØŦØ¯ŲØ¯\n\nاŲŲŲŲ
اØĒ Ø§ŲØĒŲ ØĒØØĒŲŲ ØšŲŲ ØŖØØąŲ ŲØ¨ŲØąØŠ ŲŲ Ø§ŲŲØŗØˇ ŲØŦب ØĨد؎اŲŲØ§ Ų
Øš Ø§ŲØĒØēŲŲŲ (Ų
ØĢŲ: iPhone, TikTok, Leblanc)</string>
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">اŲŲŲŲ
اØĒ ŲØ§ŲØšØ¨Ø§ØąØ§ØĒ Ø§ŲØąØĻŲØŗŲØŠ Ø§ŲØĒŲ ŲØŦب ØĨØŽŲØ§Ø¤ŲØ§Ø Ų
ŲØĩŲŲØŠ Ø¨ØŗØˇØą ØŦØ¯ŲØ¯\n\nاŲŲŲŲ
اØĒ Ø§ŲØąØĻŲØŗŲØŠ ŲŲ
ŲŲ ØŖŲ ØĒŲŲŲ ØŖØŗŲ
Ø§ØĄ ŲŲŲØ§ØĒ ØŖŲ ØŖŲ ŲØĩ ŲØ¸ŲØą ŲŲ ØšŲØ§ŲŲŲ Ø§ŲŲŲØ¯ŲŲ\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>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Ø§ŲØĩŲØØŠ Ø§ŲØąØĻŲØŗŲØŠ/Ø§ŲØ§Ø´ØĒØąØ§ŲØ§ØĒ/ŲØĒØ§ØĻØŦ Ø§ŲØĨØ´ØĒØąØ§Ų/ŲØĒŲ
ØĒØĩŲŲØŠ ŲØĒØ§ØĻØŦ Ø§ŲØ¨ØØĢ ŲØĨØŽŲØ§ØĄ اŲŲ
ØØĒŲŲ Ø§ŲØ°Ų ŲØĒØˇØ§Ø¨Ų Ų
Øš ØšØ¨Ø§ØąØ§ØĒ اŲŲŲŲ
اØĒ Ø§ŲØąØĻŲØŗŲØŠ\n\nاŲŲŲŲØ¯\nâĸ ŲØ§ ŲŲ
ŲŲ ØĨØŽŲØ§ØĄ ŲŲØ¯ŲŲŲØ§ØĒ Shorts Ø¨ŲØ§ØŗØˇØŠ Ø§ØŗŲ
اŲŲŲØ§ØŠ\nâĸ ŲØ¯ ŲØ§ ØĒŲŲŲ Ø¨ØšØļ Ų
ŲŲŲØ§ØĒ ŲØ§ØŦŲØŠ Ø§ŲŲ
ØŗØĒ؎دŲ
Ų
ØŽŲŲØŠ\nâĸ ŲØ¯ ŲØ§ ØĒØ¸ŲØą ŲØĒØ§ØĻØŦ Ø¨ØØĢ ØšŲ ŲŲŲ
ØŠ ØąØĻŲØŗŲØŠ</string>
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_title">Ų
ØˇØ§Ø¨ŲØŠ Ø§ŲŲŲŲ
اØĒ Ø¨ØŖŲŲ
ŲŲØ§</string>
|
||||
<!-- Translations _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_summary">ØŗŲØ¤Ø¯Ų ŲØļØš ØšŲØ§Ų
ØŠ Ø§ŲØĒØ¨Ø§Øŗ Ų
Ø˛Ø¯ŲØŦØŠ ØŲŲ ŲŲŲ
ØŠ ØąØĻŲØŗŲØŠ/ØšØ¨Ø§ØąØŠ ØĨŲŲ Ų
ب𠨧بǨˇØ§Ø¨ŲاØĒ Ø§ŲØŦØ˛ØĻŲØŠ ŲØšŲاŲŲŲ Ø§ŲŲŲØ¯ŲŲ ŲØŖØŗŲ
Ø§ØĄ اŲŲŲŲØ§ØĒ.<br><br>ØšŲŲ ØŗØ¨ŲŲ Ø§ŲŲ
ØĢØ§ŲØ<br><b>\"ai\"</b> ØŗŲØŽŲŲ Ø§ŲŲŲØ¯ŲŲ: <b>How does AI work?</b><br>ŲŲŲŲ ŲŲ ŲØŽŲŲ: <b>What does fair use mean?</b></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_common">ŲØ§ ŲŲ
ŲŲ Ø§ØŗØĒ؎داŲ
اŲŲŲŲ
ØŠ Ø§ŲØąØĻŲØŗŲØŠ: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_common_whole_word_required">ØĨØļØ§ŲØŠ Ø§ŲØĒØ¨Ø§ØŗØ§ØĒ ŲØ§ØŗØĒ؎داŲ
اŲŲŲŲ
ØŠ Ø§ŲØąØĻŲØŗŲØŠ: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_conflicting">اŲŲŲŲ
ØŠ Ø§ŲØąØĻŲØŗŲØŠ ŲŲØ§ Ø¨ŲØ§ŲاØĒ Ų
ØĒØļØ§ØąØ¨ØŠ: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_length">اŲŲŲŲ
ØŠ Ø§ŲØąØĻŲØŗŲØŠ ŲØĩŲØąØŠ ØŦØ¯ŲØ§ ŲØĒØĒØˇŲب Ø§ŲØĒØ¨Ø§ØŗØ§ØĒ: %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>
|
||||
@@ -612,6 +630,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_shorts_save_sound_button_title">ØĨØŽŲØ§ØĄ ØŲظ Ø§ŲØĩŲØĒ ØĨŲŲ Ø˛Øą ŲØ§ØĻŲ
ØŠ Ø§ŲØĒØ´ØēŲŲ</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_on">ØĒŲ
ØĨØŽŲØ§ØĄ ØŲظ Ø§ŲØĩŲØĒ ŲŲ ŲØ§ØĻŲ
ØŠ Ø§ŲØĒØ´ØēŲŲ</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_off">ŲØĒŲ
ØšØąØļ ØŲظ Ø§ŲØĩŲØĒ ŲŲ ŲØ§ØĻŲ
ØŠ Ø§ŲØĒØ´ØēŲŲ</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_title">ØĨØŽŲØ§ØĄ Ø§ØŗØĒ؎داŲ
ŲØ°Ø§ Ø§ŲØ˛Øą Ø§ŲØĩŲØĒŲ</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_summary_on">ØĒŲ
ØĨØŽŲØ§ØĄ Ø˛Øą Ø§ŲØĩŲØĒ ŲØ°Ø§</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_summary_off">Ø§ØŗØĒ؎دŲ
ŲØ°Ø§ Ø§ŲØ˛Øą Ø§ŲØĩŲØĒŲ ŲØ¸ŲØą</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_title">ØĨØŽŲØ§ØĄ Ø§ŲØĒØąØ§ØØ§ØĒ Ø§ŲØ¨ØØĢ</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_summary_on">ØĒŲ
ØĨØŽŲØ§ØĄ Ø§ŲØĒØąØ§ØØ§ØĒ Ø§ŲØ¨ØØĢ</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_summary_off">ŲØĒŲ
ØšØąØļ Ø§ŲØĒØąØ§ØØ§ØĒ Ø§ŲØ¨ØØĢ</string>
|
||||
@@ -675,7 +696,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_player_overlay_opacity_invalid_toast">Ø´ŲØ§ŲŲØŠ ŲØ§ØŦŲØŠ Ø§ŲŲ
Ø´ØēŲ ŲØŦب ØŖŲ ØĒŲŲŲ Ø¨ŲŲ 0-100</string>
|
||||
</patch>
|
||||
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
|
||||
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Ų
ØŽŲŲ</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">ŲŲ
ŲØšØŦبŲŲ ØēŲØą Ų
ØĒØ§Ø Ų
Ø¤ŲØĒŲØ§ (Ø§ŲØĒŲØĒ Ų
ŲŲØŠ API)</string>
|
||||
<string name="revanced_ryd_failure_connection_status_code">ŲŲ
ŲØšØŦبŲŲ ØēŲØą Ų
ØĒØ§Ø (Ø§ŲØØ§ŲØŠ %d)</string>
|
||||
@@ -771,6 +791,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_general_skipcount_sum_off">ØĒŲ
ØĒØšØˇŲŲ ØĒØĒبؚ Ų
ØąØ§ØĒ Ø§ŲØĒØŽØˇŲ</string>
|
||||
<string name="revanced_sb_general_min_duration">Ø§ŲØØ¯ Ø§ŲØŖØ¯ŲŲ ŲŲ
د؊ اŲŲ
ŲØˇØš</string>
|
||||
<string name="revanced_sb_general_min_duration_sum">ŲŲ ŲØĒŲ
ØšØąØļ اŲŲ
ŲØ§ØˇØš Ø§ŲØŖŲØĩØą Ų
Ų ŲØ°Ų اŲŲŲŲ
ØŠ (Ø¨Ø§ŲØĢŲØ§ŲŲ) ØŖŲ ØĒØŽØˇŲŲØ§</string>
|
||||
<string name="revanced_sb_general_min_duration_invalid">اŲŲ
د؊ Ø§ŲØ˛Ų
ŲŲØŠ ØēŲØą ØĩØ§ŲØØŠ</string>
|
||||
<string name="revanced_sb_general_uuid">Ų
ØšØąŲ Ø§ŲŲ
ØŗØĒ؎دŲ
اŲŲØąŲد Ø§ŲØŽØ§Øĩ بŲ</string>
|
||||
<string name="revanced_sb_general_uuid_sum">ŲØŦب ØŖŲ ŲØ¨ŲŲ ŲØ°Ø§ ؎اØĩŲØ§. اŲŲ Ų
ØĢŲ ŲŲŲ
ØŠ اŲŲ
ØąŲØą ŲŲØ§ ŲŲØ¨ØēŲ Ų
Ø´Ø§ØąŲØĒŲ Ų
Øš ØŖŲ Ø´ØŽØĩ. ØĨذا ŲØ§Ų Ø´ØŽØĩ Ų
ا ŲŲ
ŲŲ ŲØ°Ø§Ø ŲŲŲ
ŲŲŲ Ø§ŲØĒØØ§Ų Ø´ØŽØĩŲØĒŲ</string>
|
||||
<string name="revanced_sb_general_uuid_invalid">ŲØŦب ØŖŲ ŲŲŲŲ Ų
ØšØąŲ Ø§ŲŲ
ØŗØĒ؎دŲ
Ø§ŲØŽØ§Øĩ 30 ØØąŲŲØ§ ØšŲŲ Ø§ŲØŖŲŲ</string>
|
||||
@@ -871,8 +892,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_new_segment_time_start">اŲŲŲØĒ Ø§ŲØ°Ų ŲØ¨Ø¯ØŖ ØšŲØ¯Ų اŲŲ
ŲØˇØš</string>
|
||||
<string name="revanced_sb_new_segment_time_end">اŲŲŲØĒ Ø§ŲØ°Ų ŲŲØĒŲŲ ØšŲØ¯Ų اŲŲ
ŲØˇØš</string>
|
||||
<string name="revanced_sb_new_segment_confirm_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_confirm_content">اŲŲ
ŲØˇØš Ų
Ų\n\n%1$s\nto\n%2$s\n\n(%3$s)\n\nReady to ØŦØ§ŲØ˛ ŲŲØĨØąØŗØ§ŲØ</string>
|
||||
<string name="revanced_sb_new_segment_start_is_before_end">ŲØŦب ØŖŲ ØĒŲŲŲ Ø§ŲØ¨Ø¯Ø§ŲØŠ ŲØ¨Ų اŲŲŲØ§ŲØŠ</string>
|
||||
<string name="revanced_sb_new_segment_mark_locations_first">ØļØš ØšŲØ§Ų
ØŠ ØšŲŲ Ų
ŲŲØšŲŲ ŲŲ Ø´ØąŲØˇ اŲŲŲØĒ ØŖŲŲŲØ§</string>
|
||||
@@ -891,6 +910,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_stats_username_changed">ØĒŲ
ØĒØēŲŲØą Ø§ØŗŲ
اŲŲ
ØŗØĒ؎دŲ
Ø¨ŲØŦاØ</string>
|
||||
<string name="revanced_sb_stats_reputation">ØŗŲ
ØšØĒŲ ŲŲ <b>%.2f</b></string>
|
||||
<string name="revanced_sb_stats_submissions">ŲŲØ¯ ØŖŲØ´ØŖØĒ <b>%s</b> Ų
ŲØˇØš</string>
|
||||
<string name="revanced_sb_stats_submissions_sum">اØļØēØˇ ŲŲØ§ ŲØšØąØļ Ø§ŲŲ
ŲØ§ØˇØš Ø§ŲØŽØ§ØĩØŠ بŲ</string>
|
||||
<string name="revanced_sb_stats_saved_zero">Ų
ØĒØĩØ¯ØąŲŲ SponsorBlock</string>
|
||||
<string name="revanced_sb_stats_saved">ŲŲØ¯ ŲŲ
ØĒ بØŲظ اŲŲØ§Øŗ Ų
Ų <b>%s</b> Ų
ŲØˇØš</string>
|
||||
<string name="revanced_sb_stats_saved_sum_zero">اØļØēØˇ ŲŲØ§ ŲØąØ¤ŲØŠ Ø§ŲØĨØØĩاØĻŲØ§ØĒ Ø§ŲØšØ§ŲŲ
ŲØŠ ŲØŖØ¨ØąØ˛ اŲŲ
ØŗØ§ŲŲ
ŲŲ</string>
|
||||
@@ -923,7 +943,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<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.30.34 - Ø§ØŗØĒؚاد؊ ØĒØĩŲ
ŲŲ
ŲØ§ØŦŲØŠ Ø§ŲŲ
ØŗØĒ؎دŲ
اŲŲØ¯ŲŲ
</string>
|
||||
<string name="revanced_spoof_app_version_target_entry_5">17.33.42 - Ø§ØŗØĒؚاد؊ ØĒØĩŲ
ŲŲ
ŲØ§ØŦŲØŠ Ø§ŲŲ
ØŗØĒ؎دŲ
اŲŲØ¯ŲŲ
</string>
|
||||
</patch>
|
||||
<patch id="layout.startpage.ChangeStartPagePatch">
|
||||
<string name="revanced_start_page_title">ØĒØšŲŲŲ ØĩŲØØŠ Ø§ŲØ¨Ø¯Ø§ŲØŠ</string>
|
||||
@@ -1033,6 +1053,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_announcements_connection_failed">ŲØ´Ų Ø§ŲØ§ØĒØĩØ§Ų Ø¨Ų
ŲŲØą Ø§ŲØĨØšŲØ§ŲاØĒ</string>
|
||||
<string name="revanced_announcements_dialog_dismiss">ØĒØŦاŲŲ</string>
|
||||
</patch>
|
||||
<patch id="misc.dns.CheckWatchHistoryDomainNameResolutionPatch">
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_title">ØĒØØ°ŲØą</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_message">ŲŲ
ŲØĒŲ
ØŲظ ØŗØŦŲ Ø§ŲŲ
Ø´Ø§ŲØ¯ØŠ Ø§ŲØŽØ§Øĩ بŲ.<br><br>Ų
Ų Ø§ŲŲ
ØąØŦØ ØŖŲ ŲŲŲŲ Ø§ŲØŗØ¨Ø¨ ŲŲ Ø°ŲŲ ŲŲ Ų
Ø§ŲØš ØĨØšŲØ§ŲاØĒ DNS ØŖŲ ŲŲŲŲ Ø§ŲØ´Ø¨ŲØŠ.<br><br>ŲØĨØĩŲØ§Ø ŲØ°Ų اŲŲ
Ø´ŲŲØŠØ ŲŲ
بØĨØļØ§ŲØŠ <b>s.youtube.com</b> ØĨŲŲ Ø§ŲŲØ§ØĻŲ
ØŠ Ø§ŲØ¨ŲØļØ§ØĄ ØŖŲ ŲŲ
بØĨŲŲØ§Ų ØĒØ´ØēŲŲ ØŦŲ
ŲØš ØŖØ¯ŲØ§ØĒ ØØ¸Øą DNS ŲŲŲŲØ§ØĄ Ø§ŲØ¨ØąŲŲØŗŲ.</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_ignore">ŲØ§ ØĒØšØąØļ Ų
ØąØŠ ØŖØŽØąŲ</string>
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
<string name="revanced_auto_repeat_title">ØĒŲ
ŲŲŲ Ø§ŲØĒŲØąØ§Øą Ø§ŲØĒŲŲØ§ØĻŲ</string>
|
||||
<string name="revanced_auto_repeat_summary_on">ØĒŲ
ØĒŲ
ŲŲŲ Ø§ŲØĒŲØąØ§Øą Ø§ŲØĒŲŲØ§ØĻŲ</string>
|
||||
@@ -1114,9 +1139,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_client_summary_on">ŲØĒŲ
Ų
ØØ§Ųا؊ Ø§ŲØšŲ
ŲŲ</string>
|
||||
<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âĸ ŲØ§ ŲŲ
ŲŲ ØĒØ´ØēŲŲ Ø§ŲØ¨ØĢ اŲŲ
Ø¨Ø§Ø´Øą ŲŲØĩŲØĒ ŲŲØˇ</string>
|
||||
<string name="revanced_spoof_client_use_ios_summary_off">Ø§ŲØšŲ
ŲŲ Ų
ØšØˇŲØ¨ ØØ§ŲŲØ§Ų ØĨŲŲ ØŖŲØ¯ØąŲŲØ¯ VR\n\nØ§ŲØĸØĢØ§Øą Ø§ŲØŦØ§ŲØ¨ŲØŠ ØĒØ´Ų
Ų:\nâĸ ŲØ§ ŲŲØŦد ŲŲØ¯ŲŲ HDR\nâĸ ŲŲØ¯ŲŲŲØ§ØĒ Ø§ŲØŖØˇŲØ§Ų ŲØ§ ŲØĒŲ
ØĒØ´ØēŲŲŲŲ
\nâĸ Ų
ŲØ§ØˇØš اŲŲŲØ¯ŲŲ Ø§ŲŲ
ŲŲŲŲØŠ ŲŲ
ŲŲ ØŖŲ ØĒØŗØĒØŖŲŲ ØšØ´ŲØ§ØĻŲØ§Ų\nâĸ ØŦŲØ¯ØŠ Ų
ŲØŽŲØļØŠ ŲØĩŲØą Ų
ØĩØēØąØŠ Ø´ØąŲØˇ Ø§ŲØ¨ØØĢ\nâĸ Ø˛Øą Ø§ŲØĒØŲ
ŲŲ Ų
ØŽŲŲ\nâĸ Ø¨ØˇØ§ŲØ§ØĒ ØĨŲŲØ§ØĄ Ø§ŲØ´Ø§Ø´ØŠ Ų
ØŽŲŲØŠ</string>
|
||||
<string name="revanced_spoof_client_type_title">ŲŲØš Spoof Client</string>
|
||||
<string name="revanced_spoof_client_ios_force_avc_title">ŲØąØļ AVC iOS (H.264)</string>
|
||||
<string name="revanced_spoof_client_ios_force_avc_summary_on">ØĒØąŲ
ŲØ˛ ŲŲØ¯ŲŲ iOS ŲŲ AVC</string>
|
||||
<string name="revanced_spoof_client_ios_force_avc_summary_off">ØĒØąŲ
ŲØ˛ ŲŲØ¯ŲŲ iOS ŲŲ AVC ØŖŲ VP9 ØŖŲ AV1</string>
|
||||
<string name="revanced_spoof_client_ios_force_avc_user_dialog_message">ŲØ¯ ŲØ¤Ø¯Ų ØĒŲ
ŲŲŲ ŲØ°Ø§ ØĨŲŲ ØĒØØŗŲŲ ØšŲ
Øą Ø§ŲØ¨ØˇØ§ØąŲØŠ ŲØĨØĩŲØ§Ø Ų
Ø´ŲŲØŠ ØĒŲØˇŲØš Ø§ŲØĒØ´ØēŲŲ.\n\nŲØĒŲ
ØĒØš ØĒŲØŗŲŲ AVC Ø¨Ø¯ŲØŠ ŲØĩŲŲ ØĒØ¨ŲØē 1080PØ ŲØŗŲØŗØĒ؎دŲ
ØĒØ´ØēŲŲ Ø§ŲŲŲØ¯ŲŲ Ø§ŲŲ
Ø˛ŲØ¯ Ų
Ų Ø¨ŲØ§ŲاØĒ Ø§ŲØĨŲØĒØąŲØĒ Ų
ŲØ§ØąŲØŠŲ Ø¨ØĒŲØŗŲŲ VP9 ØŖŲ AV1.</string>
|
||||
<string name="revanced_spoof_client_about_android_ios_title">Ø§ŲØĒØŖØĢبਧØĒ Ø§ŲØŦØ§ŲØ¨ŲØŠ ŲŲ
ØØ§Ųا؊ iOS</string>
|
||||
<string name="revanced_spoof_client_about_android_ios_summary">âĸ HDR Ų
دؚŲŲ
ŲŲØˇ Ų
Øš ØĒØąŲ
ŲØ˛ AV1\nâĸ ØŗØŦŲ Ø§ŲŲ
Ø´Ø§ŲØ¯ØŠ ŲØ§ ŲØšŲ
Ų Ų
Øš ØØŗØ§Ø¨ Ø§ŲØšŲاŲ
ØŠ Ø§ŲØĒØŦØ§ØąŲØŠ</string>
|
||||
<string name="revanced_spoof_client_about_android_vr_title">Ø§ŲØĒØŖØĢبਧØĒ Ø§ŲØŦØ§ŲØ¨ŲØŠ ŲŲ
ØØ§Ųا؊ Android VR</string>
|
||||
<string name="revanced_spoof_client_about_android_vr_summary">âĸ ŲØ§ ŲŲØŦد ŲŲØ¯ŲŲ HDR\nâĸ ŲØ§ ŲØĒŲ
ØĒØ´ØēŲŲ Ų
ŲØ§ØˇØš ŲŲØ¯ŲŲ Ø§ŲØŖØˇŲاŲ\nâĸ ŲŲ
ŲŲ Ø§ØŗØĒØĻŲØ§Ų Ų
ŲØ§ØˇØš اŲŲŲØ¯ŲŲ Ø§ŲŲ
ØĒŲŲŲØŠ Ų
Ø¤ŲØĒŲØ§ بشŲŲ ØšØ´ŲØ§ØĻŲ\nâĸ Ų
ØĩØēØąØ§ØĒ Ø´ØąŲØˇ ØĒŲØ¯Ų
ŲŲØ¯ŲŲŲØ§ØĒ Shorts Ų
ŲØŽŲØļØŠ Ø§ŲØŦŲØ¯ØŠ\nâĸ Ø˛Øą ØĨØŦØąØ§ØĄ Ø§ŲØĒŲØ˛ŲŲ Ų
ØŽŲŲ\nâĸ Ø¨ØˇØ§ŲØ§ØĒ شاش؊ اŲŲŲØ§ŲØŠ Ų
ØŽŲŲØŠ</string>
|
||||
<string name="revanced_spoof_client_storyboard_timeout">Ų
ØØ§Ųا؊ Ų
ØĩØēØąØ§ØĒ Ø§ŲØšŲ
ŲŲ ØēŲØą Ų
ØĒŲŲØąØŠ (Ø§ŲØĒŲØĒ Ų
ŲŲØŠ API)</string>
|
||||
<string name="revanced_spoof_client_storyboard_io_exception">Ų
ØØ§Ųا؊ Ų
ØĩØēØąØ§ØĒ Ø§ŲØšŲ
ŲŲ ØēŲØą Ų
ØĒŲŲØąØŠ Ų
Ø¤ŲØĒŲØ§: %s</string>
|
||||
</patch>
|
||||
|
||||
@@ -32,15 +32,16 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
<!-- Settings about dialog. -->
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.SettingsPatch">
|
||||
</patch>
|
||||
<patch id="misc.debugging.DebuggingPatch">
|
||||
@@ -57,7 +58,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<!-- '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 _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<!-- 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">
|
||||
@@ -171,8 +172,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
|
||||
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
|
||||
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
|
||||
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
|
||||
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
|
||||
<!-- Shown in the settings preferences, and translations can be any text length. -->
|
||||
</patch>
|
||||
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
|
||||
@@ -206,6 +205,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
<patch id="misc.announcements.AnnouncementsPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.CheckWatchHistoryDomainNameResolutionPatch">
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
</patch>
|
||||
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
|
||||
|
||||
@@ -32,6 +32,17 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Yoxlamalar uÄursuz oldu</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">XidmÉti veb saytÄą aç</string>
|
||||
<string name="revanced_check_environment_dialog_ignore_button">Yan keç</string>
|
||||
<string name="revanced_check_environment_failed_message"><h5>Bu tÉtbiq sizin tÉrÉfinizdÉn yamaqlanmayÄąb.</h5><br>Bu tÉtbiq dÃŧzgÃŧn iÅlÉmÉyÉ bilÉr, <b>istifadÉ etmÉk zÉrÉrli vÉ ya hÉtta tÉhlÃŧkÉli ola bilÉr</b>.<br><br><br>Bu yoxlamalar bu tÉtbiqin ÉvvÉldÉn yamaqlandÄąÄÄąnÄą vÉ ya baÅqasÄąndan ÉldÉ edildiyini gÃļstÉrir:<br><br><small>%1$s</small><br> <br>onu silmÉyiniz vÉ ÃļzÃŧnÃŧz yamaqlamaÄÄąnÄąz tÃļvsiyÉ olunur. </b>tÉsdiqlÉnmiÅ vÉ tÉhlÃŧkÉsiz tÉtbiq istifadÉ etdiyinizÉ Émin olmaq ÃŧçÃŧn. <p><br> İnkar edilmÉzsÉ, bu xÉbÉrdarlÄąq yalnÄąz iki dÉfÉ gÃļstÉrilÉcÉk.</string>
|
||||
<string name="revanced_check_environment_not_same_patching_device">FÉrqli cihazda yamaqlanÄąb</string>
|
||||
<string name="revanced_check_environment_manager_not_expected_installer">ReVanced Manager tÉrÉfindÉn quraÅdÄąrÄąlmayÄąb</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time">10 dÉqiqÉdÉn çox ÉvvÉl yamaqlanÄąb</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_days">%s gÃŧn ÉvvÉl yamaqlanÄąb</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_invalid">APK quruluÅ tarixi pozulub</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
<string name="revanced_settings_confirm_user_dialog_title">Davam etmÉk istÉyirsiniz?</string>
|
||||
<string name="revanced_settings_reset">SÄąfÄąrla</string>
|
||||
@@ -42,6 +53,14 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_settings_import_reset">ReVanced tÉnzimlÉmÉlÉr standarta tÉyin edildi</string>
|
||||
<string name="revanced_settings_import_success">%d tÉnzimlÉmÉ idxal edildi</string>
|
||||
<string name="revanced_settings_import_failure_parse">UÄursuz idxal prosesi: %s</string>
|
||||
<string name="revanced_pref_import_export_title">İdxal/İxrac et</string>
|
||||
<string name="revanced_pref_import_export_summary">ReVanced tÉnzimlÉmÉlÉrin idxal/ixrac et</string>
|
||||
<!-- Settings about dialog. -->
|
||||
<string name="revanced_settings_about_links_body">ReVanced Patches <i>%s</i> versiyasÄąnÄą istifadÉ edirsiniz</string>
|
||||
<string name="revanced_settings_about_links_dev_header">Qeyd</string>
|
||||
<string name="revanced_settings_about_links_dev_body">Bu versiya ilkin buraxÄąlÄąÅdÄąr vÉ gÃļzlÉnilmÉz problemlÉrlÉ ÃŧzlÉÅÉ bilÉrsiniz</string>
|
||||
<string name="revanced_settings_about_links_header">RÉsmi baÄlantÄąlar</string>
|
||||
<string name="revanced_settings_about_links_donate">İanÉ ver</string>
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
@@ -54,14 +73,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
<string name="revanced_settings_about_links_body">ReVanced Patches <i>%s</i> versiyasÄąnÄą istifadÉ edirsiniz</string>
|
||||
<string name="revanced_settings_about_links_dev_header">Qeyd</string>
|
||||
<string name="revanced_settings_about_links_dev_body">Bu versiya ilkin buraxÄąlÄąÅdÄąr vÉ gÃļzlÉnilmÉz problemlÉrlÉ ÃŧzlÉÅÉ bilÉrsiniz</string>
|
||||
<string name="revanced_settings_about_links_header">RÉsmi baÄlantÄąlar</string>
|
||||
<string name="revanced_pref_import_export_title">İdxal/İxrac et</string>
|
||||
<string name="revanced_pref_import_export_summary">ReVanced tÉnzimlÉmÉlÉrin idxal/ixrac et</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.SettingsPatch">
|
||||
<string name="revanced_settings_screen_00_about_title">HaqqÄąnda</string>
|
||||
<string name="revanced_settings_screen_01_ads_title">Reklamlar</string>
|
||||
@@ -145,9 +156,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_community_posts_title">İcma elanlarÄąn gizlÉt</string>
|
||||
<string name="revanced_hide_community_posts_summary_on">İcma elanlarÄą gizlÉdilib</string>
|
||||
<string name="revanced_hide_community_posts_summary_off">İcma elanlarÄą gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_compact_banner_title">YÄąÄcam bannerlÉri gizlÉt</string>
|
||||
<string name="revanced_hide_compact_banner_summary_on">YÄąÄcam bannerlÉr gizlidir</string>
|
||||
<string name="revanced_hide_compact_banner_summary_off">YÄąÄcam bannerlÉr gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_compact_banner_title">YÄąÄcam etiketlÉri gizlÉt</string>
|
||||
<string name="revanced_hide_compact_banner_summary_on">YÄąÄcam etiketlÉr gizlidir</string>
|
||||
<string name="revanced_hide_compact_banner_summary_off">YÄąÄcam etiketlÉr gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_movies_section_title">FilmlÉr bÃļlmÉsini gizlÉt</string>
|
||||
<string name="revanced_hide_movies_section_summary_on">FilmlÉr bÃļlmÉsi gizlidir</string>
|
||||
<string name="revanced_hide_movies_section_summary_off">FilmlÉr bÃļlmÉsi gÃļstÉrilir</string>
|
||||
@@ -184,7 +195,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_related_videos_title">CÉld fÉaliyyÉtlÉrdÉ ÉlaqÉli videolarÄą gizlÉ</string>
|
||||
<string name="revanced_hide_related_videos_summary_on">ÆlaqÉdar videolar gizlÉdilib</string>
|
||||
<string name="revanced_hide_related_videos_summary_off">ÆlaqÉdar videolar gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_image_shelf_title">AxtarÄąÅ nÉticÉlÉrindÉki ÅÉkil bÃļlmÉsin gizlÉt</string>
|
||||
<string name="revanced_hide_image_shelf_title">AxtarÄąÅ nÉticÉsindÉ ÅÉkil bÃļlmÉsin gizlÉ</string>
|
||||
<string name="revanced_hide_image_shelf_summary_on">ÅÉkil bÃļlmÉsi gizlidir</string>
|
||||
<string name="revanced_hide_image_shelf_summary_off">ÅÉkil bÃļlmÉsi gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_latest_posts_ads_title">Son elanlarÄą gizlÉt</string>
|
||||
@@ -211,6 +222,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_info_cards_section_title">MÉlumat kartlarÄą bÃļlmÉsini gizlÉt</string>
|
||||
<string name="revanced_hide_info_cards_section_summary_on">MÉlumat kartlarÄą bÃļlmÉsi gizlÉdilir</string>
|
||||
<string name="revanced_hide_info_cards_section_summary_off">MÉlumat kartlarÄą bÃļlmÉsi gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_key_concepts_section_title">\"Æsas anlayÄąÅlar\" bÃļlmÉsini gizlÉt</string>
|
||||
<string name="revanced_hide_key_concepts_section_summary_on">\"Æsas anlayÄąÅlar\" bÃļlmÉsi gizlidir</string>
|
||||
<string name="revanced_hide_key_concepts_section_summary_off">\"Æsas anlayÄąÅlar\" bÃļlmÉsi gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_transcript_section_title">Transkripsiya bÃļlmÉsini gizlÉt</string>
|
||||
<string name="revanced_hide_transcript_section_summary_on">Transkripsiya bÃļlmÉsi gizlidir</string>
|
||||
<string name="revanced_hide_transcript_section_summary_off">Transkripsiya bÃļlmÉsi gÃļstÉrilir</string>
|
||||
@@ -239,14 +253,18 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_keyword_content_phrases_title">GizlÉdilÉcÉk açar sÃļzlÉr</string>
|
||||
<!-- 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. -->
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">Yeni sÉtirlÉrlÉ ayrÄąlmÄąÅ gizlÉdilÉcÉk açar sÃļzlÉr vÉ ifadÉlÉr\n\nOrtada bÃļyÃŧk hÉrf olan sÃļzlÉr korpusla birlikdÉ daxil edilmÉlidir (yÉni: iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_phrases_summary">Yeni sÉtirlÉrlÉ ayrÄąlmÄąÅ gizlÉdilÉcÉk açar sÃļzlÉr vÉ frazalar\n\nAçar sÃļzlÉr kanal adlarÄą vÉ ya video adlarÄąnda gÃļstÉrilÉn istÉnilÉn mÉtn ola bilÉr\n\nOrtada bÃļyÃŧk hÉrf olan sÃļzlÉr korpusla birlikdÉ qeyd edilmÉlidir (yÉni: iPhone, TikTok, LeBlanc)</string>
|
||||
<string name="revanced_hide_keyword_content_about_title">Açar sÃļz filtrlÉmÉsi haqqÄąnda</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Æsas sÉhifÉ/AbunÉlik/AxtarÄąÅ nÉticÉlÉri açar sÃļz ifadÉlÉrinÉ uyÄunlaÅan mÉzmunu gizlÉtmÉk ÃŧçÃŧn filtrlÉnir\n\nMÉhdudiyyÉtlÉr\nâĸ BÉzi Shorts gizlÉnÉ bilmÉz\nâĸ BÉzi UI elementlÉri gizlÉnÉ bilmÉz\nâĸ Açar sÃļz axtarÄąÅÄą heç bir nÉticÉ gÃļstÉrmÉyÉ bilÉr</string>
|
||||
<string name="revanced_hide_keyword_content_about_summary">Æsas sÉhifÉ/AbunÉlik/AxtarÄąÅ nÉticÉlÉri açar sÃļz ifadÉlÉrinÉ uyÄunlaÅan mÉzmunu gizlÉtmÉk ÃŧçÃŧn filtrlÉnir\n\nMÉhdudiyyÉtlÉr\nâĸ Shorts-lar kanal adÄąna gÃļrÉ gizlÉnÉ bilmÉz\nâĸ BÉzi UI hissÉciklÉri gizlÉdilÉ bilmÉz\nâĸ Açar sÃļz axtarÄąÅÄąnda nÉticÉ olmaya bilÉr</string>
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_title">BÃŧtÃŧn sÃļzlÉri uyÄunlaÅdÄąr</string>
|
||||
<!-- Translations _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_summary">Açar sÃļz/frazanÄąn qoÅa dÄąrnaqlarla ÉhatÉ olunmasÄą video adlarÄą vÉ kanal adlarÄąnÄąn qismÉn uyÄunlaÅmasÄąna mane olacaq <br><br>MÉsÉlÉn,<br><b>\"ai\"</b> videonu gizlÉdÉcÉk:<b>How does AI work?</b><br> lakin gizlÉtmÉyÉcÉk: DÃŧzgÃŧn;<b>What does fair use mean?</b></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">EtibarsÄąz açar sÃļzÃŧ. \'%s\' istifadÉ edilÉ bilmÉz</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">EtibarsÄąz açar sÃļzÃŧ. \'%1$s\', %2$d simvoldan azdÄąr</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_broad">\"%s\" açar sÃļzÃŧ, bÃŧtÃŧn videolarda gizlÉdilÉcÉk</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_common">Açar sÃļz istifadÉ edilÉ bilmir: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_common_whole_word_required">Açar sÃļz istifadÉsi ÃŧçÃŧn istinad ÉlavÉ et: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_conflicting">Açar sÃļzÃŧn ziddiyyÉtli hissÉciklÉri var: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_length">Açar sÃļz çox qÄąsadÄąr vÉ istinad tÉlÉb edir: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_broad">Açar sÃļz, bÃŧtÃŧn videolarÄą gizlÉdÉcÉk: %s</string>
|
||||
</patch>
|
||||
<patch id="ad.general.HideAdsResourcePatch">
|
||||
<string name="revanced_hide_general_ads_title">Ãmumi reklamlarÄą gizlÉt</string>
|
||||
@@ -264,22 +282,22 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_self_sponsor_ads_title">Ãz-sponsorlu kartlarÄą gizlÉt</string>
|
||||
<string name="revanced_hide_self_sponsor_ads_summary_on">ÃzÃŧnÉ sponsorluq edilÉn kartlar gizlidir</string>
|
||||
<string name="revanced_hide_self_sponsor_ads_summary_off">ÃzÃŧnÉ sponsorluq edilÉn kartlar gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_products_banner_title">MÉhsullara baxma panelin gizlÉt</string>
|
||||
<string name="revanced_hide_products_banner_summary_on">Panel gizlÉdilib</string>
|
||||
<string name="revanced_hide_products_banner_summary_off">Panel gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_shopping_links_title">Video aÃ§ÄąqlamadakÄą alÄąÅ-veriÅ linklÉrin gizlÉ</string>
|
||||
<string name="revanced_hide_products_banner_title">MÉhsullara baxma etiketin gizlÉt</string>
|
||||
<string name="revanced_hide_products_banner_summary_on">Etiket gizlÉdilib</string>
|
||||
<string name="revanced_hide_products_banner_summary_off">Etiket gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_shopping_links_title">Video aÃ§Äąqlama alÄąÅ-veriÅ linklÉr gizlÉ</string>
|
||||
<string name="revanced_hide_shopping_links_summary_on">AlÄąÅ-veriÅ baÄlantÄąlarÄą gizlÉdilir</string>
|
||||
<string name="revanced_hide_shopping_links_summary_off">AlÄąÅ-veriÅ baÄlantÄąlarÄą gÃļstÉrilir</string>
|
||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_visit_store_button_title">KanaldakÄą \"MaÄazanÄą ziyarÉt et\" dÃŧymÉsin gizlÉ</string>
|
||||
<string name="revanced_hide_visit_store_button_title">Kanalda \"MaÄaza ziyarÉt\" dÃŧymÉ gizlÉ</string>
|
||||
<string name="revanced_hide_visit_store_button_summary_on">DÃŧymÉ gizlidir</string>
|
||||
<string name="revanced_hide_visit_store_button_summary_off">DÃŧymÉ gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_web_search_results_title">Veb axtarÄąÅ nÉticÉlÉrini gizlÉt</string>
|
||||
<string name="revanced_hide_web_search_results_summary_on">Veb axtarÄąÅ nÉticÉlÉri gizlÉdilir</string>
|
||||
<string name="revanced_hide_web_search_results_summary_off">Veb axtarÄąÅ nÉticÉlÉri gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_merchandise_banners_title">MÉhsul bannerlÉrini gizlÉt</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">MÉhsul bannerlÉri gizlÉdilir</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">MÉhsul bannerlÉri gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_merchandise_banners_title">MÉhsul etiketlÉrini gizlÉt</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">MÉhsul etiketlÉri gizlÉdilir</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">MÉhsul etiketlÉri gÃļstÉrilir</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">Tam ekran reklamlarÄą gizlÉtmÉ yalnÄąz kÃļhnÉ cihazlarda iÅlÉyir</string>
|
||||
</patch>
|
||||
@@ -304,7 +322,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_copy_video_url_timestamp_summary_off">DÃŧymÉ gÃļstÉrilmir</string>
|
||||
</patch>
|
||||
<patch id="interaction.dialog.RemoveViewerDiscretionDialogPatch">
|
||||
<string name="revanced_remove_viewer_discretion_dialog_title">İzlÉyici mÃŧlahizÉ dialoqunu sil</string>
|
||||
<string name="revanced_remove_viewer_discretion_dialog_title">İzlÉyici mÃŧlahizÉ dialoqun sil</string>
|
||||
<string name="revanced_remove_viewer_discretion_dialog_summary_on">Dialoq silindi</string>
|
||||
<string name="revanced_remove_viewer_discretion_dialog_summary_off">Dialoq gÃļstÉrilir</string>
|
||||
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">Bu, yaÅ mÉhdudiyyÉtini ÃļtÃŧrmÃŧr. SadÉcÉ avtomatik qÉbul edir.</string>
|
||||
@@ -316,7 +334,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_external_downloader_summary_on">YÃŧklÉmÉ dÃŧymÉsi oynadÄącÄąda gÃļstÉrilir</string>
|
||||
<string name="revanced_external_downloader_summary_off">YÃŧklÉmÉ dÃŧymÉsi oynadÄącÄąda gÃļstÉrilmir</string>
|
||||
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
|
||||
<string name="revanced_external_downloader_action_button_title">YÃŧklÉmÉ fÉaliyyÉti dÃŧymÉsini qÉbul etmÉ</string>
|
||||
<string name="revanced_external_downloader_action_button_title">YÃŧklÉmÉ fÉaliyyÉt dÃŧymÉsin qÉbul etmÉ</string>
|
||||
<string name="revanced_external_downloader_action_button_summary_on">YÃŧklÉmÉ dÃŧymÉsi, xarici yÃŧklÉyicini aÃ§Äąr</string>
|
||||
<string name="revanced_external_downloader_action_button_summary_off">YÃŧklÉmÉ dÃŧymÉsi tÉtbiqdÉki standart yÃŧklÉyicini aÃ§Äąr</string>
|
||||
<string name="revanced_external_downloader_name_title">YÃŧklÉyici paketi adÄą</string>
|
||||
@@ -567,7 +585,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_disable_rolling_number_animations_summary_off">SÃŧrÃŧÅÉn say animasiyasÄą aÃ§ÄąqdÄąr</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.seekbar.HideSeekbarPatch">
|
||||
<string name="revanced_hide_seekbar_title">Video oynadÄącÄąda axtarÄąÅ Ã§ubuÄunu gizlÉt</string>
|
||||
<string name="revanced_hide_seekbar_title">Video oynadÄącÄąda axtarÄąÅ Ã§ubuÄun gizlÉ</string>
|
||||
<string name="revanced_hide_seekbar_summary_on">Video oynadÄącÄą axtarÄąÅ Ã§ubuÄu gizlidir</string>
|
||||
<string name="revanced_hide_seekbar_summary_off">Video oynadÄącÄą axtarÄąÅ Ã§ubuÄu gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_seekbar_thumbnail_title">Video miniatÃŧrlÉrdÉ vaxt çubuÄun gizlÉ</string>
|
||||
@@ -606,12 +624,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_shorts_tagged_products_title">EtiketlÉnmiÅ mÉhsullarÄą gizlÉt</string>
|
||||
<string name="revanced_hide_shorts_tagged_products_summary_on">EtiketlÉnmiÅ mÉhsullar gizlÉdilir</string>
|
||||
<string name="revanced_hide_shorts_tagged_products_summary_off">EtiketlÉnmiÅ mÉhsullar gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_shorts_location_label_title">YerlÉÅmÉ etiketini gizlÉt</string>
|
||||
<string name="revanced_hide_shorts_location_label_title">MÉkan etiketini gizlÉt</string>
|
||||
<string name="revanced_hide_shorts_location_label_summary_on">MÉkan etiketi gizlidir</string>
|
||||
<string name="revanced_hide_shorts_location_label_summary_off">MÉkan etiketi gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_title">SÉsi pleylistdÉ saxlama dÃŧymÉsini gizlÉt</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_on">SÉsi pleylistdÉ saxlama gizlidir</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_off">SÉsi pleylistdÉ saxlama gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_title">\"Bu sÉsi istifadÉ et\" dÃŧymÉsini gizlÉt</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_summary_on">\"Bu sÉsi istifadÉ et\" dÃŧymÉsi gizlidir</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_summary_off">\"Bu sÉsi istifadÉ et\" dÃŧymÉsi gÃļstÉrilir</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_title">AxtarÄąÅ tÉkliflÉrini gizlÉt</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_summary_on">AxtarÄąÅ tÉkliflÉri gizlÉdilib</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_summary_off">AxtarÄąÅ tÉkliflÉri gÃļstÉrilir</string>
|
||||
@@ -665,7 +686,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_timestamp_summary_off">Vaxt mÃļhÃŧrÃŧ gÃļstÉrilir</string>
|
||||
</patch>
|
||||
<patch id="layout.panels.popup.PlayerPopupPanelsPatch">
|
||||
<string name="revanced_hide_player_popup_panels_title">OynadÄącÄą aÃ§Äąlan pÉncÉrÉ panellÉrini gizlÉt</string>
|
||||
<string name="revanced_hide_player_popup_panels_title">OynadÄącÄą aÃ§Äąlan pÉncÉrÉ panellÉrin gizlÉ</string>
|
||||
<string name="revanced_hide_player_popup_panels_summary_on">OynadÄącÄą aÃ§Äąlan pÉncÉrÉ panellÉri gizlidir</string>
|
||||
<string name="revanced_hide_player_popup_panels_summary_off">OynadÄącÄą aÃ§Äąlan pÉncÉrÉ panellÉri gÃļstÉrilir</string>
|
||||
</patch>
|
||||
@@ -675,7 +696,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_player_overlay_opacity_invalid_toast">OynadÄącÄą ÃļrtÃŧyÃŧnÃŧn qeyri-ÅÉffaflÄąÄÄą 0-100 arasÄą olmalÄądÄąr</string>
|
||||
</patch>
|
||||
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
|
||||
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Gizli</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">\"BÉyÉnmÉmÉ\" mÃŧvÉqqÉti ÉlçatmazdÄąr(API vaxtÄą bitdi)</string>
|
||||
<string name="revanced_ryd_failure_connection_status_code">BÉyÉnmÉmÉ ÉlçatmazdÄąr (status %d)</string>
|
||||
@@ -725,7 +745,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_wide_searchbar_summary_off">GeniÅ axtarÄąÅ Ã§ubuÄu qeyri-aktivdir</string>
|
||||
</patch>
|
||||
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
|
||||
<string name="revanced_restore_old_seekbar_thumbnails_title">KÃļhnÉ axtarÄąÅ Ã§ubuÄu miniatÃŧrlÉrini qaytar</string>
|
||||
<string name="revanced_restore_old_seekbar_thumbnails_title">KÃļhnÉ axtarÄąÅ Ã§ubuÄu miniatÃŧrlÉrin al</string>
|
||||
<string name="revanced_restore_old_seekbar_thumbnails_summary_on">AxtarÄąÅ Ã§ubuÄu miniatÃŧrlÉri axtarÄąÅ Ã§ubuÄu ÃŧstÃŧndÉ gÃļrÃŧnÉcÉk</string>
|
||||
<string name="revanced_restore_old_seekbar_thumbnails_summary_off">AxtarÄąÅ Ã§ubuÄu miniatÃŧrlÉri tam ekranda gÃļrÃŧnÉcÉk</string>
|
||||
</patch>
|
||||
@@ -771,6 +791,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_general_skipcount_sum_off">ÃtÃŧrmÉ sayÄąnÄąn izlÉnmÉsi aktiv deyil</string>
|
||||
<string name="revanced_sb_general_min_duration">Minimum bÃļlÃŧm mÃŧddÉti</string>
|
||||
<string name="revanced_sb_general_min_duration_sum">BÃļlÃŧmlÉr bu dÉyÉrdÉn (saniyÉ olaraq) daha qÄąsadÄąrsa gÃļstÉrilmÉyÉcÉk vÉ ya ÃļtÃŧrÃŧlmÉyÉcÉk</string>
|
||||
<string name="revanced_sb_general_min_duration_invalid">EtibarsÄąz vaxt mÃŧddÉti</string>
|
||||
<string name="revanced_sb_general_uuid">ÅÉxsi istifadÉçi kimliyiniz</string>
|
||||
<string name="revanced_sb_general_uuid_sum">Bu gizli saxlanÄąlmalÄądÄąr. Bu, parol kimidir vÉ heç kimlÉ paylaÅÄąlmamalÄądÄąr. KimsÉ bunu bilsÉ, onlar sizi tÉqlid edÉ bilÉr</string>
|
||||
<string name="revanced_sb_general_uuid_invalid">ÅÉxsi istifadÉçi kimliyiniz Én az 30 simvol uzunluÄunda olmalÄądÄąr</string>
|
||||
@@ -788,7 +809,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_settings_export_failed">%s ixrac uÄursuz</string>
|
||||
<string name="revanced_sb_settings_revanced_export_user_id_warning">TÉnzimlÉmÉlÉr ÅÉxsi SponsorBlock istifadÉçi kimliyi ehtiva edir.\n\nİstifadÉçi kimliyiniz parol kimidir vÉ bu paylaÅÄąlmamalÄądÄąr.\n</string>
|
||||
<string name="revanced_sb_settings_revanced_export_user_id_warning_dismiss">TÉkrar gÃļstÉrmÉ</string>
|
||||
<string name="revanced_sb_diff_segments">Seqment davranÄąÅÄąnÄą dÉyiÅdir</string>
|
||||
<string name="revanced_sb_diff_segments">BÃļlÃŧm davranÄąÅÄąnÄą dÉyiÅdir</string>
|
||||
<string name="revanced_sb_segments_sponsor">Sponsor</string>
|
||||
<string name="revanced_sb_segments_sponsor_sum">ÃdÉniÅli tanÄątÄąm, ÃļdÉniÅli yÃļnlÉndirmÉlÉr vÉ birbaÅa reklamlar. Ãz-tanÄątÄąm vÉ ya bÉyÉndiklÉri sÉbÉblÉrÉ/yaradÄącÄąlara/veb saytlara/mÉhsullara ÃļdÉniÅsiz çaÄÄąrÄąÅlar etmÉk ÃŧçÃŧn deyil</string>
|
||||
<string name="revanced_sb_segments_selfpromo">ÃdÉniÅsiz/Ãz reklamÄą</string>
|
||||
@@ -871,8 +892,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_new_segment_time_start">BÃļlÃŧm baÅladÄąÄÄą vaxt</string>
|
||||
<string name="revanced_sb_new_segment_time_end">BÃļlÃŧmÃŧn bitmÉ vaxtÄą</string>
|
||||
<string name="revanced_sb_new_segment_confirm_title">Vaxtlar dÃŧzgÃŧndÃŧr?</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_confirm_content">BÃļlÃŧm \n\n%1$s\n\n%2$s\n\n(%3$s)\n\nTÉqdim etmÉyÉ hazÄąrsÄąnÄąz?</string>
|
||||
<string name="revanced_sb_new_segment_start_is_before_end">BaÅlanÄÄąc sondan ÉvvÉl olmalÄądÄąr</string>
|
||||
<string name="revanced_sb_new_segment_mark_locations_first">ÆvvÉlcÉ vaxt çubuÄunda iki yeri doldur</string>
|
||||
@@ -891,6 +910,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_stats_username_changed">İstifadÉçi adÄą uÄurla dÉyiÅdirildi</string>
|
||||
<string name="revanced_sb_stats_reputation">NÃŧfuzunuz <b>%.2f</b></string>
|
||||
<string name="revanced_sb_stats_submissions"><b>%s</b> bÃļlÃŧm yaratdÄąnÄąz</string>
|
||||
<string name="revanced_sb_stats_submissions_sum">BÃļlÃŧmlÉrinizÉ baxmaq ÃŧçÃŧn bura toxunun</string>
|
||||
<string name="revanced_sb_stats_saved_zero">SponsorBlock liderlik lÃļvhÉsi</string>
|
||||
<string name="revanced_sb_stats_saved">İnsanlarÄą <b>%s</b> bÃļlÃŧmdÉn xilas etdiniz</string>
|
||||
<string name="revanced_sb_stats_saved_sum_zero">Qlobal statistikalarÄą vÉ baÅlÄąca tÃļhfÉçilÉri gÃļrmÉk ÃŧçÃŧn bura toxunun</string>
|
||||
@@ -942,7 +962,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_start_page_entry_9">TrenddÉ olan</string>
|
||||
</patch>
|
||||
<patch id="layout.startupshortsreset.DisableResumingShortsOnStartupPatch">
|
||||
<string name="revanced_disable_resuming_shorts_player_title">Shorts oynadÄącÄą davam etdirmÉsini baÄla</string>
|
||||
<string name="revanced_disable_resuming_shorts_player_title">Shorts oynadÄącÄą baÅladÄącÄąnÄą baÄla</string>
|
||||
<string name="revanced_disable_resuming_shorts_player_summary_on">TÉtbiq aÃ§Äąlanda Shorts oynadÄącÄą davam etmÉyÉcÉk</string>
|
||||
<string name="revanced_disable_resuming_shorts_player_summary_off">TÉtbiq aÃ§Äąlanda Shorts oynadÄącÄą davam edÉcÉk</string>
|
||||
</patch>
|
||||
@@ -1004,7 +1024,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_alt_thumbnail_search_title">AxtarÄąÅ nÉticÉlÉri</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_1">Orijinal miniatÃŧrlÉr</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & Orijinal miniatÃŧrlÉr</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & Kadr çÉkiliÅlÉri</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & Kadr çÉkiliÅlÉr</string>
|
||||
<string name="revanced_alt_thumbnail_options_entry_4">Kadr çÉkiliÅlÉri</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_about_summary">DeArrow YouTube videolarÄą ÃŧçÃŧn bÃļlÃŧk mÉnbÉli miniatÃŧrlÉr tÉchiz edir. Bu miniatÃŧrlÉr hÉr zaman YouTube tÉrÉfindÉn tÉmin edilÉnlÉrdÉn daha uyÄun olur\n\nÆgÉr aktivlÉÅdirilÉrsÉ, video URL-lÉr API serverinÉ gÃļndÉrilÉcÉk vÉ baÅqa heç bir mÉlumat gÃļndÉrilmÉyÉcÉk. Videoda DeArrow miniatÃŧrlÉri yoxdursa, orijinal vÉ ya hÉlÉ dÉ kadr çÉkiliÅlÉri gÃļstÉrilir\n\nDeArrow haqqÄąnda ÉtraflÄą ÃļyrÉnmÉk ÃŧçÃŧn bura toxun</string>
|
||||
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">API Élçatan deyilsÉ ani bildiriÅ gÃļstÉr</string>
|
||||
@@ -1033,6 +1053,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_announcements_connection_failed">Elan provayderinÉ baÄlanmaq olmadÄą</string>
|
||||
<string name="revanced_announcements_dialog_dismiss">LÉÄv et</string>
|
||||
</patch>
|
||||
<patch id="misc.dns.CheckWatchHistoryDomainNameResolutionPatch">
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_title">XĶbĶrdarlÄąq</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_message">BaxÄąÅ tarixçÉniz saxlanmÄąr.<br><br>Bu çox gÃŧman ki, DNS reklam bloklayÄącÄą vÉ ya ÅÉbÉkÉ proksisinÉ gÃļrÉdir.<br><br>.Bunu dÃŧzÉltmÉk ÃŧçÃŧn s.youtube.com-u</b> <b>aÄ siyahÄąya salÄąn vÉ ya bÃŧtÃŧn DNS bloklayÄącÄąlarÄą vÉ proksilÉri baÄlayÄąn.</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_ignore">TÉkrar gÃļstÉrmÉ</string>
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
<string name="revanced_auto_repeat_title">Avto-tÉkrarlamanÄą aktivlÉÅdir</string>
|
||||
<string name="revanced_auto_repeat_summary_on">Avtomatik tÉkrar aktivlÉÅdirilib</string>
|
||||
@@ -1059,9 +1084,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_external_browser_summary_off">BaÄlantÄąlar tÉtbiqdÉ aÃ§ÄąlÄąr</string>
|
||||
</patch>
|
||||
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
|
||||
<string name="revanced_remove_tracking_query_parameter_title">İzlÉmÉ sorÄusu parametrini sil</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_on">İzlÉmÉ sorÄusu parametri baÄlantÄąlardan silinir</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_off">İzlÉmÉ sorÄusu parametri baÄlantÄąlardan silinmir</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_title">İzlÉmÉ sorÄusu faktorun sil</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_on">İzlÉmÉ sorÄusu faktoru baÄlantÄąlardan silinir</string>
|
||||
<string name="revanced_remove_tracking_query_parameter_summary_off">İzlÉmÉ sorÄusu faktoru baÄlantÄąlardan silinmir</string>
|
||||
</patch>
|
||||
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
|
||||
<string name="revanced_disable_zoom_haptics_title">YaxÄąnlaÅdÄąrma Éks-ÉlaqÉsini baÄla</string>
|
||||
@@ -1114,9 +1139,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_client_summary_on">QÉbuledici saxtalaÅdÄąrÄąldÄą</string>
|
||||
<string name="revanced_spoof_client_summary_off">QÉbuledici dÉyiÅmÉyib\n\nVideo oynatma iÅlÉmÉyÉ bilÉr</string>
|
||||
<string name="revanced_spoof_client_user_dialog_message">Bu seçimin baÄlanmasÄą, video oynatma problemlÉrinÉ sÉbÉb ola bilÉr.</string>
|
||||
<string name="revanced_spoof_client_use_ios_title">QÉbuledicini iOS olaraq saxtalaÅdÄąr</string>
|
||||
<string name="revanced_spoof_client_use_ios_summary_on">QÉbuledici hazÄąrda iOS-a saxtalaÅdÄąrÄąlÄąb\n\nYan tÉsirlÉrÉ daxildir:\nâĸ HDR video yoxdur\nâĸ Daha yÃŧksÉk video keyfiyyÉtlÉr olmaya bilÉr\nâĸ CanlÄą yayÄąmlar yalnÄąz sÉs kimi oynadÄąla bilmÉz</string>
|
||||
<string name="revanced_spoof_client_use_ios_summary_off">QÉbuledici hazÄąrda Android VR\'É saxtalaÅdÄąrÄąlÄąb \n\nYan tÉsirlÉrÉ daxildir:\nâĸ HDR video yoxdur\nâĸ UÅaq videolarÄą oynadÄąlmÄąr\nâĸ FasilÉ verilmiÅ videolar gÃļzlÉnilmÉdÉn davam edÉ bilÉr\nâĸ AÅaÄÄą keyfiyyÉtli Shorts axtarma çubuÄu miniatÃŧrlÉri\nâĸ \"YÃŧklÉ\" fÉaliyyÉt dÃŧymÉsi gizlidir\nâĸ BitiÅ ekran kartlarÄą gizlidir</string>
|
||||
<string name="revanced_spoof_client_type_title">QÉbuledici saxtalaÅdÄąrma nÃļvÃŧ</string>
|
||||
<string name="revanced_spoof_client_ios_force_avc_title">iOS AVC-yÉ Zorla (H.264)</string>
|
||||
<string name="revanced_spoof_client_ios_force_avc_summary_on">iOS video kodlayÄącÄą AVC-dir</string>
|
||||
<string name="revanced_spoof_client_ios_force_avc_summary_off">iOS video kodlayÄącÄą AVC, VP9 vÉ ya AV1-dir</string>
|
||||
<string name="revanced_spoof_client_ios_force_avc_user_dialog_message">Bunu aktivlÉÅdirmÉ batareya ÃļmrÃŧnÃŧ yaxÅÄąlaÅdÄąra vÉ oynatma donmasÄąnÄą dÃŧzÉldÉ bilÉr.\n\nAVC maksimum 1080p gÃļrÃŧntÃŧ imkanÄąna malikdir vÉ video oynadÄąlmasÄą VP9 vÉ ya AV1-dÉn daha çox internet mÉlumatÄąndan istifadÉ edÉcÉk.</string>
|
||||
<string name="revanced_spoof_client_about_android_ios_title">iOS saxtakarlÄąÄÄąnÄąn yan tÉsirlÉri</string>
|
||||
<string name="revanced_spoof_client_about_android_ios_summary">âĸ HDR yalnÄąz AV1 kodlayÄącÄą ilÉ dÉstÉklÉnir\nâĸ BaxÄąÅ tarixçÉsi ÃļdÉyici hesab ilÉ iÅlÉmir</string>
|
||||
<string name="revanced_spoof_client_about_android_vr_title">Android VR saxtakarlÄąÄÄą yan tÉsirlÉri</string>
|
||||
<string name="revanced_spoof_client_about_android_vr_summary">âĸ HDR video yoxdurâĸ UÅaq videolarÄą oynadÄąlmÄąr\nâĸ FasilÉ verilmiÅ videolar gÃļzlÉnilmÉdÉn davam edÉ bilÉr\nâĸ AÅaÄÄą keyfiyyÉtli Shorts axtarma çubuÄu miniatÃŧrlÉri\nâĸ \"YÃŧklÉ\" fÉaliyyÉt dÃŧymÉsi gizlidir\nâĸ BitiÅ ekran kartlarÄą gizlidir</string>
|
||||
<string name="revanced_spoof_client_storyboard_timeout">Client kiçik ÅÉkillÉrini tÉqlid etmÉ ÉlçatmazdÄąr (API vaxtÄą bitdi)</string>
|
||||
<string name="revanced_spoof_client_storyboard_io_exception">Client kiçik ÅÉkillÉrini tÉqlid etmÉ mÃŧvÉqqÉti ÉlçatmazdÄąr: %s</string>
|
||||
</patch>
|
||||
|
||||
@@ -32,6 +32,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
<string name="revanced_settings_confirm_user_dialog_title">ĐŅ Ņ
ĐžŅаŅĐĩ ĐŋŅаŅŅĐŗĐŊŅŅŅ?</string>
|
||||
<string name="revanced_settings_reset">ĐĄĐēŅĐŊŅŅŅ</string>
|
||||
@@ -42,6 +44,13 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_settings_import_reset">ĐаĐģĐ°Đ´Ņ ReVanced ŅĐēŅĐŊŅŅŅ Đ´Đ° ŅŅаĐŊдаŅŅĐŊŅŅ
</string>
|
||||
<string name="revanced_settings_import_success">ĐĐŧĐŋаŅŅаваĐŊа %d ĐŊаĐģад</string>
|
||||
<string name="revanced_settings_import_failure_parse">ĐаĐŧŅĐģĐēа ŅĐŧĐŋаŅŅŅ: %s</string>
|
||||
<string name="revanced_pref_import_export_title">ĐĐŧĐŋаŅŅ / ĐĐēŅĐŋаŅŅ</string>
|
||||
<string name="revanced_pref_import_export_summary">ĐĐŧĐŋаŅŅ / ĐĐēŅĐŋаŅŅ ĐŊаĐģад ReVanced</string>
|
||||
<!-- Settings about dialog. -->
|
||||
<string name="revanced_settings_about_links_body">ĐŅ Đ˛ŅĐēаŅŅŅŅĐžŅваĐĩŅĐĩ вĐĩŅŅŅŅ ReVanced Patches <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>
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
@@ -54,14 +63,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
<string name="revanced_settings_about_links_body">ĐŅ Đ˛ŅĐēаŅŅŅŅĐžŅваĐĩŅĐĩ вĐĩŅŅŅŅ ReVanced Patches <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>
|
||||
<string name="revanced_pref_import_export_title">ĐĐŧĐŋаŅŅ / ĐĐēŅĐŋаŅŅ</string>
|
||||
<string name="revanced_pref_import_export_summary">ĐĐŧĐŋаŅŅ / ĐĐēŅĐŋаŅŅ ĐŊаĐģад ReVanced</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.SettingsPatch">
|
||||
<string name="revanced_settings_screen_00_about_title">ĐŅа ĐŋŅĐ°ĐŗŅаĐŧŅ</string>
|
||||
<string name="revanced_settings_screen_01_ads_title">ĐĐą\"ŅвŅ</string>
|
||||
@@ -211,6 +212,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_info_cards_section_title">ĐĄŅ
аваŅŅ ŅаСдСĐĩĐģ ŅĐŊŅаŅĐŧаŅŅĐšĐŊŅŅ
ĐēаŅŅ</string>
|
||||
<string name="revanced_hide_info_cards_section_summary_on">РаСдСĐĩĐģ ŅĐŊŅаŅĐŧаŅŅĐšĐŊŅŅ
ĐēаŅŅаĐē ŅŅ
аваĐŊŅ</string>
|
||||
<string name="revanced_hide_info_cards_section_summary_off">ĐаĐēаСваĐĩŅŅа ŅаСдСĐĩĐģ ŅĐŊŅаŅĐŧаŅŅĐšĐŊŅŅ
ĐēаŅŅ</string>
|
||||
<string name="revanced_hide_key_concepts_section_title">ĐĄŅ
аваŅŅ ŅаСдСĐĩĐģ ÂĢĐĐģŅŅавŅŅ ĐŋаĐŊŅŅŅŅÂģ</string>
|
||||
<string name="revanced_hide_key_concepts_section_summary_on">РаСдСĐĩĐģ \"ĐĐģŅŅавŅŅ ĐŋаĐŊŅŅŅŅ\" ŅŅ
аваĐŊŅ</string>
|
||||
<string name="revanced_hide_key_concepts_section_summary_off">ĐаĐēаСаĐŊŅ ŅаСдСĐĩĐģ ÂĢĐĐģŅŅавŅŅ ĐŋаĐŊŅŅŅŅÂģ</string>
|
||||
<string name="revanced_hide_transcript_section_title">ĐĄŅ
аваŅŅ ŅаСдСĐĩĐģ ŅŅŅĐŊĐ°ĐŗŅаĐŧŅ</string>
|
||||
<string name="revanced_hide_transcript_section_summary_on">РаСдСĐĩĐģ ŅŅŅĐŊĐ°ĐŗŅаĐŧŅ ŅŅ
аваĐŊŅ</string>
|
||||
<string name="revanced_hide_transcript_section_summary_off">ĐаĐēаСваĐĩŅŅа ŅаСдСĐĩĐģ ŅŅŅĐŊĐ°ĐŗŅаĐŧŅ</string>
|
||||
@@ -239,14 +243,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_keyword_content_phrases_title">ĐĐģŅŅавŅŅ ŅĐģОвŅ, ŅĐēŅŅ ŅŅŅйа ŅŅ
аваŅŅ</string>
|
||||
<!-- 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. -->
|
||||
<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 _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<!-- 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>
|
||||
</patch>
|
||||
<patch id="ad.general.HideAdsResourcePatch">
|
||||
<string name="revanced_hide_general_ads_title">ĐĄŅ
аваŅŅ Đ°ĐŗŅĐģŅĐŊŅŅ ŅŅĐēĐģаĐŧŅ</string>
|
||||
@@ -427,7 +426,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_home_button_summary_on">ĐĐŊĐžĐŋĐēа \"ĐадОĐŧŅ\" ŅŅ
аваĐŊа</string>
|
||||
<string name="revanced_hide_home_button_summary_off">ĐаĐēаСаĐŊа ĐēĐŊĐžĐŋĐēа \"ĐадОĐŧŅ\".</string>
|
||||
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
|
||||
<string name="revanced_hide_shorts_button_title">ĐĄŅ
аваŅŅ ŅĐžŅŅŅ</string>
|
||||
<string name="revanced_hide_shorts_button_title">ĐĄŅ
аваŅŅ ĐēĐŊĐžĐŋĐēŅ \"Shorts\"</string>
|
||||
<string name="revanced_hide_shorts_button_summary_on">ĐĐŊĐžĐŋĐēа Shorts ŅŅ
аваĐŊа</string>
|
||||
<string name="revanced_hide_shorts_button_summary_off">ĐаĐēаСаĐŊа ĐēĐŊĐžĐŋĐēа Shorts</string>
|
||||
<!-- The Create button has no display name. Translate normally. -->
|
||||
@@ -675,7 +674,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_player_overlay_opacity_invalid_toast">ĐĐĩĐŋŅаСŅŅŅŅаŅŅŅ ĐŊаĐēĐģадаĐŊĐŊŅ ĐŋŅĐ°ĐšĐŗŅаваĐģŅĐŊŅĐēа ĐŋавŅĐŊĐŊа ĐąŅŅŅ ĐŋаĐŧŅĐļ 0-100</string>
|
||||
</patch>
|
||||
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
|
||||
<string name="revanced_ryd_video_likes_hidden_by_video_owner">ĐĄŅ
аваĐŊŅ</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">ĐдСĐŊаĐēŅ \"ĐĐĩ ĐŋадайаĐĩŅŅа\" ŅаŅОва ĐŊĐĩдаŅŅŅĐŋĐŊŅŅ (ŅĐ°Ņ ŅаĐēаĐŊĐŊŅ API ŅĐēĐžĐŊŅŅŅŅŅ)</string>
|
||||
<string name="revanced_ryd_failure_connection_status_code">ĐŅСĐģаКĐēŅ ĐŊĐĩдаŅŅŅĐŋĐŊŅŅ (ŅŅаŅŅŅ %d)</string>
|
||||
@@ -864,14 +862,13 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_new_segment_choose_category">ĐŅĐąĐĩŅŅŅĐĩ ĐēаŅŅĐŗĐžŅŅŅ ŅĐĩĐŗĐŧĐĩĐŊŅа</string>
|
||||
<string name="revanced_sb_new_segment_disabled_category">ĐаŅŅĐŗĐžŅŅŅ Đ°Đ´ĐēĐģŅŅаĐŊа Ņ ĐŊаĐģадаŅ
. ĐŖĐēĐģŅŅŅŅĐĩ ĐēаŅŅĐŗĐžŅŅŅ Đ´ĐģŅ Đ°Đ´ĐŋŅаŅĐēŅ.</string>
|
||||
<string name="revanced_sb_new_segment_title">ĐĐžĐ˛Ņ ŅĐĩĐŗĐŧĐĩĐŊŅ SponsorBlock</string>
|
||||
<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>
|
||||
<string name="revanced_sb_new_segment_time_start">Đ§Đ°Ņ ĐŋаŅаŅĐēŅ ŅĐĩĐŗĐŧĐĩĐŊŅа</string>
|
||||
<string name="revanced_sb_new_segment_time_end">Đ§Đ°Ņ ĐˇĐ°ĐēаĐŊŅŅĐŊĐŊŅ ŅĐĩĐŗĐŧĐĩĐŊŅа</string>
|
||||
<string name="revanced_sb_new_segment_confirm_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_confirm_content">ĐĄĐĩĐŗĐŧĐĩĐŊŅ Đ°Đ´\n\n%1$s\nда\n%2$s\n\n(%3$s)\n\nĐаŅĐžĐ˛Ņ Đ°Đ´ĐŋŅавŅŅŅ?</string>
|
||||
<string name="revanced_sb_new_segment_start_is_before_end">ĐаŅŅĐŊаŅŅ ŅŅŅйа ŅаĐŊĐĩĐš Са ĐēаĐŊĐĩŅ</string>
|
||||
<string name="revanced_sb_new_segment_mark_locations_first">ĐĄĐŋаŅаŅĐēŅ Đ°Đ´ĐˇĐŊаŅŅĐĩ два ĐŧĐĩŅŅŅ ĐŊа ĐŋаĐŊŅĐģŅ ŅаŅŅ</string>
|
||||
@@ -988,6 +985,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<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. -->
|
||||
@@ -1029,6 +1029,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_announcements_connection_failed">ĐĐĩ ŅдаĐģĐžŅŅ ĐŋадĐēĐģŅŅŅŅŅа да ĐŋаŅŅаŅŅŅŅĐēа ай\"ŅŅ</string>
|
||||
<string name="revanced_announcements_dialog_dismiss">ŅаŅŅĐģайŅŅŅа</string>
|
||||
</patch>
|
||||
<patch id="misc.dns.CheckWatchHistoryDomainNameResolutionPatch">
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_title">ĐŖĐ˛Đ°ĐŗĐ°</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_ignore">ĐĐžĐģŅŅ ĐŊĐĩ ĐŋаĐēаСваŅŅ</string>
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
<string name="revanced_auto_repeat_title">ĐŖĐēĐģŅŅŅŅŅ Đ°ŅŅаĐŧаŅŅŅĐŊŅ ĐŋаŅŅĐžŅ</string>
|
||||
<string name="revanced_auto_repeat_summary_on">ĐŅŅаĐŧаŅŅŅĐŊŅ ĐŋаŅŅĐžŅ ŅĐēĐģŅŅаĐŊŅ</string>
|
||||
@@ -1110,7 +1114,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_client_summary_on">ĐĐģŅĐĩĐŊŅ ĐŋадŅОйĐģĐĩĐŊŅ</string>
|
||||
<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_about_android_vr_summary">âĸ ĐŅĐŧа HDR-вŅĐ´Ņа\nâĸ ĐСŅŅŅŅŅŅ Đ˛ŅĐ´Ņа ĐŊĐĩ ĐŋŅĐ°ĐšĐŗŅаваŅŅŅа\nâĸ ĐŅŅĐŋŅĐŊĐĩĐŊŅŅ Đ˛ŅĐ´Ņа ĐŧĐžĐŗŅŅŅ Đ°Đ´ĐŊаŅĐģŅŅŅа вŅĐŋадĐēОвŅĐŧ ŅŅĐŊаĐŧ\nâĸ ĐŅСĐēĐ°Ņ ŅĐēаŅŅŅ ĐŧŅĐŊŅŅŅŅŅ ĐŊа ĐŋаĐŊŅĐģŅ ĐŋĐžŅŅĐēŅ Shorts\nâĸ ĐĐŊĐžĐŋĐēа дСĐĩŅĐŊĐŊŅ ĐĄĐŋаĐŧĐŋаваŅŅ ŅŅ
аваĐŊа\nâĸ ĐаŅŅĐēŅ ĐēаĐŊŅĐ°Đ˛ĐžĐŗĐ° ŅĐēŅаĐŊа ŅŅ
аваĐŊŅ</string>
|
||||
<string name="revanced_spoof_client_storyboard_timeout">ĐŅĐŊŅŅŅŅŅŅ ĐēĐģŅĐĩĐŊŅа Spoof ĐŊĐĩдаŅŅŅĐŋĐŊŅŅ (ŅĐ°Ņ ŅаĐēаĐŊĐŊŅ API ŅĐēĐžĐŊŅŅŅŅŅ)</string>
|
||||
<string name="revanced_spoof_client_storyboard_io_exception">ĐŅĐŊŅŅŅŅŅŅ ĐēĐģŅĐĩĐŊŅа Spoof ŅаŅОва ĐŊĐĩдаŅŅŅĐŋĐŊŅŅ: %s</string>
|
||||
</patch>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
<string name="revanced_settings_confirm_user_dialog_title">āĻāĻĒāύāĻŋ āĻāĻŋ āĻāĻāĻŋā§ā§ āϝā§āϤ⧠āĻāĻā§āĻā§āĻ?</string>
|
||||
<string name="revanced_settings_reset">āĻāĻŦāĻžāϰ āϏā§āĻ āĻāϰā§āύ</string>
|
||||
@@ -42,6 +44,13 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_settings_import_reset">ReVanced āϏā§āĻāĻŋāĻ āĻĄāĻŋāĻĢāϞā§āĻ āϏā§āĻ āĻāϰāĻž āĻšā§ā§āĻā§</string>
|
||||
<string name="revanced_settings_import_success">%d āϏā§āĻāĻŋāĻ āĻāĻŽāĻĻāĻžāύāĻŋ āĻšā§ā§āĻā§</string>
|
||||
<string name="revanced_settings_import_failure_parse">āĻāĻŽāĻĻāĻžāύāĻŋ āĻāϰāĻž āϝāĻžā§āύāĻŋ: %s</string>
|
||||
<string name="revanced_pref_import_export_title">āĻāĻŽāĻĻāĻžāύāĻŋ āĻāĻŦāĻ āϰāĻĒā§āϤāĻžāύāĻŋ</string>
|
||||
<string name="revanced_pref_import_export_summary">ReVanced āϏā§āĻāĻŋāĻ āĻāĻŽāĻĻāĻžāύāĻŋ āĻŦāĻž āϰāĻĒā§āϤāĻžāύāĻŋ āĻāϰā§āύ</string>
|
||||
<!-- Settings about dialog. -->
|
||||
<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>
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
@@ -54,14 +63,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
<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>
|
||||
<string name="revanced_pref_import_export_title">āĻāĻŽāĻĻāĻžāύāĻŋ āĻāĻŦāĻ āϰāĻĒā§āϤāĻžāύāĻŋ</string>
|
||||
<string name="revanced_pref_import_export_summary">ReVanced āϏā§āĻāĻŋāĻ āĻāĻŽāĻĻāĻžāύāĻŋ āĻŦāĻž āϰāĻĒā§āϤāĻžāύāĻŋ āĻāϰā§āύ</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.SettingsPatch">
|
||||
<string name="revanced_settings_screen_00_about_title">āϏāĻŽā§āĻĒāϰā§āĻāĻŋāϤ</string>
|
||||
<string name="revanced_settings_screen_01_ads_title">āĻŦāĻŋāĻā§āĻāĻžāĻĒāύ</string>
|
||||
@@ -201,6 +202,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<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_attributes_section_summary_off">\'āĻŦā§āĻļāĻŋāώā§āĻā§āϝāϝā§āĻā§āϤ āϏā§āĻĨāĻžāύ\', āĻā§āĻŽ āĻāĻŦāĻ āϏāĻā§āĻā§āϤ āĻŦāĻŋāĻāĻžāĻāĻā§āϞāĻŋ āĻĒā§āϰāĻĻāϰā§āĻļāĻŋāϤ āĻšā§ā§āĻā§</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>
|
||||
@@ -231,13 +233,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_keyword_content_phrases_title">āϞā§āĻāĻžāύā§āϰ āĻāύā§āϝ āĻā§āĻā§āĻžāϰā§āĻĄ</string>
|
||||
<!-- 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. -->
|
||||
<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 _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<!-- 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">
|
||||
<string name="revanced_hide_general_ads_title">āϏāĻžāϧāĻžāϰāĻŖ āĻŦāĻŋāĻā§āĻāĻžāĻĒāύ āϞā§āĻāĻžāύ</string>
|
||||
@@ -603,7 +601,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_player_overlay_opacity_invalid_toast">āĻĒā§āϞā§ā§āĻžāϰ āĻāĻāĻžāϰāϞ⧠āĻ
āϏā§āĻŦāĻā§āĻāϤāĻž āĻ
āĻŦāĻļā§āϝāĻ ā§Ļ-ā§§ā§Ļā§Ļ āĻāϰ āĻŽāϧā§āϝ⧠āĻšāϤ⧠āĻšāĻŦā§</string>
|
||||
</patch>
|
||||
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
|
||||
<string name="revanced_ryd_video_likes_hidden_by_video_owner">āϞā§āĻāĻŋā§ā§ āϰā§ā§āĻā§</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">āĻ
āĻĒāĻāύā§āĻĻ āϏāĻžāĻŽā§āĻŋāĻāĻāĻžāĻŦā§ āĻāĻĒāϞāĻā§āϝ āύ⧠(API āϏāĻŽā§ āĻļā§āώ āĻšā§ā§āĻā§)</string>
|
||||
<string name="revanced_ryd_failure_connection_status_code">āĻ
āĻĒāĻāύā§āĻĻ āĻāĻĒāϞāĻā§āϝ āύ⧠(āĻ
āĻŦāϏā§āĻĨāĻž %d)</string>
|
||||
@@ -798,8 +795,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_new_segment_time_start">āϏā§āĻāĻŽā§āύā§āĻāĻāĻŋ āĻļā§āϰ⧠āĻšāĻāϝāĻŧāĻžāϰ āϏāĻŽāϝāĻŧ</string>
|
||||
<string name="revanced_sb_new_segment_time_end">āϏā§āĻāĻŽā§āύā§āĻāĻāĻŋ āĻļā§āώ āĻšāĻāϝāĻŧāĻžāϰ āϏāĻŽāϝāĻŧ</string>
|
||||
<string name="revanced_sb_new_segment_confirm_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_confirm_content">āϏā§āĻāĻŽā§āύā§āĻ āϏāĻŽā§āĻāĻžāϞ\n\n%1$s\nāĻĨā§āĻā§\n%2$s\n\n(%3$s)\n\nāĻāĻŽāĻž āĻāϰāĻžāϰ āĻāύā§āϝ āĻĒā§āϰāϏā§āϤā§āϤ?</string>
|
||||
<string name="revanced_sb_new_segment_start_is_before_end">āĻļā§āϰ⧠āĻ
āĻŦāĻļā§āϝāĻ āĻļā§āώā§āϰ āĻāĻā§ āĻšāϤ⧠āĻšāĻŦā§</string>
|
||||
<string name="revanced_sb_new_segment_mark_locations_first">āĻāĻā§ āϏāĻŽā§ āĻŦāĻžāϰ⧠āĻĻā§āĻāĻŋ āĻ
āĻŦāϏā§āĻĨāĻžāύ āĻāĻŋāĻšā§āύāĻŋāϤ āĻāϰā§āύ</string>
|
||||
@@ -958,6 +953,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_announcements_connection_failed">āĻā§āώāύāĻžāĻĻāĻžāϤāĻžāϰ āϏāĻžāĻĨā§ āϏāĻŽā§āĻĒāϰā§āĻ āϏā§āĻĨāĻžāĻĒāύ āĻŦā§āϝāϰā§āĻĨ āĻšā§ā§āĻā§</string>
|
||||
<string name="revanced_announcements_dialog_dismiss">āĻŦāĻžāϤāĻŋāϞ āĻāϰā§āύ</string>
|
||||
</patch>
|
||||
<patch id="misc.dns.CheckWatchHistoryDomainNameResolutionPatch">
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_title">āϏāϤāϰā§āĻā§āĻāϰāĻŖ</string>
|
||||
<string name="revanced_check_watch_history_domain_name_dialog_ignore">āĻāĻŦāĻžāϰ āĻĻā§āĻāĻžāĻŦā§āύ āύāĻž</string>
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
<string name="revanced_auto_repeat_title">āϏā§āĻŦā§āĻāĻā§āϰāĻŋā§āĻāĻžāĻŦā§-āĻāĻŦāĻžāϰ āĻĻā§āĻāĻžāύ⧠āϏāĻā§āϰāĻŋā§ āĻāϰā§āύ</string>
|
||||
<string name="revanced_auto_repeat_summary_on">āϏā§āĻŦā§āĻāĻā§āϰāĻŋā§āĻāĻžāĻŦā§-āĻāĻŦāĻžāϰ āĻĻā§āĻāĻžāύ⧠āϏāĻā§āϰāĻŋā§ āĻšā§ā§āĻā§</string>
|
||||
@@ -1039,7 +1038,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_client_summary_on">āĻā§āϞāĻžā§ā§āύā§āĻ āϏā§āĻĒā§āĻĢ āĻāϰāĻž āĻšā§ā§āĻā§</string>
|
||||
<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_about_android_vr_summary">âĸ āĻā§āύāĻ 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>
|
||||
|
||||
@@ -32,15 +32,16 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
<!-- Settings about dialog. -->
|
||||
</patch>
|
||||
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
</patch>
|
||||
</app>
|
||||
<app id="youtube">
|
||||
<patch id="misc.settings.SettingsResourcePatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.SettingsPatch">
|
||||
</patch>
|
||||
<patch id="misc.debugging.DebuggingPatch">
|
||||
@@ -57,7 +58,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<!-- '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 _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<!-- 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">
|
||||
@@ -171,8 +172,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
|
||||
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
|
||||
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
|
||||
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
|
||||
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
|
||||
<!-- Shown in the settings preferences, and translations can be any text length. -->
|
||||
</patch>
|
||||
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
|
||||
@@ -206,6 +205,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
<patch id="misc.announcements.AnnouncementsPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.CheckWatchHistoryDomainNameResolutionPatch">
|
||||
</patch>
|
||||
<patch id="misc.autorepeat.AutoRepeatPatch">
|
||||
</patch>
|
||||
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user