mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-08 10:23:55 +01:00
Compare commits
71 Commits
v4.13.3-de
...
v4.16.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b49012130 | ||
|
|
4c7b018878 | ||
|
|
5ddd957313 | ||
|
|
bb0dcbe83d | ||
|
|
163736fb26 | ||
|
|
0c6db43bde | ||
|
|
317e9a80eb | ||
|
|
464e6a3673 | ||
|
|
2e9142eda4 | ||
|
|
b4c6d0a7d2 | ||
|
|
c0ee85e12a | ||
|
|
2d326072e2 | ||
|
|
586770aa3a | ||
|
|
9f314c2425 | ||
|
|
c8b3456738 | ||
|
|
e8cb6ee028 | ||
|
|
3e796eb7c2 | ||
|
|
303d2de81d | ||
|
|
0ab7344295 | ||
|
|
ff8fe46685 | ||
|
|
a104eeaf68 | ||
|
|
18b09168cc | ||
|
|
f7209f0a53 | ||
|
|
1fb3fc4857 | ||
|
|
e03c14cc01 | ||
|
|
bed29d00dc | ||
|
|
d36982e245 | ||
|
|
13031f0534 | ||
|
|
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 | ||
|
|
ada642f4a7 |
235
CHANGELOG.md
235
CHANGELOG.md
@@ -1,3 +1,238 @@
|
|||||||
|
# [4.16.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.16.0-dev.1...v4.16.0-dev.2) (2024-09-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Hide Shorts components:** Add patch option to hide Shorts from app launcher widget Beta ([#3707](https://github.com/ReVanced/revanced-patches/issues/3707)) ([838f183](https://github.com/ReVanced/revanced-patches/commit/838f1834a5df547ce2c3217b874c0594b6878a67))
|
||||||
|
|
||||||
|
# [4.16.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.15.1-dev.2...v4.16.0-dev.1) (2024-09-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Hide Shorts components:** Add patch option to hide Shorts app shortcut (long press app icon) ([#3699](https://github.com/ReVanced/revanced-patches/issues/3699)) ([0d4e1f5](https://github.com/ReVanced/revanced-patches/commit/0d4e1f5d03cf3dcc06fd41165e26a1ce901b976b))
|
||||||
|
|
||||||
|
## [4.15.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.15.1-dev.1...v4.15.1-dev.2) (2024-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Show video chapter titles without clipping when overlay buttons are enabled ([#3674](https://github.com/ReVanced/revanced-patches/issues/3674)) ([4b88c31](https://github.com/ReVanced/revanced-patches/commit/4b88c316ed90c56e83e2aee266561833b36fc37d))
|
||||||
|
|
||||||
|
## [4.15.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.15.0...v4.15.1-dev.1) (2024-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Twitter - Open links with app chooser:** Fix incorrect version in compatibility list ([#3683](https://github.com/ReVanced/revanced-patches/issues/3683)) ([adafe85](https://github.com/ReVanced/revanced-patches/commit/adafe85d77f6a0031a5523b9b7da69475959d78d))
|
||||||
|
|
||||||
|
# [4.15.0](https://github.com/ReVanced/revanced-patches/compare/v4.14.1...v4.15.0) (2024-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok - Playback speed:** Prevent crash by fixing invalid patch ([82d53cb](https://github.com/ReVanced/revanced-patches/commit/82d53cbc3bbfa585ba4337fdfaec9f0f19c802e6))
|
||||||
|
* **TikTok - Settings:** Prevent crash by fixing invalid patch ([8074032](https://github.com/ReVanced/revanced-patches/commit/8074032fad3eff1c03296a882d2e2820da99b592))
|
||||||
|
* **Twitter - Open links with app chooser:** Constrain patch to last working version `10.48.0-release` ([b9955d5](https://github.com/ReVanced/revanced-patches/commit/b9955d5ff6e456593b01f0f25d80ff660d02082a))
|
||||||
|
* **YouTube - Spoof video streams:** Change default client type to Android VR ([74c8637](https://github.com/ReVanced/revanced-patches/commit/74c8637943347078955f51325bc6af92a35d4463))
|
||||||
|
* **YouTube - Spoof video streams:** Change default client type to Android VR ([#3672](https://github.com/ReVanced/revanced-patches/issues/3672)) ([a3306f6](https://github.com/ReVanced/revanced-patches/commit/a3306f6717a09b734354f00363a96abad0ae14e7))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **TikTok:** Bump patches to support the latest version 36.5.4 ([e5dcb72](https://github.com/ReVanced/revanced-patches/commit/e5dcb72597092fb32003f11fdf6f861ede4e7ff3))
|
||||||
|
|
||||||
|
# [4.15.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.14.2-dev.2...v4.15.0-dev.1) (2024-09-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **TikTok:** Bump patches to support the latest version 36.5.4 ([e5dcb72](https://github.com/ReVanced/revanced-patches/commit/e5dcb72597092fb32003f11fdf6f861ede4e7ff3))
|
||||||
|
|
||||||
|
## [4.14.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.14.2-dev.1...v4.14.2-dev.2) (2024-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Change default client type to Android VR ([74c8637](https://github.com/ReVanced/revanced-patches/commit/74c8637943347078955f51325bc6af92a35d4463))
|
||||||
|
* **YouTube - Spoof video streams:** Change default client type to Android VR ([#3672](https://github.com/ReVanced/revanced-patches/issues/3672)) ([a3306f6](https://github.com/ReVanced/revanced-patches/commit/a3306f6717a09b734354f00363a96abad0ae14e7))
|
||||||
|
|
||||||
|
## [4.14.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.14.1...v4.14.2-dev.1) (2024-09-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok - Playback speed:** Prevent crash by fixing invalid patch ([82d53cb](https://github.com/ReVanced/revanced-patches/commit/82d53cbc3bbfa585ba4337fdfaec9f0f19c802e6))
|
||||||
|
* **TikTok - Settings:** Prevent crash by fixing invalid patch ([8074032](https://github.com/ReVanced/revanced-patches/commit/8074032fad3eff1c03296a882d2e2820da99b592))
|
||||||
|
* **Twitter - Open links with app chooser:** Constrain patch to last working version `10.48.0-release` ([b9955d5](https://github.com/ReVanced/revanced-patches/commit/b9955d5ff6e456593b01f0f25d80ff660d02082a))
|
||||||
|
|
||||||
|
## [4.14.1](https://github.com/ReVanced/revanced-patches/compare/v4.14.0...v4.14.1) (2024-09-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Check environment:** Only use fields available since Android 8 ([#3655](https://github.com/ReVanced/revanced-patches/issues/3655)) ([4413533](https://github.com/ReVanced/revanced-patches/commit/441353306572340131030e1c4fee1ab6acb63cd9))
|
||||||
|
|
||||||
|
## [4.14.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.14.0...v4.14.1-dev.1) (2024-09-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Check environment:** Only use fields available since Android 8 ([#3655](https://github.com/ReVanced/revanced-patches/issues/3655)) ([4413533](https://github.com/ReVanced/revanced-patches/commit/441353306572340131030e1c4fee1ab6acb63cd9))
|
||||||
|
|
||||||
|
# [4.14.0](https://github.com/ReVanced/revanced-patches/compare/v4.13.3...v4.14.0) (2024-09-18)
|
||||||
|
|
||||||
|
|
||||||
|
### 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))
|
||||||
|
* **Soundcloud - Hide ads:** Support latest version ([#3628](https://github.com/ReVanced/revanced-patches/issues/3628)) ([66e7e33](https://github.com/ReVanced/revanced-patches/commit/66e7e33efce9b702fdfcc2b9803e9da8491c1f08))
|
||||||
|
* **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))
|
||||||
|
* **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))
|
||||||
|
* **YouTube - SponsorBlock:** Add summary text to 'view my segments' button ([df80b9f](https://github.com/ReVanced/revanced-patches/commit/df80b9f92f0d981b9a40b7756d74f8ccc3dcb1e9))
|
||||||
|
* **YouTube - SponsorBlock:** Handle if the user enters an invalid number into any SB settings ([37b3dd1](https://github.com/ReVanced/revanced-patches/commit/37b3dd1e789f8bb16fa1b9dd582e39c89dbe730c))
|
||||||
|
* **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))
|
||||||
|
|
||||||
|
|
||||||
|
### 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))
|
||||||
|
* Add `Check environment` patch ([#3610](https://github.com/ReVanced/revanced-patches/issues/3610)) ([fbcbdaf](https://github.com/ReVanced/revanced-patches/commit/fbcbdafa4938a35b5fdec46aae7b250a84b9c139))
|
||||||
|
* **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))
|
||||||
|
* **Sync for Reddit:** Add `Fix /user/ endpoint` patch ([46d11f3](https://github.com/ReVanced/revanced-patches/commit/46d11f3530fcdae9ed08b7e93aac235638a92dff))
|
||||||
|
* **Sync for Reddit:** Rename patch to `Use /user/ endpoint` ([98ead49](https://github.com/ReVanced/revanced-patches/commit/98ead493380932cb105530f4ba992673fd364d82))
|
||||||
|
* **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))
|
||||||
|
* **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))
|
||||||
|
* **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))
|
||||||
|
* **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))
|
||||||
|
* **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.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)
|
## [4.13.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.13.2...v4.13.3-dev.1) (2024-08-15)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 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 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 static final field INSTANCE Lapp/revanced/patches/all/interaction/gestures/PredictiveBackGesturePatch;
|
||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
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 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 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 static final field INSTANCE Lapp/revanced/patches/facebook/ads/story/HideStoryAdsPatch;
|
||||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
@@ -808,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 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 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;
|
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch;
|
||||||
}
|
}
|
||||||
@@ -842,6 +868,12 @@ public final class app/revanced/patches/serviceportalbund/detection/root/RootDet
|
|||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
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 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 static final field INSTANCE Lapp/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch;
|
||||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
@@ -1850,6 +1882,10 @@ public final class app/revanced/patches/youtube/misc/backgroundplayback/Backgrou
|
|||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
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 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 static final field INSTANCE Lapp/revanced/patches/youtube/misc/debugging/DebuggingPatch;
|
||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
@@ -1880,6 +1916,12 @@ public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignature
|
|||||||
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
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 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 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;
|
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;
|
||||||
@@ -1958,13 +2000,19 @@ public final class app/revanced/patches/youtube/misc/playercontrols/BottomContro
|
|||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
|
public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch;
|
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch;
|
||||||
public static field showPlayerControlsFingerprintResult Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
|
|
||||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
public final fun getShowPlayerControlsFingerprintResult ()Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
|
public final fun initializeBottomControl (Ljava/lang/String;)V
|
||||||
public final fun initializeControl (Ljava/lang/String;)V
|
public final fun initializeControl (Ljava/lang/String;)V
|
||||||
public final fun injectVisibilityCheckCall (Ljava/lang/String;)V
|
public final fun injectVisibilityCheckCall (Ljava/lang/String;)V
|
||||||
public final fun setShowPlayerControlsFingerprintResult (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;)V
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch;
|
||||||
|
public final fun addBottomControls (Ljava/lang/String;)V
|
||||||
|
public fun close ()V
|
||||||
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
|
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch : app/revanced/patcher/patch/BytecodePatch {
|
public final class app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
@@ -2132,6 +2180,8 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
||||||
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
|
public static final fun indexOfFirstWideLiteralInstructionValueReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
|
public static final fun indexOfFirstWideLiteralInstructionValueReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
||||||
@@ -2159,6 +2209,7 @@ public final class app/revanced/util/ResourceUtilsKt {
|
|||||||
public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/DomFileEditor;Lapp/revanced/patcher/util/DomFileEditor;)Ljava/lang/AutoCloseable;
|
public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/DomFileEditor;Lapp/revanced/patcher/util/DomFileEditor;)Ljava/lang/AutoCloseable;
|
||||||
public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
||||||
public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
||||||
|
public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V
|
||||||
public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
|
public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ dependencies {
|
|||||||
implementation(libs.guava)
|
implementation(libs.guava)
|
||||||
// Used in JsonGenerator.
|
// Used in JsonGenerator.
|
||||||
implementation(libs.gson)
|
implementation(libs.gson)
|
||||||
|
// Android API stubs defined here.
|
||||||
|
compileOnly(project(":stub"))
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching = true
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 4.13.3-dev.1
|
version = 4.16.0-dev.2
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,3 +5,5 @@ buildCache {
|
|||||||
isEnabled = "CI" !in System.getenv()
|
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",
|
"eu-rES" to "eu",
|
||||||
"fa-rIR" to "fa",
|
"fa-rIR" to "fa",
|
||||||
"fi-rFI" to "fi",
|
"fi-rFI" to "fi",
|
||||||
"tl-rPH" to "tl",
|
"fil-rPH" to "tl",
|
||||||
"fr-rFR" to "fr",
|
"fr-rFR" to "fr",
|
||||||
|
"ga-rIE" to "ga",
|
||||||
"gl-rES" to "gl",
|
"gl-rES" to "gl",
|
||||||
"gu-rIN" to "gu",
|
"gu-rIN" to "gu",
|
||||||
"hi-rIN" to "hi",
|
"hi-rIN" to "hi",
|
||||||
@@ -139,7 +140,6 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
|||||||
"uz-rUZ" to "uz",
|
"uz-rUZ" to "uz",
|
||||||
"vi-rVN" to "vi",
|
"vi-rVN" to "vi",
|
||||||
"zh-rCN" to "zh-rCN",
|
"zh-rCN" to "zh-rCN",
|
||||||
"zh-rHK" to "zh-rHK",
|
|
||||||
"zh-rTW" to "zh-rTW",
|
"zh-rTW" to "zh-rTW",
|
||||||
"zu-rZA" to "zu",
|
"zu-rZA" to "zu",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -13,16 +13,7 @@ import app.revanced.util.exception
|
|||||||
name = "Hide video ads",
|
name = "Hide video ads",
|
||||||
description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.",
|
description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -12,16 +12,7 @@ import com.android.tools.smali.dexlib2.Opcode
|
|||||||
@Patch(
|
@Patch(
|
||||||
description = "Adds more audio codec options. The new audio codecs usually result in better audio quality.",
|
description = "Adds more audio codec options. The new audio codecs usually result in better audio quality.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Deprecated("This patch is no longer needed as the feature is now enabled by default.")
|
@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",
|
name = "Enable exclusive audio playback",
|
||||||
description = "Enables the option to play audio without video.",
|
description = "Enables the option to play audio without video.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -14,16 +14,7 @@ import app.revanced.patches.music.interaction.permanentrepeat.fingerprints.Repea
|
|||||||
name = "Permanent repeat",
|
name = "Permanent repeat",
|
||||||
description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
|
description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
use = false
|
use = false
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,16 +14,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
name = "Hide category bar",
|
name = "Hide category bar",
|
||||||
description = "Hides the category bar at the top of the homepage.",
|
description = "Hides the category bar at the top of the homepage.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
use = false,
|
use = false,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,16 +17,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
|||||||
name = "Hide 'Get Music Premium' label",
|
name = "Hide 'Get Music Premium' label",
|
||||||
description = "Hides the \"Get Music Premium\" label from the account menu and settings.",
|
description = "Hides the \"Get Music Premium\" label from the account menu and settings.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -23,16 +23,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|||||||
name = "Remove upgrade button",
|
name = "Remove upgrade button",
|
||||||
description = "Removes the upgrade tab from the pivot bar.",
|
description = "Removes the upgrade tab from the pivot bar.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -13,16 +13,7 @@ import app.revanced.patches.music.misc.androidauto.fingerprints.CheckCertificate
|
|||||||
name = "Bypass certificate checks",
|
name = "Bypass certificate checks",
|
||||||
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -14,16 +14,7 @@ import app.revanced.util.resultOrThrow
|
|||||||
name = "Remove background playback restrictions",
|
name = "Remove background playback restrictions",
|
||||||
description = "Removes restrictions on background playback, including playing kids videos in the background.",
|
description = "Removes restrictions on background playback, including playing kids videos in the background.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music")
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
[
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -23,16 +23,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
|||||||
integrationsPatchDependency = IntegrationsPatch::class,
|
integrationsPatchDependency = IntegrationsPatch::class,
|
||||||
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
||||||
compatiblePackages = setOf(
|
compatiblePackages = setOf(
|
||||||
CompatiblePackage(
|
CompatiblePackage("com.google.android.apps.youtube.music"),
|
||||||
"com.google.android.apps.youtube.music",
|
|
||||||
setOf(
|
|
||||||
"6.45.54",
|
|
||||||
"6.51.53",
|
|
||||||
"7.01.53",
|
|
||||||
"7.02.52",
|
|
||||||
"7.03.52",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
fingerprints = setOf(
|
fingerprints = setOf(
|
||||||
CastDynamiteModuleV2Fingerprint,
|
CastDynamiteModuleV2Fingerprint,
|
||||||
|
|||||||
@@ -1,28 +1,25 @@
|
|||||||
package app.revanced.patches.pixiv.ads
|
package app.revanced.patches.pixiv.ads
|
||||||
|
|
||||||
import app.revanced.util.exception
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
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(
|
@Patch(
|
||||||
name = "Hide ads",
|
name = "Hide ads",
|
||||||
compatiblePackages = [CompatiblePackage("jp.pxv.android")]
|
compatiblePackages = [CompatiblePackage("jp.pxv.android")],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object HideAdsPatch : BytecodePatch(setOf(IsNotPremiumFingerprint)) {
|
object HideAdsPatch : BytecodePatch(setOf(ShouldShowAdsFingerprint)) {
|
||||||
// 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.
|
|
||||||
override fun execute(context: BytecodeContext) =
|
override fun execute(context: BytecodeContext) =
|
||||||
IsNotPremiumFingerprint.result?.mutableClass?.virtualMethods?.first()?.addInstructions(
|
ShouldShowAdsFingerprint.result?.mutableMethod?.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
const/4 v0, 0x0
|
const/4 v0, 0x0
|
||||||
return v0
|
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")
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
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_PRODUCT" to PRODUCT.encodedAndHashed,
|
||||||
|
"PATCH_RADIO" to RADIO.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;"
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -115,8 +115,8 @@ abstract class BaseGmsCoreSupportPatch(
|
|||||||
|
|
||||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||||
mainActivityOnCreateFingerprint.result?.mutableMethod?.apply {
|
mainActivityOnCreateFingerprint.result?.mutableMethod?.apply {
|
||||||
// Temporary fix for Google photos integration.
|
// Temporary fix for patches with an integrations patch that hook the onCreate method as well.
|
||||||
var setContextIndex = indexOfFirstInstruction {
|
val setContextIndex = indexOfFirstInstruction {
|
||||||
val reference = getReference<MethodReference>() ?: return@indexOfFirstInstruction false
|
val reference = getReference<MethodReference>() ?: return@indexOfFirstInstruction false
|
||||||
|
|
||||||
reference.toString() == "Lapp/revanced/integrations/shared/Utils;->setContext(Landroid/content/Context;)V"
|
reference.toString() == "Lapp/revanced/integrations/shared/Utils;->setContext(Landroid/content/Context;)V"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
|||||||
import app.revanced.util.ResourceGroup
|
import app.revanced.util.ResourceGroup
|
||||||
import app.revanced.util.copyResources
|
import app.revanced.util.copyResources
|
||||||
import app.revanced.util.getNode
|
import app.revanced.util.getNode
|
||||||
|
import app.revanced.util.insertFirst
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
@@ -47,11 +48,7 @@ abstract class BaseSettingsResourcePatch(
|
|||||||
// It may be necessary to ask for the desired resourceValue in the future.
|
// It may be necessary to ask for the desired resourceValue in the future.
|
||||||
AddResourcesPatch("values", resource)
|
AddResourcesPatch("values", resource)
|
||||||
}.let { preferenceNode ->
|
}.let { preferenceNode ->
|
||||||
if (prepend && firstChild != null) {
|
insertFirst(preferenceNode)
|
||||||
insertBefore(preferenceNode, firstChild)
|
|
||||||
} else {
|
|
||||||
appendChild(preferenceNode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
// Prevent verification of an HTTP header containing the user's current plan, which would contradict the previous patch.
|
||||||
InterceptFingerprint.resultOrThrow().let { result ->
|
InterceptFingerprint.resultOrThrow().let { result ->
|
||||||
val conditionIndex = result.scanResult.patternScanResult!!.endIndex
|
val conditionIndex = result.scanResult.patternScanResult!!.endIndex + 1
|
||||||
result.mutableMethod.addInstruction(
|
result.mutableMethod.addInstruction(
|
||||||
conditionIndex,
|
conditionIndex,
|
||||||
"return-object p1",
|
"return-object p1",
|
||||||
|
|||||||
@@ -9,14 +9,13 @@ internal object InterceptFingerprint : MethodFingerprint(
|
|||||||
accessFlags = AccessFlags.PUBLIC.value,
|
accessFlags = AccessFlags.PUBLIC.value,
|
||||||
parameters = listOf("L"),
|
parameters = listOf("L"),
|
||||||
opcodes = listOf(
|
opcodes = listOf(
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_INTERFACE,
|
||||||
Opcode.MOVE_RESULT,
|
Opcode.MOVE_RESULT_OBJECT
|
||||||
Opcode.IF_EQZ,
|
|
||||||
),
|
),
|
||||||
strings = listOf("SC-Mob-UserPlan", "Configuration"),
|
strings = listOf("SC-Mob-UserPlan", "Configuration"),
|
||||||
customFingerprint = { _, classDef ->
|
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
|
import app.revanced.util.resultOrThrow
|
||||||
|
|
||||||
@Patch(
|
@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." +
|
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.",
|
"If the device is rooted, root permissions must be hidden from the app.",
|
||||||
compatiblePackages = [CompatiblePackage("com.swisssign.swissid.mobile")],
|
compatiblePackages = [CompatiblePackage("com.swisssign.swissid.mobile")],
|
||||||
|
|||||||
@@ -17,16 +17,16 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
@Patch(
|
@Patch(
|
||||||
name = "Feed filter",
|
name = "Feed filter",
|
||||||
description = "Removes ads, livestreams, stories, image videos " +
|
description = "Removes ads, livestreams, stories, image videos " +
|
||||||
"and videos with a specific amount of views or likes from the feed.",
|
"and videos with a specific amount of views or likes from the feed.",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.ss.android.ugc.trill", ["32.5.3"]),
|
CompatiblePackage("com.ss.android.ugc.trill", ["36.5.4"]),
|
||||||
CompatiblePackage("com.zhiliaoapp.musically", ["32.5.3"])
|
CompatiblePackage("com.zhiliaoapp.musically", ["36.5.4"]),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object FeedFilterPatch : BytecodePatch(
|
object FeedFilterPatch : BytecodePatch(
|
||||||
setOf(FeedApiServiceLIZFingerprint, SettingsStatusLoadFingerprint)
|
setOf(FeedApiServiceLIZFingerprint, SettingsStatusLoadFingerprint),
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
FeedApiServiceLIZFingerprint.result?.mutableMethod?.apply {
|
FeedApiServiceLIZFingerprint.result?.mutableMethod?.apply {
|
||||||
@@ -36,13 +36,13 @@ object FeedFilterPatch : BytecodePatch(
|
|||||||
addInstruction(
|
addInstruction(
|
||||||
returnFeedItemInstruction.location.index,
|
returnFeedItemInstruction.location.index,
|
||||||
"invoke-static { v$feedItemsRegister }, " +
|
"invoke-static { v$feedItemsRegister }, " +
|
||||||
"Lapp/revanced/integrations/tiktok/feedfilter/FeedItemsFilter;->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V"
|
"Lapp/revanced/integrations/tiktok/feedfilter/FeedItemsFilter;->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V",
|
||||||
)
|
)
|
||||||
} ?: throw FeedApiServiceLIZFingerprint.exception
|
} ?: throw FeedApiServiceLIZFingerprint.exception
|
||||||
|
|
||||||
SettingsStatusLoadFingerprint.result?.mutableMethod?.addInstruction(
|
SettingsStatusLoadFingerprint.result?.mutableMethod?.addInstruction(
|
||||||
0,
|
0,
|
||||||
"invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableFeedFilter()V"
|
"invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableFeedFilter()V",
|
||||||
) ?: throw SettingsStatusLoadFingerprint.exception
|
) ?: throw SettingsStatusLoadFingerprint.exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import app.revanced.patcher.patch.annotation.CompatiblePackage
|
|||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnClearDisplayEventFingerprint
|
import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnClearDisplayEventFingerprint
|
||||||
import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnRenderFirstFrameFingerprint
|
import app.revanced.patches.tiktok.shared.fingerprints.OnRenderFirstFrameFingerprint
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
@@ -19,16 +19,16 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
|
|||||||
name = "Remember clear display",
|
name = "Remember clear display",
|
||||||
description = "Remembers the clear display configurations in between videos.",
|
description = "Remembers the clear display configurations in between videos.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.ss.android.ugc.trill", ["32.5.3"]),
|
CompatiblePackage("com.ss.android.ugc.trill", ["36.5.4"]),
|
||||||
CompatiblePackage("com.zhiliaoapp.musically", ["32.5.3"])
|
CompatiblePackage("com.zhiliaoapp.musically", ["36.5.4"]),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object RememberClearDisplayPatch : BytecodePatch(
|
object RememberClearDisplayPatch : BytecodePatch(
|
||||||
setOf(
|
setOf(
|
||||||
OnClearDisplayEventFingerprint,
|
OnClearDisplayEventFingerprint,
|
||||||
OnRenderFirstFrameFingerprint
|
OnRenderFirstFrameFingerprint,
|
||||||
)
|
),
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
OnClearDisplayEventFingerprint.result?.mutableMethod?.let {
|
OnClearDisplayEventFingerprint.result?.mutableMethod?.let {
|
||||||
@@ -40,7 +40,7 @@ object RememberClearDisplayPatch : BytecodePatch(
|
|||||||
it.addInstructions(
|
it.addInstructions(
|
||||||
isEnabledIndex,
|
isEnabledIndex,
|
||||||
"invoke-static { v$isEnabledRegister }, " +
|
"invoke-static { v$isEnabledRegister }, " +
|
||||||
"Lapp/revanced/integrations/tiktok/cleardisplay/RememberClearDisplayPatch;->rememberClearDisplayState(Z)V"
|
"Lapp/revanced/integrations/tiktok/cleardisplay/RememberClearDisplayPatch;->rememberClearDisplayState(Z)V",
|
||||||
)
|
)
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
@@ -54,22 +54,25 @@ object RememberClearDisplayPatch : BytecodePatch(
|
|||||||
"""
|
"""
|
||||||
# Create a new clearDisplayEvent and post it to the EventBus (https://github.com/greenrobot/EventBus)
|
# Create a new clearDisplayEvent and post it to the EventBus (https://github.com/greenrobot/EventBus)
|
||||||
|
|
||||||
# The state of clear display.
|
|
||||||
invoke-static { }, Lapp/revanced/integrations/tiktok/cleardisplay/RememberClearDisplayPatch;->getClearDisplayState()Z
|
|
||||||
move-result v3
|
|
||||||
if-eqz v3, :clear_display_disabled
|
|
||||||
|
|
||||||
# Clear display type such as 0 = LONG_PRESS, 1 = SCREEN_RECORD etc.
|
# Clear display type such as 0 = LONG_PRESS, 1 = SCREEN_RECORD etc.
|
||||||
const/4 v1, 0x0
|
const/4 v1, 0x0
|
||||||
|
|
||||||
|
# Enter method (Such as "pinch", "swipe_exit", or an empty string (unknown, what it means)).
|
||||||
|
const-string v2, ""
|
||||||
|
|
||||||
# Name of the clear display type which is equivalent to the clear display type.
|
# Name of the clear display type which is equivalent to the clear display type.
|
||||||
const-string v2, "long_press"
|
const-string v3, "long_press"
|
||||||
|
|
||||||
|
# The state of clear display.
|
||||||
|
invoke-static { }, Lapp/revanced/integrations/tiktok/cleardisplay/RememberClearDisplayPatch;->getClearDisplayState()Z
|
||||||
|
move-result v4
|
||||||
|
if-eqz v4, :clear_display_disabled
|
||||||
|
|
||||||
new-instance v0, $clearDisplayEventClass
|
new-instance v0, $clearDisplayEventClass
|
||||||
invoke-direct { v0, v1, v2, v3 }, $clearDisplayEventClass-><init>(ILjava/lang/String;Z)V
|
invoke-direct { v0, v1, v2, v3, v4 }, $clearDisplayEventClass-><init>(ILjava/lang/String;Ljava/lang/String;Z)V
|
||||||
invoke-virtual { v0 }, $clearDisplayEventClass->post()Lcom/ss/android/ugc/governance/eventbus/IEvent;
|
invoke-virtual { v0 }, $clearDisplayEventClass->post()Lcom/ss/android/ugc/governance/eventbus/IEvent;
|
||||||
""",
|
""",
|
||||||
ExternalLabel("clear_display_disabled", getInstruction(0))
|
ExternalLabel("clear_display_disabled", getInstruction(0)),
|
||||||
)
|
)
|
||||||
} ?: throw OnRenderFirstFrameFingerprint.exception
|
} ?: throw OnRenderFirstFrameFingerprint.exception
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
|
||||||
|
|
||||||
internal object OnRenderFirstFrameFingerprint : MethodFingerprint(
|
|
||||||
customFingerprint = { methodDef, _ ->
|
|
||||||
methodDef.definingClass.endsWith("/BaseListFragmentPanel;") && methodDef.name == "onRenderFirstFrame"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -13,14 +13,13 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|||||||
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.ACLCommonShareFingerprint
|
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.ACLCommonShareFingerprint
|
||||||
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.ACLCommonShareFingerprint2
|
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.ACLCommonShareFingerprint2
|
||||||
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.ACLCommonShareFingerprint3
|
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.ACLCommonShareFingerprint3
|
||||||
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.DownloadPathParentFingerprint
|
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.DownloadUriFingerprint
|
||||||
import app.revanced.patches.tiktok.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.tiktok.misc.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.tiktok.misc.settings.SettingsPatch
|
import app.revanced.patches.tiktok.misc.settings.SettingsPatch
|
||||||
import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsStatusLoadFingerprint
|
import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsStatusLoadFingerprint
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
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.iface.reference.MethodReference
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
@@ -28,9 +27,9 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|||||||
description = "Removes download restrictions and changes the default path to download to.",
|
description = "Removes download restrictions and changes the default path to download to.",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.ss.android.ugc.trill", ["32.5.3"]),
|
CompatiblePackage("com.ss.android.ugc.trill", ["36.5.4"]),
|
||||||
CompatiblePackage("com.zhiliaoapp.musically", ["32.5.3"])
|
CompatiblePackage("com.zhiliaoapp.musically", ["36.5.4"]),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object DownloadsPatch : BytecodePatch(
|
object DownloadsPatch : BytecodePatch(
|
||||||
@@ -38,9 +37,9 @@ object DownloadsPatch : BytecodePatch(
|
|||||||
ACLCommonShareFingerprint,
|
ACLCommonShareFingerprint,
|
||||||
ACLCommonShareFingerprint2,
|
ACLCommonShareFingerprint2,
|
||||||
ACLCommonShareFingerprint3,
|
ACLCommonShareFingerprint3,
|
||||||
DownloadPathParentFingerprint,
|
DownloadUriFingerprint,
|
||||||
SettingsStatusLoadFingerprint
|
SettingsStatusLoadFingerprint,
|
||||||
)
|
),
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
fun MethodFingerprint.getMethod() = result?.mutableMethod ?: throw exception
|
fun MethodFingerprint.getMethod() = result?.mutableMethod ?: throw exception
|
||||||
@@ -52,7 +51,7 @@ object DownloadsPatch : BytecodePatch(
|
|||||||
"""
|
"""
|
||||||
const/4 v0, 0x0
|
const/4 v0, 0x0
|
||||||
return v0
|
return v0
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
ACLCommonShareFingerprint2 to {
|
ACLCommonShareFingerprint2 to {
|
||||||
@@ -61,7 +60,7 @@ object DownloadsPatch : BytecodePatch(
|
|||||||
"""
|
"""
|
||||||
const/4 v0, 0x2
|
const/4 v0, 0x2
|
||||||
return v0
|
return v0
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
// Download videos without watermark.
|
// Download videos without watermark.
|
||||||
@@ -76,48 +75,40 @@ object DownloadsPatch : BytecodePatch(
|
|||||||
return v0
|
return v0
|
||||||
:noremovewatermark
|
:noremovewatermark
|
||||||
nop
|
nop
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
// Change the download path patch.
|
// Change the download path patch.
|
||||||
DownloadPathParentFingerprint to {
|
DownloadUriFingerprint to {
|
||||||
val targetIndex = indexOfFirstInstructionOrThrow { opcode == Opcode.INVOKE_STATIC }
|
val firstIndex = indexOfFirstInstructionOrThrow {
|
||||||
val downloadUriMethod = context
|
getReference<MethodReference>()?.name == "<init>"
|
||||||
.toMethodWalker(this)
|
|
||||||
.nextMethod(targetIndex, true)
|
|
||||||
.getMethod() as MutableMethod
|
|
||||||
|
|
||||||
val firstIndex = downloadUriMethod.indexOfFirstInstructionOrThrow {
|
|
||||||
opcode == Opcode.INVOKE_DIRECT && ((this as Instruction35c).reference as MethodReference).name == "<init>"
|
|
||||||
}
|
}
|
||||||
val secondIndex = downloadUriMethod.indexOfFirstInstructionOrThrow {
|
val secondIndex = indexOfFirstInstructionOrThrow {
|
||||||
opcode == Opcode.INVOKE_STATIC && ((this as Instruction35c).reference as MethodReference).returnType.contains(
|
getReference<MethodReference>()?.returnType?.contains("Uri") == true
|
||||||
"Uri"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadUriMethod.addInstructions(
|
addInstructions(
|
||||||
secondIndex,
|
secondIndex,
|
||||||
"""
|
"""
|
||||||
invoke-static {}, Lapp/revanced/integrations/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String;
|
invoke-static {}, Lapp/revanced/integrations/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String;
|
||||||
move-result-object v0
|
move-result-object v0
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
downloadUriMethod.addInstructions(
|
addInstructions(
|
||||||
firstIndex,
|
firstIndex,
|
||||||
"""
|
"""
|
||||||
invoke-static {}, Lapp/revanced/integrations/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String;
|
invoke-static {}, Lapp/revanced/integrations/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String;
|
||||||
move-result-object v0
|
move-result-object v0
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
SettingsStatusLoadFingerprint to {
|
SettingsStatusLoadFingerprint to {
|
||||||
addInstruction(
|
addInstruction(
|
||||||
0,
|
0,
|
||||||
"invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableDownload()V"
|
"invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableDownload()V",
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
).forEach { (fingerprint, patch) ->
|
).forEach { (fingerprint, patch) ->
|
||||||
fingerprint.getMethod().patch()
|
fingerprint.getMethod().patch()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,18 @@ package app.revanced.patches.tiktok.interaction.downloads.fingerprints
|
|||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal object DownloadPathParentFingerprint : MethodFingerprint(
|
internal object DownloadUriFingerprint : MethodFingerprint(
|
||||||
"L",
|
"Landroid/net/Uri;",
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
strings = listOf(
|
strings = listOf(
|
||||||
"video/mp4"
|
"/",
|
||||||
|
"/Camera",
|
||||||
|
"/Camera/",
|
||||||
|
"video/mp4",
|
||||||
),
|
),
|
||||||
parameters = listOf(
|
parameters = listOf(
|
||||||
"L",
|
"Landroid/content/Context;",
|
||||||
"L"
|
"Ljava/lang/String;",
|
||||||
),
|
),
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.CONST_STRING,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.RETURN_OBJECT
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
@@ -9,30 +9,33 @@ import app.revanced.patcher.patch.PatchException
|
|||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patches.tiktok.interaction.speed.fingerprints.GetSpeedFingerprint
|
import app.revanced.patches.tiktok.interaction.speed.fingerprints.GetSpeedFingerprint
|
||||||
import app.revanced.patches.tiktok.interaction.speed.fingerprints.OnRenderFirstFrameFingerprint
|
|
||||||
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SetSpeedFingerprint
|
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SetSpeedFingerprint
|
||||||
|
import app.revanced.patches.tiktok.shared.fingerprints.GetEnterFromFingerprint
|
||||||
|
import app.revanced.patches.tiktok.shared.fingerprints.OnRenderFirstFrameFingerprint
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Playback speed",
|
name = "Playback speed",
|
||||||
description = "Enables the playback speed option for all videos and " +
|
description = "Enables the playback speed option for all videos and " +
|
||||||
"retains the speed configurations in between videos.",
|
"retains the speed configurations in between videos.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.ss.android.ugc.trill", ["32.5.3"]),
|
CompatiblePackage("com.ss.android.ugc.trill", ["36.5.4"]),
|
||||||
CompatiblePackage("com.zhiliaoapp.musically", ["32.5.3"])
|
CompatiblePackage("com.zhiliaoapp.musically", ["36.5.4"]),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object PlaybackSpeedPatch : BytecodePatch(
|
object PlaybackSpeedPatch : BytecodePatch(
|
||||||
setOf(
|
setOf(
|
||||||
GetSpeedFingerprint,
|
GetSpeedFingerprint,
|
||||||
OnRenderFirstFrameFingerprint,
|
OnRenderFirstFrameFingerprint,
|
||||||
SetSpeedFingerprint
|
SetSpeedFingerprint,
|
||||||
)
|
GetEnterFromFingerprint,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
SetSpeedFingerprint.result?.let { onVideoSwiped ->
|
SetSpeedFingerprint.result?.let { onVideoSwiped ->
|
||||||
@@ -44,7 +47,7 @@ object PlaybackSpeedPatch : BytecodePatch(
|
|||||||
addInstruction(
|
addInstruction(
|
||||||
injectIndex,
|
injectIndex,
|
||||||
"invoke-static { v$register }," +
|
"invoke-static { v$register }," +
|
||||||
" Lapp/revanced/integrations/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V"
|
" Lapp/revanced/integrations/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V",
|
||||||
)
|
)
|
||||||
} ?: throw GetSpeedFingerprint.exception
|
} ?: throw GetSpeedFingerprint.exception
|
||||||
|
|
||||||
@@ -53,29 +56,29 @@ object PlaybackSpeedPatch : BytecodePatch(
|
|||||||
OnRenderFirstFrameFingerprint.result?.mutableMethod?.addInstructions(
|
OnRenderFirstFrameFingerprint.result?.mutableMethod?.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
# Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method.
|
# Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method.
|
||||||
const/4 v0, 0x1
|
const/4 v0, 0x1
|
||||||
invoke-virtual {p0, v0}, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getEnterFrom(Z)Ljava/lang/String;
|
invoke-virtual {p0, v0}, ${GetEnterFromFingerprint.resultOrThrow().method}
|
||||||
move-result-object v0
|
move-result-object v0
|
||||||
|
|
||||||
# Model of current video retrieved using getCurrentAweme method.
|
# Model of current video retrieved using getCurrentAweme method.
|
||||||
invoke-virtual {p0}, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getCurrentAweme()Lcom/ss/android/ugc/aweme/feed/model/Aweme;
|
invoke-virtual {p0}, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getCurrentAweme()Lcom/ss/android/ugc/aweme/feed/model/Aweme;
|
||||||
move-result-object v1
|
move-result-object v1
|
||||||
|
|
||||||
# Desired playback speed retrieved using getPlaybackSpeed method.
|
# Desired playback speed retrieved using getPlaybackSpeed method.
|
||||||
invoke-static {}, Lapp/revanced/integrations/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F
|
invoke-static {}, Lapp/revanced/integrations/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F
|
||||||
move-result-object v2
|
move-result v2
|
||||||
invoke-static { v0, v1, v2 }, ${onVideoSwiped.method}
|
invoke-static { v0, v1, v2 }, ${onVideoSwiped.method}
|
||||||
"""
|
""",
|
||||||
) ?: throw OnRenderFirstFrameFingerprint.exception
|
) ?: throw OnRenderFirstFrameFingerprint.exception
|
||||||
|
|
||||||
// Force enable the playback speed option for all videos.
|
// Force enable the playback speed option for all videos.
|
||||||
onVideoSwiped.mutableClass.methods.find { method -> method.returnType == "Z" }?.addInstructions(
|
onVideoSwiped.mutableClass.methods.find { method -> method.returnType == "Z" }?.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
const/4 v0, 0x1
|
const/4 v0, 0x1
|
||||||
return v0
|
return v0
|
||||||
"""
|
""",
|
||||||
) ?: throw PatchException("Failed to force enable the playback speed option.")
|
) ?: throw PatchException("Failed to force enable the playback speed option.")
|
||||||
} ?: throw SetSpeedFingerprint.exception
|
} ?: throw SetSpeedFingerprint.exception
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|||||||
description = "Adds ReVanced settings to TikTok.",
|
description = "Adds ReVanced settings to TikTok.",
|
||||||
dependencies = [IntegrationsPatch::class],
|
dependencies = [IntegrationsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.ss.android.ugc.trill", ["32.5.3"]),
|
CompatiblePackage("com.ss.android.ugc.trill", ["36.5.4"]),
|
||||||
CompatiblePackage("com.zhiliaoapp.musically", ["32.5.3"])
|
CompatiblePackage("com.zhiliaoapp.musically", ["36.5.4"]),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
object SettingsPatch : BytecodePatch(
|
object SettingsPatch : BytecodePatch(
|
||||||
setOf(
|
setOf(
|
||||||
@@ -34,21 +34,21 @@ object SettingsPatch : BytecodePatch(
|
|||||||
AddSettingsEntryFingerprint,
|
AddSettingsEntryFingerprint,
|
||||||
SettingsEntryFingerprint,
|
SettingsEntryFingerprint,
|
||||||
SettingsEntryInfoFingerprint,
|
SettingsEntryInfoFingerprint,
|
||||||
)
|
),
|
||||||
) {
|
) {
|
||||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/integrations/tiktok/settings/AdPersonalizationActivityHook;"
|
"Lapp/revanced/integrations/tiktok/settings/AdPersonalizationActivityHook;"
|
||||||
|
|
||||||
private const val INITIALIZE_SETTINGS_METHOD_DESCRIPTOR =
|
private const val INITIALIZE_SETTINGS_METHOD_DESCRIPTOR =
|
||||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->initialize(" +
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->initialize(" +
|
||||||
"Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;" +
|
"Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;" +
|
||||||
")Z"
|
")Z"
|
||||||
|
|
||||||
private const val CREATE_SETTINGS_ENTRY_METHOD_DESCRIPTOR =
|
private const val CREATE_SETTINGS_ENTRY_METHOD_DESCRIPTOR =
|
||||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->createSettingsEntry(" +
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->createSettingsEntry(" +
|
||||||
"Ljava/lang/String;" +
|
"Ljava/lang/String;" +
|
||||||
"Ljava/lang/String;" +
|
"Ljava/lang/String;" +
|
||||||
")Ljava/lang/Object;"
|
")Ljava/lang/Object;"
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// Find the class name of classes which construct a settings entry
|
// Find the class name of classes which construct a settings entry
|
||||||
@@ -70,8 +70,8 @@ object SettingsPatch : BytecodePatch(
|
|||||||
markIndex + 2,
|
markIndex + 2,
|
||||||
listOf(
|
listOf(
|
||||||
getUnitManager,
|
getUnitManager,
|
||||||
addEntry
|
addEntry,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
@@ -81,7 +81,8 @@ object SettingsPatch : BytecodePatch(
|
|||||||
const-string v1, "$settingsButtonInfoClass"
|
const-string v1, "$settingsButtonInfoClass"
|
||||||
invoke-static {v0, v1}, $CREATE_SETTINGS_ENTRY_METHOD_DESCRIPTOR
|
invoke-static {v0, v1}, $CREATE_SETTINGS_ENTRY_METHOD_DESCRIPTOR
|
||||||
move-result-object v0
|
move-result-object v0
|
||||||
"""
|
check-cast v0, ${SettingsEntryFingerprint.result!!.classDef}
|
||||||
|
""",
|
||||||
)
|
)
|
||||||
} ?: throw AddSettingsEntryFingerprint.exception
|
} ?: throw AddSettingsEntryFingerprint.exception
|
||||||
|
|
||||||
@@ -102,12 +103,10 @@ object SettingsPatch : BytecodePatch(
|
|||||||
if-eqz v$usableRegister, :do_not_open
|
if-eqz v$usableRegister, :do_not_open
|
||||||
return-void
|
return-void
|
||||||
""",
|
""",
|
||||||
ExternalLabel("do_not_open", getInstruction(initializeSettingsIndex))
|
ExternalLabel("do_not_open", getInstruction(initializeSettingsIndex)),
|
||||||
)
|
)
|
||||||
} ?: throw AdPersonalizationActivityOnCreateFingerprint.exception
|
} ?: throw AdPersonalizationActivityOnCreateFingerprint.exception
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.toClassName(): String {
|
private fun String.toClassName(): String = substring(1, this.length - 1).replace("/", ".")
|
||||||
return substring(1, this.length - 1).replace("/", ".")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package app.revanced.patches.tiktok.shared.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 GetEnterFromFingerprint : MethodFingerprint(
|
||||||
|
returnType = "Ljava/lang/String;",
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
parameters = listOf("Z"),
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.RETURN_OBJECT,
|
||||||
|
),
|
||||||
|
customFingerprint = { methodDef, _ ->
|
||||||
|
methodDef.definingClass.endsWith("/BaseListFragmentPanel;")
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
package app.revanced.patches.tiktok.shared.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
|
||||||
internal object OnRenderFirstFrameFingerprint : MethodFingerprint(
|
internal object OnRenderFirstFrameFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("method_enable_viewpager_preload_duration"),
|
||||||
customFingerprint = { methodDef, _ ->
|
customFingerprint = { methodDef, _ ->
|
||||||
methodDef.definingClass.endsWith("/BaseListFragmentPanel;") && methodDef.name == "onRenderFirstFrame"
|
methodDef.definingClass.endsWith("/BaseListFragmentPanel;")
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
@@ -12,7 +12,7 @@ import app.revanced.util.exception
|
|||||||
name = "Open links with app chooser",
|
name = "Open links with app chooser",
|
||||||
description = "Instead of opening links directly, open them with an app chooser. " +
|
description = "Instead of opening links directly, open them with an app chooser. " +
|
||||||
"As a result you can select a browser to open the link with.",
|
"As a result you can select a browser to open the link with.",
|
||||||
compatiblePackages = [CompatiblePackage("com.twitter.android")],
|
compatiblePackages = [CompatiblePackage("com.twitter.android", ["10.48.0-release.0"])],
|
||||||
use = false,
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ object CopyVideoUrlBytecodePatch : BytecodePatch(emptySet()) {
|
|||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
BUTTONS_DESCRIPTORS.forEach { descriptor ->
|
BUTTONS_DESCRIPTORS.forEach { descriptor ->
|
||||||
PlayerControlsBytecodePatch.initializeControl("$descriptor->initializeButton(Landroid/view/View;)V")
|
PlayerControlsBytecodePatch.initializeBottomControl(descriptor)
|
||||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$descriptor->changeVisibility(Z)V")
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall(descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.patch.ResourcePatch
|
|||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
|
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.util.ResourceGroup
|
import app.revanced.util.ResourceGroup
|
||||||
import app.revanced.util.copyResources
|
import app.revanced.util.copyResources
|
||||||
@@ -13,7 +13,7 @@ import app.revanced.util.copyResources
|
|||||||
@Patch(
|
@Patch(
|
||||||
dependencies = [
|
dependencies = [
|
||||||
SettingsPatch::class,
|
SettingsPatch::class,
|
||||||
BottomControlsResourcePatch::class,
|
PlayerControlsResourcePatch::class,
|
||||||
AddResourcesPatch::class
|
AddResourcesPatch::class
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -34,6 +34,6 @@ internal object CopyVideoUrlResourcePatch : ResourcePatch() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
BottomControlsResourcePatch.addControls("copyvideourl")
|
PlayerControlsResourcePatch.addBottomControls("copyvideourl")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,8 +58,8 @@ object DownloadsPatch : BytecodePatch(
|
|||||||
private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;"
|
private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;"
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
PlayerControlsBytecodePatch.initializeControl("$BUTTON_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
|
PlayerControlsBytecodePatch.initializeBottomControl(BUTTON_DESCRIPTOR)
|
||||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$BUTTON_DESCRIPTOR->changeVisibility(Z)V")
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall(BUTTON_DESCRIPTOR)
|
||||||
|
|
||||||
// Main activity is used to launch downloader intent.
|
// Main activity is used to launch downloader intent.
|
||||||
MainActivityFingerprint.resultOrThrow().mutableMethod.apply {
|
MainActivityFingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ 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.PreferenceScreen.Sorting
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||||
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
|
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.util.ResourceGroup
|
import app.revanced.util.ResourceGroup
|
||||||
import app.revanced.util.copyResources
|
import app.revanced.util.copyResources
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
dependencies = [
|
dependencies = [
|
||||||
BottomControlsResourcePatch::class,
|
PlayerControlsResourcePatch::class,
|
||||||
SettingsPatch::class,
|
SettingsPatch::class,
|
||||||
AddResourcesPatch::class,
|
AddResourcesPatch::class,
|
||||||
],
|
],
|
||||||
@@ -42,6 +42,6 @@ internal object DownloadsResourcePatch : ResourcePatch() {
|
|||||||
ResourceGroup("drawable", "revanced_yt_download_button.xml"),
|
ResourceGroup("drawable", "revanced_yt_download_button.xml"),
|
||||||
)
|
)
|
||||||
|
|
||||||
BottomControlsResourcePatch.addControls("downloads")
|
PlayerControlsResourcePatch.addBottomControls("downloads")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,8 +138,10 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
|||||||
SwitchPreference("revanced_hide_keyword_content_search"),
|
SwitchPreference("revanced_hide_keyword_content_search"),
|
||||||
TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE),
|
TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE),
|
||||||
NonInteractivePreference("revanced_hide_keyword_content_about"),
|
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(
|
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||||
import app.revanced.patches.youtube.layout.hide.shorts.fingerprints.*
|
import app.revanced.patches.youtube.layout.hide.shorts.fingerprints.*
|
||||||
@@ -76,6 +77,20 @@ object HideShortsComponentsPatch : BytecodePatch(
|
|||||||
) {
|
) {
|
||||||
private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/components/ShortsFilter;"
|
private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/components/ShortsFilter;"
|
||||||
|
|
||||||
|
internal val hideShortsAppShortcut by booleanPatchOption(
|
||||||
|
key = "hideShortsAppShortcut",
|
||||||
|
default = false,
|
||||||
|
title = "Hide Shorts app shortcut",
|
||||||
|
description = "Permanently hides the shortcut to open Shorts when long pressing the app icon in your launcher."
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val hideShortsWidget by booleanPatchOption(
|
||||||
|
key = "hideShortsWidget",
|
||||||
|
default = false,
|
||||||
|
title = "Hide Shorts widget",
|
||||||
|
description = "Permanently hides the launcher widget Shorts button."
|
||||||
|
)
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// region Hide the Shorts shelf.
|
// region Hide the Shorts shelf.
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import app.revanced.patcher.patch.annotation.Patch
|
|||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
|
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsAppShortcut
|
||||||
|
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsWidget
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
|
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class, AddResourcesPatch::class])
|
@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class, AddResourcesPatch::class])
|
||||||
object HideShortsComponentsResourcePatch : ResourcePatch() {
|
object HideShortsComponentsResourcePatch : ResourcePatch() {
|
||||||
@@ -38,6 +42,7 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
|
|||||||
SwitchPreference("revanced_hide_shorts_subscribe_button"),
|
SwitchPreference("revanced_hide_shorts_subscribe_button"),
|
||||||
SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"),
|
SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"),
|
||||||
SwitchPreference("revanced_hide_shorts_save_sound_button"),
|
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_shop_button"),
|
||||||
SwitchPreference("revanced_hide_shorts_tagged_products"),
|
SwitchPreference("revanced_hide_shorts_tagged_products"),
|
||||||
SwitchPreference("revanced_hide_shorts_search_suggestions"),
|
SwitchPreference("revanced_hide_shorts_search_suggestions"),
|
||||||
@@ -51,6 +56,28 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
|
|||||||
SwitchPreference("revanced_hide_shorts_navigation_bar"),
|
SwitchPreference("revanced_hide_shorts_navigation_bar"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (hideShortsAppShortcut == true) {
|
||||||
|
context.xmlEditor["res/xml/main_shortcuts.xml"].use { editor ->
|
||||||
|
val shortsItem = editor.file.childNodes.findElementByAttributeValueOrThrow(
|
||||||
|
"android:shortcutId",
|
||||||
|
"shorts-shortcut"
|
||||||
|
)
|
||||||
|
|
||||||
|
shortsItem.parentNode.removeChild(shortsItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hideShortsWidget == true) {
|
||||||
|
context.xmlEditor["res/layout/appwidget_two_rows.xml"].use { editor ->
|
||||||
|
val shortsItem = editor.file.childNodes.findElementByAttributeValueOrThrow(
|
||||||
|
"android:id",
|
||||||
|
"@id/button_shorts_container"
|
||||||
|
)
|
||||||
|
|
||||||
|
shortsItem.parentNode.removeChild(shortsItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reelPlayerRightCellButtonHeight = ResourceMappingPatch[
|
reelPlayerRightCellButtonHeight = ResourceMappingPatch[
|
||||||
"dimen",
|
"dimen",
|
||||||
"reel_player_right_cell_button_height",
|
"reel_player_right_cell_button_height",
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import app.revanced.patcher.patch.PatchException
|
|||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
|
||||||
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.AppendTimeFingerprint
|
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.AppendTimeFingerprint
|
||||||
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.ControlsOverlayFingerprint
|
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.ControlsOverlayFingerprint
|
||||||
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.RectangleFieldInvalidatorFingerprint
|
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.RectangleFieldInvalidatorFingerprint
|
||||||
@@ -26,7 +25,10 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
|||||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.*
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
@@ -169,59 +171,14 @@ object SponsorBlockBytecodePatch : BytecodePatch(
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Change visibility of the buttons.
|
||||||
* Voting & Shield button
|
PlayerControlsBytecodePatch.initializeTopControl(INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
|
||||||
*/
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall(INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
|
||||||
val controlsMethodResult = PlayerControlsBytecodePatch.showPlayerControlsFingerprintResult
|
|
||||||
|
|
||||||
val controlsLayoutStubResourceId =
|
PlayerControlsBytecodePatch.initializeTopControl(INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
|
||||||
ResourceMappingPatch["id", "controls_layout_stub"]
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall(INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
|
||||||
val zoomOverlayResourceId =
|
|
||||||
ResourceMappingPatch["id", "video_zoom_overlay_stub"]
|
|
||||||
|
|
||||||
methods@ for (method in controlsMethodResult.mutableClass.methods) {
|
// Append the new time to the player layout.
|
||||||
val instructions = method.implementation?.instructions!!
|
|
||||||
instructions@ for ((index, instruction) in instructions.withIndex()) {
|
|
||||||
// search for method which inflates the controls layout view
|
|
||||||
if (instruction.opcode != Opcode.CONST) continue@instructions
|
|
||||||
|
|
||||||
when ((instruction as NarrowLiteralInstruction).wideLiteral) {
|
|
||||||
controlsLayoutStubResourceId -> {
|
|
||||||
// replace the view with the YouTubeControlsOverlay
|
|
||||||
val moveResultInstructionIndex = index + 5
|
|
||||||
val inflatedViewRegister =
|
|
||||||
(instructions[moveResultInstructionIndex] as OneRegisterInstruction).registerA
|
|
||||||
// initialize with the player overlay object
|
|
||||||
method.addInstructions(
|
|
||||||
moveResultInstructionIndex + 1, // insert right after moving the view to the register and use that register
|
|
||||||
"""
|
|
||||||
invoke-static {v$inflatedViewRegister}, $INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/View;)V
|
|
||||||
invoke-static {v$inflatedViewRegister}, $INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/View;)V
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
zoomOverlayResourceId -> {
|
|
||||||
val invertVisibilityMethod =
|
|
||||||
context.toMethodWalker(method).nextMethod(index - 6, true).getMethod() as MutableMethod
|
|
||||||
// change visibility of the buttons
|
|
||||||
invertVisibilityMethod.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static {p1}, $INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibilityNegatedImmediate(Z)V
|
|
||||||
invoke-static {p1}, $INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibilityNegatedImmediate(Z)V
|
|
||||||
""".trimIndent(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// change visibility of the buttons
|
|
||||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibility(Z)V")
|
|
||||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibility(Z)V")
|
|
||||||
|
|
||||||
// append the new time to the player layout
|
|
||||||
val appendTimeFingerprintResult = AppendTimeFingerprint.result!!
|
val appendTimeFingerprintResult = AppendTimeFingerprint.result!!
|
||||||
val appendTimePatternScanStartIndex = appendTimeFingerprintResult.scanResult.patternScanResult!!.startIndex
|
val appendTimePatternScanStartIndex = appendTimeFingerprintResult.scanResult.patternScanResult!!.startIndex
|
||||||
val targetRegister =
|
val targetRegister =
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
package app.revanced.patches.youtube.layout.sponsorblock
|
package app.revanced.patches.youtube.layout.sponsorblock
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.PatchException
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsResourcePatch
|
import app.revanced.patches.youtube.misc.settings.SettingsResourcePatch
|
||||||
import app.revanced.util.ResourceGroup
|
import app.revanced.util.ResourceGroup
|
||||||
import app.revanced.util.copyResources
|
import app.revanced.util.copyResources
|
||||||
import app.revanced.util.copyXmlNode
|
|
||||||
import app.revanced.util.inputStreamFromBundledResource
|
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@@ -60,49 +58,6 @@ internal object SponsorBlockResourcePatch : ResourcePatch() {
|
|||||||
context.copyResources("sponsorblock", resourceGroup)
|
context.copyResources("sponsorblock", resourceGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy nodes from host resources to their real xml files
|
PlayerControlsResourcePatch.addTopControls("sponsorblock")
|
||||||
|
|
||||||
val hostingResourceStream =
|
|
||||||
inputStreamFromBundledResource(
|
|
||||||
"sponsorblock",
|
|
||||||
"host/layout/youtube_controls_layout.xml",
|
|
||||||
)!!
|
|
||||||
|
|
||||||
var modifiedControlsLayout = false
|
|
||||||
val editor = context.xmlEditor["res/layout/youtube_controls_layout.xml"]
|
|
||||||
"RelativeLayout".copyXmlNode(
|
|
||||||
context.xmlEditor[hostingResourceStream],
|
|
||||||
editor,
|
|
||||||
).also {
|
|
||||||
val document = editor.file
|
|
||||||
|
|
||||||
val children = document.getElementsByTagName("RelativeLayout").item(0).childNodes
|
|
||||||
|
|
||||||
// Replace the startOf with the voting button view so that the button does not overlap
|
|
||||||
for (i in 1 until children.length) {
|
|
||||||
val view = children.item(i)
|
|
||||||
|
|
||||||
// Replace the attribute for a specific node only
|
|
||||||
if (!(
|
|
||||||
view.hasAttributes() &&
|
|
||||||
view.attributes.getNamedItem(
|
|
||||||
"android:id",
|
|
||||||
).nodeValue.endsWith("live_chat_overlay_button")
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// voting button id from the voting button view from the youtube_controls_layout.xml host file
|
|
||||||
val votingButtonId = "@+id/revanced_sb_voting_button"
|
|
||||||
|
|
||||||
view.attributes.getNamedItem("android:layout_toStartOf").nodeValue = votingButtonId
|
|
||||||
|
|
||||||
modifiedControlsLayout = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}.close()
|
|
||||||
|
|
||||||
if (!modifiedControlsLayout) throw PatchException("Could not modify controls layout")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")),
|
||||||
|
)
|
||||||
@@ -1,390 +1,11 @@
|
|||||||
package app.revanced.patches.youtube.misc.fix.playback
|
package app.revanced.patches.youtube.misc.fix.playback
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
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.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.backgroundplayback.BackgroundPlaybackPatch
|
|
||||||
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.*
|
|
||||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
|
||||||
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(
|
@Deprecated("This patch is obsolete.", replaceWith = ReplaceWith("SpoofVideoStreamsPatch"))
|
||||||
name = "Spoof client",
|
|
||||||
description = "Spoofs the client to allow video playback.",
|
|
||||||
dependencies = [
|
|
||||||
SettingsPatch::class,
|
|
||||||
AddResourcesPatch::class,
|
|
||||||
UserAgentClientSpoofPatch::class,
|
|
||||||
// Required since iOS livestream fix partially enables background playback.
|
|
||||||
BackgroundPlaybackPatch::class,
|
|
||||||
PlayerTypeHookPatch::class,
|
|
||||||
],
|
|
||||||
compatiblePackages = [
|
|
||||||
CompatiblePackage(
|
|
||||||
"com.google.android.youtube",
|
|
||||||
[
|
|
||||||
// This patch works with these versions,
|
|
||||||
// but the dependent background playback patch does not.
|
|
||||||
// "18.37.36",
|
|
||||||
// "18.38.44",
|
|
||||||
// "18.43.45",
|
|
||||||
// "18.44.41",
|
|
||||||
// "18.45.43",
|
|
||||||
"18.48.39",
|
|
||||||
"18.49.37",
|
|
||||||
"19.01.34",
|
|
||||||
"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 SpoofClientPatch : BytecodePatch(
|
object SpoofClientPatch : BytecodePatch(
|
||||||
setOf(
|
dependencies = setOf(SpoofVideoStreamsPatch::class),
|
||||||
// Client type spoof.
|
|
||||||
BuildInitPlaybackRequestFingerprint,
|
|
||||||
BuildPlayerRequestURIFingerprint,
|
|
||||||
SetPlayerRequestClientTypeFingerprint,
|
|
||||||
CreatePlayerRequestBodyFingerprint,
|
|
||||||
CreatePlayerRequestBodyWithModelFingerprint,
|
|
||||||
CreatePlayerRequestBodyWithVersionReleaseFingerprint,
|
|
||||||
|
|
||||||
// Player gesture config.
|
|
||||||
PlayerGestureConfigSyntheticFingerprint,
|
|
||||||
|
|
||||||
// Player speed menu item.
|
|
||||||
CreatePlaybackSpeedMenuItemFingerprint,
|
|
||||||
|
|
||||||
// Video qualities missing.
|
|
||||||
BuildRequestFingerprint,
|
|
||||||
|
|
||||||
// Livestream audio only background playback.
|
|
||||||
PlayerResponseModelBackgroundAudioPlaybackFingerprint,
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
override fun execute(context: BytecodeContext) {}
|
||||||
"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
|
|
||||||
|
|
||||||
// region Fix livestream audio only background play if spoofing to iOS.
|
|
||||||
// This force enables audio background playback.
|
|
||||||
|
|
||||||
PlayerResponseModelBackgroundAudioPlaybackFingerprint.resultOrThrow().mutableMethod.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideBackgroundAudioPlayback()Z
|
|
||||||
move-result v0
|
|
||||||
if-eqz v0, :do_not_override
|
|
||||||
return v0
|
|
||||||
:do_not_override
|
|
||||||
nop
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// Fix playback speed menu item if spoofing to iOS.
|
|
||||||
|
|
||||||
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
|
|
||||||
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
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,239 +1,12 @@
|
|||||||
package app.revanced.patches.youtube.misc.fix.playback
|
package app.revanced.patches.youtube.misc.fix.playback
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
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.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(
|
@Deprecated("This patch is obsolete.", replaceWith = ReplaceWith("SpoofVideoStreamsPatch"))
|
||||||
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.")
|
|
||||||
object SpoofSignaturePatch : BytecodePatch(
|
object SpoofSignaturePatch : BytecodePatch(
|
||||||
setOf(
|
dependencies = setOf(SpoofVideoStreamsPatch::class),
|
||||||
PlayerResponseModelImplGeneralFingerprint,
|
|
||||||
PlayerResponseModelImplLiveStreamFingerprint,
|
|
||||||
PlayerResponseModelImplRecommendedLevelFingerprint,
|
|
||||||
StoryboardRendererSpecFingerprint,
|
|
||||||
StoryboardRendererDecoderSpecFingerprint,
|
|
||||||
StoryboardRendererDecoderRecommendedLevelFingerprint,
|
|
||||||
StoryboardThumbnailParentFingerprint,
|
|
||||||
SpoofSignaturePatchScrubbedPreviewLayoutFingerprint,
|
|
||||||
StatsQueryParameterFingerprint,
|
|
||||||
ParamsMapPutFingerprint,
|
|
||||||
),
|
|
||||||
) {
|
) {
|
||||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
override fun execute(context: BytecodeContext) {}
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,8 @@ package app.revanced.patches.youtube.misc.fix.playback
|
|||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
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.")
|
@Deprecated("This patch will be removed in the future.")
|
||||||
object SpoofSignatureResourcePatch : ResourcePatch() {
|
object SpoofSignatureResourcePatch : ResourcePatch() {
|
||||||
internal var scrubbedPreviewThumbnailResourceId: Long = -1
|
override fun execute(context: ResourceContext) {}
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
|
||||||
scrubbedPreviewThumbnailResourceId = ResourceMappingPatch[
|
|
||||||
"id",
|
|
||||||
"thumbnail",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,285 @@
|
|||||||
|
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",
|
||||||
|
summaryKey = null
|
||||||
|
),
|
||||||
|
SwitchPreference(
|
||||||
|
"revanced_spoof_video_streams_ios_force_avc",
|
||||||
|
tag = "app.revanced.integrations.youtube.settings.preference.ForceAVCSpoofingPreference",
|
||||||
|
),
|
||||||
|
NonInteractivePreference("revanced_spoof_video_streams_about_android_vr"),
|
||||||
|
NonInteractivePreference("revanced_spoof_video_streams_about_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 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.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal object BuildRequestFingerprint : MethodFingerprint(
|
internal object BuildRequestFingerprint : MethodFingerprint(
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
returnType = "Lorg/chromium/net/UrlRequest;",
|
returnType = "Lorg/chromium/net/UrlRequest;",
|
||||||
opcodes = listOf(
|
customFingerprint = { methodDef, _ ->
|
||||||
Opcode.INVOKE_DIRECT,
|
// Different targets have slightly different parameters
|
||||||
Opcode.INVOKE_VIRTUAL
|
|
||||||
)
|
// 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,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
|
|
||||||
|
|
||||||
internal object PlayerResponseModelBackgroundAudioPlaybackFingerprint : MethodFingerprint(
|
|
||||||
returnType = "Z",
|
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
|
||||||
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
|
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.RETURN,
|
|
||||||
null, // Opcode.CONST_4 or Opcode.MOVE
|
|
||||||
Opcode.RETURN,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -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)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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 ProtobufClassParseByteBufferFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PROTECTED or AccessFlags.STATIC,
|
||||||
|
parameters = listOf("L", "Ljava/nio/ByteBuffer;"),
|
||||||
|
returnType = "L",
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.RETURN_OBJECT,
|
||||||
|
),
|
||||||
|
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 - "),
|
|
||||||
)
|
|
||||||
@@ -32,12 +32,11 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
|||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube",
|
"com.google.android.youtube",
|
||||||
setOf(
|
setOf(
|
||||||
// Patch supports these versions but ClientSpoof does not.
|
"18.37.36",
|
||||||
// "18.37.36",
|
"18.38.44",
|
||||||
// "18.38.44",
|
"18.43.45",
|
||||||
// "18.43.45",
|
"18.44.41",
|
||||||
// "18.44.41",
|
"18.45.43",
|
||||||
// "18.45.43",
|
|
||||||
"18.48.39",
|
"18.48.39",
|
||||||
"18.49.37",
|
"18.49.37",
|
||||||
"19.01.34",
|
"19.01.34",
|
||||||
|
|||||||
@@ -3,70 +3,18 @@ package app.revanced.patches.youtube.misc.playercontrols
|
|||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patcher.util.DomFileEditor
|
|
||||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
@Patch(dependencies = [ResourceMappingPatch::class])
|
@Patch(
|
||||||
|
dependencies = [PlayerControlsBytecodePatch::class],
|
||||||
|
)
|
||||||
|
@Deprecated("Patch renamed to PlayerControlsResourcePatch", replaceWith = ReplaceWith("PlayerControlsBytecodePatch"))
|
||||||
object BottomControlsResourcePatch : ResourcePatch(), Closeable {
|
object BottomControlsResourcePatch : ResourcePatch(), Closeable {
|
||||||
internal var bottomUiContainerResourceId: Long = -1
|
override fun execute(context: ResourceContext) {}
|
||||||
|
|
||||||
private const val TARGET_RESOURCE_NAME = "youtube_controls_bottom_ui_container.xml"
|
|
||||||
private const val TARGET_RESOURCE = "res/layout/$TARGET_RESOURCE_NAME"
|
|
||||||
|
|
||||||
// The element to the left of the element being added.
|
|
||||||
private var lastLeftOf = "fullscreen_button"
|
|
||||||
|
|
||||||
private lateinit var resourceContext: ResourceContext
|
|
||||||
private lateinit var targetDocumentEditor: DomFileEditor
|
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
|
||||||
resourceContext = context
|
|
||||||
targetDocumentEditor = context.xmlEditor[TARGET_RESOURCE]
|
|
||||||
|
|
||||||
bottomUiContainerResourceId = ResourceMappingPatch["id", "bottom_ui_container_stub"]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add new controls to the bottom of the YouTube player.
|
|
||||||
*
|
|
||||||
* @param resourceDirectoryName The name of the directory containing the hosting resource.
|
|
||||||
*/
|
|
||||||
fun addControls(resourceDirectoryName: String) {
|
fun addControls(resourceDirectoryName: String) {
|
||||||
val sourceDocumentEditor = resourceContext.xmlEditor[
|
PlayerControlsResourcePatch.addBottomControls(resourceDirectoryName)
|
||||||
this::class.java.classLoader.getResourceAsStream(
|
|
||||||
"$resourceDirectoryName/host/layout/$TARGET_RESOURCE_NAME",
|
|
||||||
)!!,
|
|
||||||
]
|
|
||||||
val sourceDocument = sourceDocumentEditor.file
|
|
||||||
val targetDocument = targetDocumentEditor.file
|
|
||||||
|
|
||||||
val targetElementTag = "android.support.constraint.ConstraintLayout"
|
|
||||||
|
|
||||||
val sourceElements = sourceDocument.getElementsByTagName(targetElementTag).item(0).childNodes
|
|
||||||
val targetElement = targetDocument.getElementsByTagName(targetElementTag).item(0)
|
|
||||||
|
|
||||||
for (index in 1 until sourceElements.length) {
|
|
||||||
val element = sourceElements.item(index).cloneNode(true)
|
|
||||||
|
|
||||||
// If the element has no attributes there's no point to adding it to the destination.
|
|
||||||
if (!element.hasAttributes()) continue
|
|
||||||
|
|
||||||
// Set the elements lastLeftOf attribute to the lastLeftOf value.
|
|
||||||
val namespace = "@+id"
|
|
||||||
element.attributes.getNamedItem("yt:layout_constraintRight_toLeftOf").nodeValue =
|
|
||||||
"$namespace/$lastLeftOf"
|
|
||||||
|
|
||||||
// Set lastLeftOf attribute to the current element.
|
|
||||||
val nameSpaceLength = 5
|
|
||||||
lastLeftOf = element.attributes.getNamedItem("android:id").nodeValue.substring(nameSpaceLength)
|
|
||||||
|
|
||||||
// Add the element.
|
|
||||||
targetDocument.adoptNode(element)
|
|
||||||
targetElement.appendChild(element)
|
|
||||||
}
|
|
||||||
sourceDocumentEditor.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() = targetDocumentEditor.close()
|
override fun close() {}
|
||||||
}
|
}
|
||||||
@@ -1,65 +1,144 @@
|
|||||||
package app.revanced.patches.youtube.misc.playercontrols
|
package app.revanced.patches.youtube.misc.playercontrols
|
||||||
|
|
||||||
import app.revanced.util.exception
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprintResult
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patches.youtube.shared.fingerprints.LayoutConstructorFingerprint
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.BottomControlsInflateFingerprint
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerControlsVisibilityFingerprint
|
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.ControlsOverlayVisibility
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.OverlayViewInflateFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerBottomControlsInflateFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerControlsIntegrationHookFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerTopControlsInflateFingerprint
|
||||||
|
import app.revanced.util.alsoResolve
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import app.revanced.util.indexOfFirstWideLiteralInstructionValueReversedOrThrow
|
||||||
|
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.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
description = "Manages the code for the player controls of the YouTube player.",
|
description = "Manages the code for the player controls of the YouTube player.",
|
||||||
dependencies = [BottomControlsResourcePatch::class],
|
dependencies = [PlayerControlsResourcePatch::class],
|
||||||
)
|
)
|
||||||
object PlayerControlsBytecodePatch : BytecodePatch(
|
object PlayerControlsBytecodePatch : BytecodePatch(
|
||||||
setOf(LayoutConstructorFingerprint, BottomControlsInflateFingerprint)
|
setOf(
|
||||||
|
PlayerTopControlsInflateFingerprint,
|
||||||
|
PlayerBottomControlsInflateFingerprint,
|
||||||
|
OverlayViewInflateFingerprint,
|
||||||
|
PlayerControlsIntegrationHookFingerprint
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
lateinit var showPlayerControlsFingerprintResult: MethodFingerprintResult
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/integrations/youtube/patches/PlayerControlsPatch;"
|
||||||
|
|
||||||
private var moveToRegisterInstructionIndex: Int = 0
|
private lateinit var inflateTopControlMethod: MutableMethod
|
||||||
private var viewRegister: Int = 0
|
private var inflateTopControlInsertIndex: Int = -1
|
||||||
private lateinit var inflateFingerprintResult: MethodFingerprintResult
|
private var inflateTopControlRegister: Int = -1
|
||||||
|
|
||||||
|
private lateinit var inflateBottomControlMethod: MutableMethod
|
||||||
|
private var inflateBottomControlInsertIndex: Int = -1
|
||||||
|
private var inflateBottomControlRegister: Int = -1
|
||||||
|
|
||||||
|
private lateinit var visibilityMethod: MutableMethod
|
||||||
|
private var visibilityInsertIndex: Int = 0
|
||||||
|
|
||||||
|
private lateinit var visibilityImmediateMethod: MutableMethod
|
||||||
|
private var visibilityImmediateInsertIndex: Int = 0
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
LayoutConstructorFingerprint.result?.let {
|
fun MutableMethod.indexOfFirstViewInflateOrThrow() =
|
||||||
if (!PlayerControlsVisibilityFingerprint.resolve(context, it.classDef))
|
indexOfFirstInstructionOrThrow {
|
||||||
throw LayoutConstructorFingerprint.exception
|
val reference = getReference<MethodReference>()
|
||||||
} ?: throw LayoutConstructorFingerprint.exception
|
reference?.definingClass == "Landroid/view/ViewStub;" &&
|
||||||
|
reference.name == "inflate"
|
||||||
|
}
|
||||||
|
|
||||||
showPlayerControlsFingerprintResult = PlayerControlsVisibilityFingerprint.result!!
|
PlayerBottomControlsInflateFingerprint.resultOrThrow().mutableMethod.apply{
|
||||||
|
inflateBottomControlMethod = this
|
||||||
|
|
||||||
inflateFingerprintResult = BottomControlsInflateFingerprint.result!!.also {
|
val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1
|
||||||
moveToRegisterInstructionIndex = it.scanResult.patternScanResult!!.endIndex
|
inflateBottomControlRegister = getInstruction<OneRegisterInstruction>(inflateReturnObjectIndex).registerA
|
||||||
viewRegister =
|
inflateBottomControlInsertIndex = inflateReturnObjectIndex + 1
|
||||||
(it.mutableMethod.implementation!!.instructions[moveToRegisterInstructionIndex] as OneRegisterInstruction).registerA
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlayerTopControlsInflateFingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
inflateTopControlMethod = this
|
||||||
|
|
||||||
|
val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1
|
||||||
|
inflateTopControlRegister = getInstruction<OneRegisterInstruction>(inflateReturnObjectIndex).registerA
|
||||||
|
inflateTopControlInsertIndex = inflateReturnObjectIndex + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlsOverlayVisibility.alsoResolve(
|
||||||
|
context, PlayerTopControlsInflateFingerprint
|
||||||
|
).mutableMethod.apply {
|
||||||
|
visibilityMethod = this
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook the fullscreen close button. Used to fix visibility
|
||||||
|
// when seeking and other situations.
|
||||||
|
OverlayViewInflateFingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
val resourceIndex = indexOfFirstWideLiteralInstructionValueReversedOrThrow(
|
||||||
|
PlayerControlsResourcePatch.fullscreenButton
|
||||||
|
)
|
||||||
|
|
||||||
|
val index = indexOfFirstInstructionOrThrow(resourceIndex) {
|
||||||
|
opcode == Opcode.CHECK_CAST && getReference<TypeReference>()?.type ==
|
||||||
|
"Landroid/widget/ImageView;"
|
||||||
|
}
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
|
||||||
|
addInstruction(index + 1, "invoke-static { v$register }, " +
|
||||||
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->setFullscreenCloseButton(Landroid/widget/ImageView;)V")
|
||||||
|
}
|
||||||
|
|
||||||
|
visibilityImmediateMethod = PlayerControlsIntegrationHookFingerprint.resultOrThrow().mutableMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injects the code to change the visibility of controls.
|
* Injects the code to initialize the controls.
|
||||||
* @param descriptor The descriptor of the method which should be called.
|
* @param descriptor The descriptor of the method which should be called.
|
||||||
*/
|
*/
|
||||||
fun injectVisibilityCheckCall(descriptor: String) {
|
internal fun initializeTopControl(descriptor: String) {
|
||||||
showPlayerControlsFingerprintResult.mutableMethod.addInstruction(
|
inflateTopControlMethod.addInstruction(
|
||||||
0,
|
inflateTopControlInsertIndex++,
|
||||||
"""
|
"invoke-static { v$inflateTopControlRegister }, $descriptor->initialize(Landroid/view/View;)V"
|
||||||
invoke-static {p1}, $descriptor
|
|
||||||
"""
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injects the code to initialize the controls.
|
* Injects the code to initialize the controls.
|
||||||
* @param descriptor The descriptor of the method which should be calleed.
|
* @param descriptor The descriptor of the method which should be called.
|
||||||
*/
|
*/
|
||||||
fun initializeControl(descriptor: String) {
|
fun initializeBottomControl(descriptor: String) {
|
||||||
inflateFingerprintResult.mutableMethod.addInstruction(
|
inflateBottomControlMethod.addInstruction(
|
||||||
moveToRegisterInstructionIndex + 1,
|
inflateBottomControlInsertIndex++,
|
||||||
"invoke-static {v$viewRegister}, $descriptor"
|
"invoke-static { v$inflateBottomControlRegister }, $descriptor->initializeButton(Landroid/view/View;)V"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Injects the code to change the visibility of controls.
|
||||||
|
* @param descriptor The descriptor of the method which should be called.
|
||||||
|
*/
|
||||||
|
fun injectVisibilityCheckCall(descriptor: String) {
|
||||||
|
visibilityMethod.addInstruction(
|
||||||
|
visibilityInsertIndex++,
|
||||||
|
"invoke-static { p1 , p2 }, $descriptor->changeVisibility(ZZ)V"
|
||||||
|
)
|
||||||
|
|
||||||
|
visibilityImmediateMethod.addInstruction(
|
||||||
|
visibilityImmediateInsertIndex++,
|
||||||
|
"invoke-static { p0 }, $descriptor->changeVisibilityImmediate(Z)V"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated("Obsolete", replaceWith = ReplaceWith("initializeBottomControl"))
|
||||||
|
fun initializeControl(descriptor: String)= initializeBottomControl(descriptor)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patcher.util.DomFileEditor
|
||||||
|
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||||
|
import app.revanced.util.copyXmlNode
|
||||||
|
import app.revanced.util.findElementByAttributeValue
|
||||||
|
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||||
|
import app.revanced.util.inputStreamFromBundledResource
|
||||||
|
import org.w3c.dom.Node
|
||||||
|
import java.io.Closeable
|
||||||
|
|
||||||
|
@Patch(dependencies = [ResourceMappingPatch::class])
|
||||||
|
object PlayerControlsResourcePatch : ResourcePatch(), Closeable {
|
||||||
|
private const val TARGET_RESOURCE_NAME = "youtube_controls_bottom_ui_container.xml"
|
||||||
|
private const val TARGET_RESOURCE = "res/layout/$TARGET_RESOURCE_NAME"
|
||||||
|
|
||||||
|
internal var bottomUiContainerResourceId: Long = -1L
|
||||||
|
internal var controlsLayoutStub: Long = -1L
|
||||||
|
internal var heatseekerViewstub = -1L
|
||||||
|
internal var fullscreenButton = -1L
|
||||||
|
|
||||||
|
private lateinit var resourceContext: ResourceContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The element to the left of the element being added.
|
||||||
|
*/
|
||||||
|
private var bottomLastLeftOf = "@id/fullscreen_button"
|
||||||
|
private lateinit var bottomInsertBeforeNode: Node
|
||||||
|
private lateinit var bottomTargetDocumentEditor: DomFileEditor
|
||||||
|
private lateinit var bottomTargetElement : Node
|
||||||
|
|
||||||
|
override fun execute(context: ResourceContext) {
|
||||||
|
bottomUiContainerResourceId = ResourceMappingPatch["id", "bottom_ui_container_stub"]
|
||||||
|
controlsLayoutStub = ResourceMappingPatch["id", "controls_layout_stub"]
|
||||||
|
heatseekerViewstub = ResourceMappingPatch["id", "heatseeker_viewstub"]
|
||||||
|
fullscreenButton = ResourceMappingPatch["id", "fullscreen_button"]
|
||||||
|
|
||||||
|
resourceContext = context
|
||||||
|
bottomTargetDocumentEditor = context.xmlEditor[TARGET_RESOURCE]
|
||||||
|
val document = bottomTargetDocumentEditor.file
|
||||||
|
|
||||||
|
bottomTargetElement = document.getElementsByTagName(
|
||||||
|
"android.support.constraint.ConstraintLayout"
|
||||||
|
).item(0)
|
||||||
|
|
||||||
|
bottomInsertBeforeNode = document.childNodes.findElementByAttributeValue(
|
||||||
|
"android:inflatedId",
|
||||||
|
bottomLastLeftOf
|
||||||
|
) ?: document.childNodes.findElementByAttributeValueOrThrow(
|
||||||
|
"android:id", // Older targets use non inflated id.
|
||||||
|
bottomLastLeftOf
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal until this is modified to work with any patch (and not just SponsorBlock).
|
||||||
|
internal fun addTopControls(resourceDirectoryName: String) {
|
||||||
|
val hostingResourceStream = inputStreamFromBundledResource(
|
||||||
|
resourceDirectoryName,
|
||||||
|
"host/layout/youtube_controls_layout.xml",
|
||||||
|
)!!
|
||||||
|
|
||||||
|
val editor = resourceContext.xmlEditor["res/layout/youtube_controls_layout.xml"]
|
||||||
|
|
||||||
|
"RelativeLayout".copyXmlNode(
|
||||||
|
resourceContext.xmlEditor[hostingResourceStream],
|
||||||
|
editor,
|
||||||
|
).use {
|
||||||
|
val document = editor.file
|
||||||
|
val children = document.getElementsByTagName("RelativeLayout").item(0).childNodes
|
||||||
|
|
||||||
|
// Replace the startOf with the voting button view so that the button does not overlap
|
||||||
|
for (index in 1 until children.length) {
|
||||||
|
val view = children.item(index)
|
||||||
|
|
||||||
|
// FIXME: This uses hard coded values that only works with SponsorBlock.
|
||||||
|
// If other top buttons are added by other patches, this code must be changed.
|
||||||
|
if (view.hasAttributes() && view.attributes.getNamedItem("android:id")
|
||||||
|
.nodeValue.endsWith("live_chat_overlay_button")
|
||||||
|
) {
|
||||||
|
// voting button id from the voting button view from the youtube_controls_layout.xml host file
|
||||||
|
val votingButtonId = "@+id/revanced_sb_voting_button"
|
||||||
|
view.attributes.getNamedItem("android:layout_toStartOf").nodeValue =
|
||||||
|
votingButtonId
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw PatchException("Could not find expected xml to modify")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add new controls to the bottom of the YouTube player.
|
||||||
|
*
|
||||||
|
* @param resourceDirectoryName The name of the directory containing the hosting resource.
|
||||||
|
*/
|
||||||
|
fun addBottomControls(resourceDirectoryName: String) {
|
||||||
|
val sourceDocumentEditor = resourceContext.xmlEditor[
|
||||||
|
this::class.java.classLoader.getResourceAsStream(
|
||||||
|
"$resourceDirectoryName/host/layout/$TARGET_RESOURCE_NAME",
|
||||||
|
)!!,
|
||||||
|
]
|
||||||
|
|
||||||
|
val sourceElements = sourceDocumentEditor.file.getElementsByTagName(
|
||||||
|
"android.support.constraint.ConstraintLayout"
|
||||||
|
).item(0).childNodes
|
||||||
|
|
||||||
|
// Copy the patch layout xml into the target layout file.
|
||||||
|
for (index in 1 until sourceElements.length) {
|
||||||
|
val element = sourceElements.item(index).cloneNode(true)
|
||||||
|
|
||||||
|
// If the element has no attributes there's no point to adding it to the destination.
|
||||||
|
if (!element.hasAttributes()) continue
|
||||||
|
|
||||||
|
element.attributes.getNamedItem("yt:layout_constraintRight_toLeftOf").nodeValue = bottomLastLeftOf
|
||||||
|
bottomLastLeftOf = element.attributes.getNamedItem("android:id").nodeValue
|
||||||
|
|
||||||
|
bottomTargetDocumentEditor.file.adoptNode(element)
|
||||||
|
// Elements do not need to be added in the layout order since a layout constraint is used,
|
||||||
|
// but in order is easier to make sense of while debugging.
|
||||||
|
bottomTargetElement.insertBefore(element, bottomInsertBeforeNode)
|
||||||
|
bottomInsertBeforeNode = element
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceDocumentEditor.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
arrayOf(
|
||||||
|
"@id/bottom_end_container",
|
||||||
|
"@id/multiview_button",
|
||||||
|
).forEach {
|
||||||
|
bottomTargetDocumentEditor.file.childNodes.findElementByAttributeValue(
|
||||||
|
"android:id",
|
||||||
|
it
|
||||||
|
)?.setAttribute("yt:layout_constraintRight_toLeftOf", bottomLastLeftOf)
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomTargetDocumentEditor.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
|
|
||||||
import app.revanced.util.patch.LiteralValueFingerprint
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal object BottomControlsInflateFingerprint : LiteralValueFingerprint(
|
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.SYNTHETIC,
|
|
||||||
returnType = "L",
|
|
||||||
parameters = listOf(),
|
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT
|
|
||||||
),
|
|
||||||
literalSupplier = { BottomControlsResourcePatch.bottomUiContainerResourceId }
|
|
||||||
)
|
|
||||||
@@ -4,7 +4,10 @@ import app.revanced.patcher.extensions.or
|
|||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
internal object PlayerControlsVisibilityFingerprint : MethodFingerprint(
|
/**
|
||||||
|
* Resolves to the class found in [PlayerTopControlsInflateFingerprint].
|
||||||
|
*/
|
||||||
|
internal object ControlsOverlayVisibility : MethodFingerprint(
|
||||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
parameters = listOf("Z", "Z")
|
parameters = listOf("Z", "Z")
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||||
|
import app.revanced.util.containsWideLiteralInstructionValue
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object OverlayViewInflateFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "V",
|
||||||
|
parameters = listOf("Landroid/view/View;"),
|
||||||
|
customFingerprint = { methodDef, _ ->
|
||||||
|
methodDef.containsWideLiteralInstructionValue(PlayerControlsResourcePatch.fullscreenButton) &&
|
||||||
|
methodDef.containsWideLiteralInstructionValue(PlayerControlsResourcePatch.heatseekerViewstub)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
|
||||||
|
internal object PlayerBottomControlsInflateFingerprint : LiteralValueFingerprint(
|
||||||
|
returnType = "Ljava/lang/Object;",
|
||||||
|
parameters = listOf(),
|
||||||
|
literalSupplier = { PlayerControlsResourcePatch.bottomUiContainerResourceId }
|
||||||
|
)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object PlayerControlsIntegrationHookFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
returnType = "V",
|
||||||
|
parameters = listOf("Z"),
|
||||||
|
customFingerprint = { methodDef, classDef ->
|
||||||
|
methodDef.name == "fullscreenButtonVisibilityChanged" &&
|
||||||
|
classDef.type == "Lapp/revanced/integrations/youtube/patches/PlayerControlsPatch;"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object PlayerTopControlsInflateFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "V",
|
||||||
|
parameters = listOf(),
|
||||||
|
literalSupplier = { PlayerControlsResourcePatch.controlsLayoutStub }
|
||||||
|
)
|
||||||
@@ -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.NonInteractivePreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
||||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
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.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.fingerprints.LicenseActivityOnCreateFingerprint
|
import app.revanced.patches.youtube.misc.settings.fingerprints.LicenseActivityOnCreateFingerprint
|
||||||
import app.revanced.patches.youtube.misc.settings.fingerprints.SetThemeFingerprint
|
import app.revanced.patches.youtube.misc.settings.fingerprints.SetThemeFingerprint
|
||||||
@@ -30,6 +31,9 @@ import java.io.Closeable
|
|||||||
IntegrationsPatch::class,
|
IntegrationsPatch::class,
|
||||||
SettingsResourcePatch::class,
|
SettingsResourcePatch::class,
|
||||||
AddResourcesPatch::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 :
|
object SettingsPatch :
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ object SettingsResourcePatch : BaseSettingsResourcePatch(
|
|||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
super.execute(context)
|
super.execute(context)
|
||||||
|
|
||||||
AddResourcesPatch(this::class)
|
|
||||||
|
|
||||||
// Used for a fingerprint from SettingsPatch.
|
// Used for a fingerprint from SettingsPatch.
|
||||||
appearanceStringId = ResourceMappingPatch["string", "app_theme_appearance_dark"]
|
appearanceStringId = ResourceMappingPatch["string", "app_theme_appearance_dark"]
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ object PlaybackSpeedButtonPatch : BytecodePatch(emptySet()) {
|
|||||||
SwitchPreference("revanced_playback_speed_dialog_button"),
|
SwitchPreference("revanced_playback_speed_dialog_button"),
|
||||||
)
|
)
|
||||||
|
|
||||||
PlayerControlsBytecodePatch.initializeControl("$SPEED_BUTTON_CLASS_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
|
PlayerControlsBytecodePatch.initializeBottomControl(SPEED_BUTTON_CLASS_DESCRIPTOR)
|
||||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$SPEED_BUTTON_CLASS_DESCRIPTOR->changeVisibility(Z)V")
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall(SPEED_BUTTON_CLASS_DESCRIPTOR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user