mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-08 18:33:57 +01:00
Compare commits
545 Commits
v1.0.0-dev
...
v2.75.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05c6c9e683 | ||
|
|
facb808e37 | ||
|
|
fe1427b88a | ||
|
|
634c6c3344 | ||
|
|
d438d9b866 | ||
|
|
e4139a112e | ||
|
|
559d7ad50c | ||
|
|
e1bbe59b40 | ||
|
|
2f932d697b | ||
|
|
237129d327 | ||
|
|
e4d7dc0aa9 | ||
|
|
fed9e4aafa | ||
|
|
85aa45d3f2 | ||
|
|
16df8d5ebc | ||
|
|
e89c3dad39 | ||
|
|
0f78f621c7 | ||
|
|
fe30cea22d | ||
|
|
5a30d8564c | ||
|
|
34222d9f2c | ||
|
|
f765af0929 | ||
|
|
298c33deb6 | ||
|
|
111db2a499 | ||
|
|
fa536957cc | ||
|
|
980deec6af | ||
|
|
658638bfad | ||
|
|
d5ee3b006d | ||
|
|
36ab007924 | ||
|
|
c46705f21a | ||
|
|
31ae362d18 | ||
|
|
b64f7bb400 | ||
|
|
64244ec476 | ||
|
|
f005314358 | ||
|
|
ae1aeffc62 | ||
|
|
56768caa4c | ||
|
|
19484ca2bc | ||
|
|
a8a98646e7 | ||
|
|
4927bc7451 | ||
|
|
66a5ca3fa8 | ||
|
|
6c0b9213fe | ||
|
|
70e21500cc | ||
|
|
784e97591a | ||
|
|
26ff3b338e | ||
|
|
28c864ae95 | ||
|
|
deb829c791 | ||
|
|
3c0f3ac968 | ||
|
|
2f6da4ed27 | ||
|
|
1b653c77c4 | ||
|
|
82ea2d3498 | ||
|
|
07dee5b87a | ||
|
|
7713516ed8 | ||
|
|
22fa09dc27 | ||
|
|
24d03387de | ||
|
|
04310901b7 | ||
|
|
3011c1fa45 | ||
|
|
6b83fe572a | ||
|
|
32d303f4b7 | ||
|
|
837c381e19 | ||
|
|
09ae60bb28 | ||
|
|
f9d180aad4 | ||
|
|
c40181140d | ||
|
|
c5e190a4d5 | ||
|
|
24e3f8b24e | ||
|
|
53b0485642 | ||
|
|
b362b560b6 | ||
|
|
b285bf96bd | ||
|
|
bfb47eb1c4 | ||
|
|
4e2d30228a | ||
|
|
dce28760f5 | ||
|
|
68ec6d2258 | ||
|
|
83498fd29c | ||
|
|
1ab35a1cfa | ||
|
|
e0b45237bb | ||
|
|
161a1b0af1 | ||
|
|
7618376388 | ||
|
|
2bdb857a9f | ||
|
|
be02acecfc | ||
|
|
9e873329c6 | ||
|
|
8a2b79ec16 | ||
|
|
9842f7ca0d | ||
|
|
8a913c6a2a | ||
|
|
84676ef33d | ||
|
|
eb985942f0 | ||
|
|
886018d6fb | ||
|
|
e7a17ccbc6 | ||
|
|
c707dc8769 | ||
|
|
1bb93e3598 | ||
|
|
e8848171ac | ||
|
|
249288c8a5 | ||
|
|
1791525b54 | ||
|
|
47fbe3cb25 | ||
|
|
89235ca7fa | ||
|
|
f63abb8eb3 | ||
|
|
b701f86fa1 | ||
|
|
3117ee7ba4 | ||
|
|
b8e084b9e1 | ||
|
|
bafeff84c2 | ||
|
|
8d193786cb | ||
|
|
d78d271967 | ||
|
|
59ea2bc636 | ||
|
|
e8b550f5aa | ||
|
|
e73c0e0a00 | ||
|
|
8c132be096 | ||
|
|
cb10ea1797 | ||
|
|
6d6a0375c8 | ||
|
|
8541ee0066 | ||
|
|
f7e0b46bc7 | ||
|
|
59af825d0a | ||
|
|
04ffffbdf2 | ||
|
|
ecc131fa4f | ||
|
|
a4f1d3558e | ||
|
|
4cbd58721d | ||
|
|
7cfe43e666 | ||
|
|
c93d7bc212 | ||
|
|
c28fd5cf20 | ||
|
|
069f754d51 | ||
|
|
cba04cf5a6 | ||
|
|
5e358c7319 | ||
|
|
ce69471684 | ||
|
|
a0981dda9d | ||
|
|
6073181ee1 | ||
|
|
522587321c | ||
|
|
b16748794e | ||
|
|
01e9a396d6 | ||
|
|
86fcd4d475 | ||
|
|
8bbc744114 | ||
|
|
d7a4ad76ea | ||
|
|
7e6ee73908 | ||
|
|
87954a1aaf | ||
|
|
e5a2ed0f70 | ||
|
|
2295aaebf4 | ||
|
|
a72bf150c2 | ||
|
|
0290cd3d36 | ||
|
|
e8d61e0c0e | ||
|
|
19769d80eb | ||
|
|
12c16d4644 | ||
|
|
75f4653c16 | ||
|
|
3286d26996 | ||
|
|
edbc36e90f | ||
|
|
7ad2be7f27 | ||
|
|
1702322671 | ||
|
|
9c9bfbd593 | ||
|
|
3df662a39c | ||
|
|
572c110a6e | ||
|
|
0be9f00915 | ||
|
|
0b6f643ff3 | ||
|
|
1989d53938 | ||
|
|
ac1acf10e6 | ||
|
|
0ad686da21 | ||
|
|
e734b2231a | ||
|
|
efe06d1cdc | ||
|
|
a0f9013fab | ||
|
|
93dcd542d5 | ||
|
|
3bac23f577 | ||
|
|
febcf60efb | ||
|
|
0d74a2dfa6 | ||
|
|
5b04fbb748 | ||
|
|
fbfa0ea3be | ||
|
|
4a30afe1e2 | ||
|
|
dd1df32036 | ||
|
|
a52d674cde | ||
|
|
67f6041bab | ||
|
|
6b3f8e46fd | ||
|
|
b95058f20a | ||
|
|
5b80ac1216 | ||
|
|
6fe1bfb856 | ||
|
|
589a7c01d1 | ||
|
|
4cd6d4459a | ||
|
|
89a92b2057 | ||
|
|
9f265a46cf | ||
|
|
a740690a08 | ||
|
|
d722a64d3c | ||
|
|
abe2674290 | ||
|
|
e5ec40f6fa | ||
|
|
96c43fc6f5 | ||
|
|
4e36d72c5c | ||
|
|
6197246e8e | ||
|
|
15a044cae1 | ||
|
|
ede60b994c | ||
|
|
c15cc431d6 | ||
|
|
fb006f87ab | ||
|
|
28206fd804 | ||
|
|
e425e75152 | ||
|
|
69d6c55942 | ||
|
|
f2b29351b4 | ||
|
|
db0b468e71 | ||
|
|
b75e759348 | ||
|
|
83b41322c4 | ||
|
|
8d8878aa08 | ||
|
|
d628674cdd | ||
|
|
23b182ccbb | ||
|
|
4a784ecd6e | ||
|
|
0165b53c6d | ||
|
|
17a0e16087 | ||
|
|
8cf91f39a5 | ||
|
|
9ef93f6618 | ||
|
|
8c3866f5e3 | ||
|
|
d115d649f4 | ||
|
|
ddc789395e | ||
|
|
e1200d4e3f | ||
|
|
8276340d2b | ||
|
|
8f32af4de1 | ||
|
|
a2bc9f0722 | ||
|
|
f94bb06996 | ||
|
|
1023936a26 | ||
|
|
98e5dbcc73 | ||
|
|
353806828b | ||
|
|
1ad69e350c | ||
|
|
42c562cff7 | ||
|
|
155121f891 | ||
|
|
c684a1c846 | ||
|
|
49ddc0a2ed | ||
|
|
221b61acc7 | ||
|
|
c133d495c3 | ||
|
|
aab39cbe93 | ||
|
|
bdab92faf2 | ||
|
|
50acd3d3b5 | ||
|
|
49a369a340 | ||
|
|
be8620bf91 | ||
|
|
32ed4c9f04 | ||
|
|
9c44497d40 | ||
|
|
8e604139e3 | ||
|
|
3de3a82afb | ||
|
|
d39fb7ca03 | ||
|
|
2d995d3757 | ||
|
|
464c98093a | ||
|
|
6ac5ed441f | ||
|
|
7925ca97c4 | ||
|
|
0c5f117d5d | ||
|
|
6ab475c284 | ||
|
|
75ac69455b | ||
|
|
e42043b939 | ||
|
|
5237c3a1a6 | ||
|
|
7d6f205fd9 | ||
|
|
f82aae930b | ||
|
|
83b11c92af | ||
|
|
a9abab97d7 | ||
|
|
a70da4f6c4 | ||
|
|
e53b490edf | ||
|
|
bfe995f635 | ||
|
|
19cc4a3e91 | ||
|
|
16f6486258 | ||
|
|
47360ea3b7 | ||
|
|
5c5b3d562d | ||
|
|
a62316a8c8 | ||
|
|
14f7d514d2 | ||
|
|
78d901338f | ||
|
|
9ae3d0546c | ||
|
|
3076a16d14 | ||
|
|
f8e1f2fe18 | ||
|
|
a9611f304e | ||
|
|
7c0e183df5 | ||
|
|
15e1ced2c9 | ||
|
|
bde7f99b5b | ||
|
|
60866af97d | ||
|
|
6580f71392 | ||
|
|
9c57961680 | ||
|
|
1d7a9ac437 | ||
|
|
51e08fb0b6 | ||
|
|
91feea1c50 | ||
|
|
fc8bfff427 | ||
|
|
9a3307c1b1 | ||
|
|
eff806c5e3 | ||
|
|
b45a44ff3f | ||
|
|
391e8fa8f6 | ||
|
|
dbf0668f28 | ||
|
|
b228640e4f | ||
|
|
0ddb2df640 | ||
|
|
c9525d0c26 | ||
|
|
3c5832eda2 | ||
|
|
eef7e87bbf | ||
|
|
535f621f20 | ||
|
|
695a0d14ad | ||
|
|
f70e54545b | ||
|
|
353f036a72 | ||
|
|
4d305775a0 | ||
|
|
d726443ca0 | ||
|
|
c5046edf8d | ||
|
|
e15d6afed1 | ||
|
|
f5055997c7 | ||
|
|
e6f41988dc | ||
|
|
c8567d35ef | ||
|
|
cea92cacfd | ||
|
|
8f83fc1edf | ||
|
|
b9e33d0d08 | ||
|
|
7df8f6b42a | ||
|
|
ac117f04df | ||
|
|
d3bb96d8b3 | ||
|
|
a31e4b4df3 | ||
|
|
90144fc55a | ||
|
|
12d1c36a3f | ||
|
|
a7fce11015 | ||
|
|
60029da7a3 | ||
|
|
46a586b2af | ||
|
|
8a86034728 | ||
|
|
ba1abf9319 | ||
|
|
82810cca6b | ||
|
|
964b598ad9 | ||
|
|
16fbfe555f | ||
|
|
16d50100e7 | ||
|
|
2397d6140b | ||
|
|
513276e950 | ||
|
|
2aab1a0c66 | ||
|
|
cfb5d0908b | ||
|
|
5e3eefe5dd | ||
|
|
6ccee3ef68 | ||
|
|
eddf520626 | ||
|
|
3613fb5409 | ||
|
|
97366cdd4c | ||
|
|
ee55cf970e | ||
|
|
ad1ed56a71 | ||
|
|
13ccea2e7c | ||
|
|
02d4ba64d6 | ||
|
|
891ac04155 | ||
|
|
c70c3de205 | ||
|
|
92dc97e2bf | ||
|
|
cdf4313880 | ||
|
|
0e0763221c | ||
|
|
6b515dd59e | ||
|
|
4ee40a954f | ||
|
|
fb96c6e4bc | ||
|
|
c5156fa7e3 | ||
|
|
8b0622f0bf | ||
|
|
1e3eddd3f2 | ||
|
|
1d12a17999 | ||
|
|
db410e1be6 | ||
|
|
9969d03116 | ||
|
|
9d1fecc2f5 | ||
|
|
63df96d251 | ||
|
|
d1266a252d | ||
|
|
6ec6d16dbc | ||
|
|
f940e4647e | ||
|
|
cc39b77b49 | ||
|
|
986065858d | ||
|
|
56475e8f38 | ||
|
|
2e0ff07d1a | ||
|
|
a654e25000 | ||
|
|
a8736a8f70 | ||
|
|
b0d6cee4db | ||
|
|
6bbcdf8ebd | ||
|
|
6fa8289ca6 | ||
|
|
97dbb43caa | ||
|
|
8cda5663ef | ||
|
|
5587c91899 | ||
|
|
4e6d82a334 | ||
|
|
be7302689e | ||
|
|
14d94d6a44 | ||
|
|
8d598f5b82 | ||
|
|
918782908b | ||
|
|
524fe19840 | ||
|
|
18aafb400b | ||
|
|
83ac6b3e34 | ||
|
|
890ebf04df | ||
|
|
6838daf1c3 | ||
|
|
08f4e8532c | ||
|
|
30d25a87da | ||
|
|
af0523df39 | ||
|
|
18a3f8e80c | ||
|
|
ce32c6f3f7 | ||
|
|
6b706bc93c | ||
|
|
6838e25764 | ||
|
|
f6f8b0ee31 | ||
|
|
8e26d0933d | ||
|
|
6f29325a85 | ||
|
|
46e163caa9 | ||
|
|
af19363997 | ||
|
|
c578e210bb | ||
|
|
980f7f04cc | ||
|
|
baeb0ad0d4 | ||
|
|
6c4a69fcfb | ||
|
|
4129fcf8a8 | ||
|
|
b39d60f46c | ||
|
|
8ce3a6b4e3 | ||
|
|
7107ee87d9 | ||
|
|
7ea5d87fa2 | ||
|
|
471a3d3263 | ||
|
|
b95fe30c78 | ||
|
|
cb0cf342c0 | ||
|
|
11db0f49c9 | ||
|
|
017ef1fb88 | ||
|
|
0991fbc9e8 | ||
|
|
e1eedc02ab | ||
|
|
9be32e10f6 | ||
|
|
87996f67a8 | ||
|
|
0822383271 | ||
|
|
0934af1ccd | ||
|
|
b3ad6fb8af | ||
|
|
87f5ebce9b | ||
|
|
a35d7c8951 | ||
|
|
3c8d3edff4 | ||
|
|
c35734bec2 | ||
|
|
e2b634053b | ||
|
|
3e6df0c96d | ||
|
|
7a2ea95e55 | ||
|
|
add3058d24 | ||
|
|
ef888f6b59 | ||
|
|
9b077d6ada | ||
|
|
93d4cee16e | ||
|
|
bc3b654357 | ||
|
|
64b4c92ca0 | ||
|
|
cd66ee4e57 | ||
|
|
f527226757 | ||
|
|
ec0b17a4d8 | ||
|
|
63598d0d42 | ||
|
|
14f6b66135 | ||
|
|
18893c005e | ||
|
|
73b51a0687 | ||
|
|
c4de003fc1 | ||
|
|
45b8ad099e | ||
|
|
8c40ff4852 | ||
|
|
f304c866f1 | ||
|
|
9adf42c308 | ||
|
|
2391db0f53 | ||
|
|
9809e4dfd0 | ||
|
|
b191efea2d | ||
|
|
c5ec801b90 | ||
|
|
95f22bc2a1 | ||
|
|
0098f25bfb | ||
|
|
9395e0835e | ||
|
|
d59d6b7022 | ||
|
|
c21e177ed1 | ||
|
|
d3e1419d0e | ||
|
|
f44d09059b | ||
|
|
185f6d84fb | ||
|
|
3dc6be5d8d | ||
|
|
742e2394a9 | ||
|
|
d9d389b939 | ||
|
|
b0775e13d5 | ||
|
|
0fe98b6c5e | ||
|
|
3a78021f5e | ||
|
|
e096009893 | ||
|
|
b599ee2096 | ||
|
|
bd7cedee48 | ||
|
|
278c07ea56 | ||
|
|
6dc2875057 | ||
|
|
7b24071956 | ||
|
|
6c472aa71d | ||
|
|
ca66490930 | ||
|
|
5317a482d1 | ||
|
|
f21d1d83f6 | ||
|
|
bb6c805f55 | ||
|
|
c5dfe3e842 | ||
|
|
4290a4e820 | ||
|
|
1f6b450df3 | ||
|
|
d9f05ffdf8 | ||
|
|
ddc693e909 | ||
|
|
a29de19d4c | ||
|
|
c6d50e8450 | ||
|
|
f0aeece867 | ||
|
|
015443d9db | ||
|
|
fde42c5bae | ||
|
|
b4ae564102 | ||
|
|
aabbe110b9 | ||
|
|
35b86c7d78 | ||
|
|
e101af72d4 | ||
|
|
59509a307e | ||
|
|
ccf0218b6c | ||
|
|
d5597fb002 | ||
|
|
0515a500c6 | ||
|
|
636aac1a28 | ||
|
|
8ff6c159fa | ||
|
|
a84766aad9 | ||
|
|
a120be4a79 | ||
|
|
8cba2c0f8f | ||
|
|
579297efa3 | ||
|
|
9094a69be8 | ||
|
|
20c77c1769 | ||
|
|
fcc450efb4 | ||
|
|
f02e8b13ac | ||
|
|
4c4f7adbce | ||
|
|
6662be893c | ||
|
|
41cf8e3fa9 | ||
|
|
aec1602a39 | ||
|
|
e00281819c | ||
|
|
48cf778d56 | ||
|
|
e6506bd4b4 | ||
|
|
3a29b6db64 | ||
|
|
cecee8f70e | ||
|
|
e5cad5f022 | ||
|
|
85e584f65a | ||
|
|
78fe1b67ea | ||
|
|
9c33d67d80 | ||
|
|
16d234e761 | ||
|
|
635f65e5a1 | ||
|
|
3ef5d2a29d | ||
|
|
5797e15b06 | ||
|
|
c987357959 | ||
|
|
e452969cdd | ||
|
|
7484f52bc4 | ||
|
|
938dd030b0 | ||
|
|
990806118f | ||
|
|
6ae1e1d0aa | ||
|
|
ed6a0e5403 | ||
|
|
d48a8e93a2 | ||
|
|
51f0a011a7 | ||
|
|
0a6a9e9f4b | ||
|
|
8669a22689 | ||
|
|
1e44644c73 | ||
|
|
5c31dd409f | ||
|
|
354a1c81ab | ||
|
|
76fb0b3797 | ||
|
|
06ea9cb23f | ||
|
|
efd7c4c8eb | ||
|
|
ee80ef909f | ||
|
|
0e76fbfc1b | ||
|
|
14b520395e | ||
|
|
0f204afa2a | ||
|
|
d71290f6f1 | ||
|
|
114e78171f | ||
|
|
0cfead3914 | ||
|
|
4a20de0410 | ||
|
|
f69bd0e636 | ||
|
|
eed04277d1 | ||
|
|
df05aecdcf | ||
|
|
680371ab30 | ||
|
|
a465f89354 | ||
|
|
4c633847b2 | ||
|
|
75f03dbf28 | ||
|
|
9c2934d617 | ||
|
|
5e94f464fc | ||
|
|
a3f5c11050 | ||
|
|
09e837d2e0 | ||
|
|
fcb80dc71f | ||
|
|
a63446e0e2 | ||
|
|
d3873e6568 | ||
|
|
8105cc0ac6 | ||
|
|
433ea02c04 | ||
|
|
c97feb1cd8 | ||
|
|
aa365d684a | ||
|
|
285aac5679 | ||
|
|
c53222ce69 | ||
|
|
21ae984acb | ||
|
|
d1b1e5a6cd | ||
|
|
0e465eb7f8 | ||
|
|
5b2d834a3e | ||
|
|
0aabe37152 | ||
|
|
89bb26b09c | ||
|
|
4e85cc4cbb | ||
|
|
771f237990 | ||
|
|
4d17ff4fb4 | ||
|
|
7a0ae2d56a | ||
|
|
71a08ca5eb | ||
|
|
4ba5b324a6 | ||
|
|
05ee34c756 | ||
|
|
73adc6f98c | ||
|
|
eb97257c41 |
73
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
Normal file
73
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
name: 🐞 Bug report
|
||||||
|
description: Report a very clearly broken issue.
|
||||||
|
title: 'bug: <title>'
|
||||||
|
labels: [bug]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# ReVanced bug report
|
||||||
|
|
||||||
|
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-patches/labels/bug).
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: Type
|
||||||
|
options:
|
||||||
|
- Error while patching
|
||||||
|
- Error at runtime
|
||||||
|
- Cosmetic
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Bug description
|
||||||
|
description: How did you find the bug? Any additional details that might help?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Add the steps to reproduce this bug including your environment.
|
||||||
|
placeholder: Step 1. Download some files. Step 2. ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Relevant log output
|
||||||
|
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Screenshots or videos
|
||||||
|
description: Add screenshots or videos that show the bug here.
|
||||||
|
placeholder: Drag and drop the screenshots/videos into this box.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Solution
|
||||||
|
description: If applicable, add a possible solution.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add additional context here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: checkboxes
|
||||||
|
id: acknowledgements
|
||||||
|
attributes:
|
||||||
|
label: Acknowledgements
|
||||||
|
description: Your issue will be closed if you haven't done these steps.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
||||||
|
required: true
|
||||||
|
- label: I have written a short but informative title.
|
||||||
|
required: true
|
||||||
|
- label: I filled out all of the requested information in this issue properly.
|
||||||
|
required: true
|
||||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: 📃 Documentation
|
||||||
|
url: https://github.com/revanced/revanced-documentation/
|
||||||
|
about: Don't know how or where to start? Check out our documentation!
|
||||||
|
- name: 🗨 Discussions
|
||||||
|
url: https://github.com/revanced/revanced-suggestions/discussions
|
||||||
|
about: Got something you think should change or be added? Search for or start a new discussion!
|
||||||
41
.github/ISSUE_TEMPLATE/patch-request.yml
vendored
Normal file
41
.github/ISSUE_TEMPLATE/patch-request.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: ⭐ Patch Request
|
||||||
|
description: Create a detailed patch request.
|
||||||
|
title: 'feat(patch): '
|
||||||
|
labels: [patch-request]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Application
|
||||||
|
description: Which application is this patch for?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Issue
|
||||||
|
description: What is the issue this patch should solve?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Patch
|
||||||
|
description: "Describe the patch you'd like to see in detail."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Motivation
|
||||||
|
description: Why should your patch request should be considered? What makes it valuable to the community?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: acknowledgements
|
||||||
|
attributes:
|
||||||
|
label: Acknowledgements
|
||||||
|
description: Your issue will be closed if you haven't done these steps.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
||||||
|
required: true
|
||||||
|
- label: I have written a short but informative title.
|
||||||
|
required: true
|
||||||
|
- label: I filled out all of the requested information in this issue properly.
|
||||||
|
required: true
|
||||||
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@@ -12,7 +12,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -24,16 +24,14 @@ jobs:
|
|||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
cache: gradle
|
cache: gradle
|
||||||
- name: Setup Node.js
|
- name: Setup Android SDK
|
||||||
uses: actions/setup-node@v2
|
uses: android-actions/setup-android@v2
|
||||||
with:
|
|
||||||
node-version: "lts/*"
|
|
||||||
- name: Make gradlew executable
|
|
||||||
run: chmod +x gradlew
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew build clean
|
run: ./gradlew build clean generateMeta
|
||||||
|
- name: Install Android build-tools
|
||||||
|
run: sdkmanager "build-tools;32.0.0"
|
||||||
- name: Setup semantic-release
|
- name: Setup semantic-release
|
||||||
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
||||||
- name: Release
|
- name: Release
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -114,4 +114,7 @@ gradle-app.setting
|
|||||||
# gradle/wrapper/gradle-wrapper.properties
|
# gradle/wrapper/gradle-wrapper.properties
|
||||||
|
|
||||||
# Potentially copyrighted test APK
|
# Potentially copyrighted test APK
|
||||||
*.apk
|
*.apk
|
||||||
|
|
||||||
|
# Ignore vscode config
|
||||||
|
.vscode/
|
||||||
|
|||||||
1
.idea/.gitignore
generated
vendored
1
.idea/.gitignore
generated
vendored
@@ -6,3 +6,4 @@
|
|||||||
/dataSources.local.xml
|
/dataSources.local.xml
|
||||||
# Editor-based HTTP Client requests
|
# Editor-based HTTP Client requests
|
||||||
/httpRequests/
|
/httpRequests/
|
||||||
|
/kotlinc.xml
|
||||||
|
|||||||
13
.releaserc
13
.releaserc
@@ -7,7 +7,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@semantic-release/commit-analyzer",
|
["@semantic-release/commit-analyzer", {
|
||||||
|
"releaseRules": [
|
||||||
|
{"type": "build", "release": "patch"}
|
||||||
|
]
|
||||||
|
}],
|
||||||
"@semantic-release/release-notes-generator",
|
"@semantic-release/release-notes-generator",
|
||||||
"@semantic-release/changelog",
|
"@semantic-release/changelog",
|
||||||
"gradle-semantic-release-plugin",
|
"gradle-semantic-release-plugin",
|
||||||
@@ -15,8 +19,10 @@
|
|||||||
"@semantic-release/git",
|
"@semantic-release/git",
|
||||||
{
|
{
|
||||||
"assets": [
|
"assets": [
|
||||||
|
"README.md",
|
||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"gradle.properties"
|
"gradle.properties",
|
||||||
|
"patches.json"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -26,6 +32,9 @@
|
|||||||
"assets": [
|
"assets": [
|
||||||
{
|
{
|
||||||
"path": "build/libs/*.jar"
|
"path": "build/libs/*.jar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "patches.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
1668
CHANGELOG.md
1668
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
124
README-template.md
Normal file
124
README-template.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
## 🧩 Patches
|
||||||
|
|
||||||
|
The official Patch bundle provided by ReVanced and the community.
|
||||||
|
|
||||||
|
> Looking for the JSON variant of this? [Click here](patches.json).
|
||||||
|
|
||||||
|
{{ table }}
|
||||||
|
|
||||||
|
## 📝 JSON Format
|
||||||
|
|
||||||
|
This section explains the JSON format for the [patches.json](patches.json) file.
|
||||||
|
|
||||||
|
The file contains an array of objects, each object representing a patch. The object contains the following properties:
|
||||||
|
|
||||||
|
| key | description |
|
||||||
|
|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `name` | The name of the patch. |
|
||||||
|
| `description` | The description of the patch. |
|
||||||
|
| `version` | The version of the patch. |
|
||||||
|
| `excluded` | Whether the patch is excluded by default. If `true`, the patch must never be included by default. |
|
||||||
|
| `deprecated` | Whether the patch is deprecated. |
|
||||||
|
| `options` | An array of options for this patch. |
|
||||||
|
| `options.key` | The key of the option. |
|
||||||
|
| `options.title` | The title of the option. |
|
||||||
|
| `options.description` | The description of the option. |
|
||||||
|
| `options.required` | Whether the option is required. |
|
||||||
|
| `options.choices?` | An array of choices of the option. This may be `null` if this option has no choices. The element type of this array may be any type. It can be a `String`, `Int` or something else. |
|
||||||
|
| `dependencies` | An array of dependencies, which are patch names. |
|
||||||
|
| `compatiblePackages` | An array of packages compatible with this patch. |
|
||||||
|
| `compatiblePackages.name` | The name of the package. |
|
||||||
|
| `compatiblePackages.versions` | An array of versions of the package compatible with this patch. If empty, all versions are seemingly compatible. |
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "remember-video-quality",
|
||||||
|
"description": "Adds the ability to remember the video quality you chose in the video quality flyout.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"excluded": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"options": [],
|
||||||
|
"dependencies": [
|
||||||
|
"integrations",
|
||||||
|
"video-id-hook"
|
||||||
|
],
|
||||||
|
"compatiblePackages": [
|
||||||
|
{
|
||||||
|
"name": "com.google.android.youtube",
|
||||||
|
"versions": [
|
||||||
|
"17.22.36",
|
||||||
|
"17.24.35",
|
||||||
|
"17.26.35",
|
||||||
|
"17.27.39",
|
||||||
|
"17.28.34",
|
||||||
|
"17.29.34",
|
||||||
|
"17.32.35",
|
||||||
|
"17.33.42"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "theme",
|
||||||
|
"description": "Enables a custom theme.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"excluded": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"key": "theme",
|
||||||
|
"title": "Theme",
|
||||||
|
"description": "Select a theme.",
|
||||||
|
"required": true,
|
||||||
|
"choices": [
|
||||||
|
"Amoled"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"locale-config-fix"
|
||||||
|
],
|
||||||
|
"compatiblePackages": [
|
||||||
|
{
|
||||||
|
"name": "com.google.android.youtube",
|
||||||
|
"versions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "custom-branding",
|
||||||
|
"description": "Changes the YouTube launcher icon and name to your choice (defaults to ReVanced).",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"excluded": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"key": "appName",
|
||||||
|
"title": "Application Name",
|
||||||
|
"description": "The name of the application it will show on your home screen.",
|
||||||
|
"required": true,
|
||||||
|
"choices": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "appIconPath",
|
||||||
|
"title": "Application Icon Path",
|
||||||
|
"description": "A path to the icon of the application.",
|
||||||
|
"required": false,
|
||||||
|
"choices": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"locale-config-fix"
|
||||||
|
],
|
||||||
|
"compatiblePackages": [
|
||||||
|
{
|
||||||
|
"name": "com.google.android.youtube",
|
||||||
|
"versions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
266
README.md
266
README.md
@@ -1,2 +1,264 @@
|
|||||||
# revanced-patches
|
## 🧩 Patches
|
||||||
Repo for all ReVanced patches
|
|
||||||
|
The official Patch bundle provided by ReVanced and the community.
|
||||||
|
|
||||||
|
> Looking for the JSON variant of this? [Click here](patches.json).
|
||||||
|
|
||||||
|
### 📦 `com.reddit.frontpage`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `general-reddit-ads` | Removes general ads from the Reddit frontpage and subreddits. | all |
|
||||||
|
| `premium-icon-reddit` | Unlocking Premium Icons in reddit app. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `com.spotify.music`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `hide-premium-navbar` | Removes the premium tab from the navbar. | all |
|
||||||
|
| `disable-capture-restriction` | Allows capturing Spotify's audio output while screen sharing or screen recording. | all |
|
||||||
|
| `spotify-theme` | Applies a custom theme. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `de.dwd.warnapp`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `promo-code-unlock` | Disables the validation of promo code. Any code will work to unlock all features. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `com.garzotto.pflotsh.ecmwf_a`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `pflotsh-ecmwf-subscription-unlock` | Unlocks all subscription features. | 3.5.4 |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `com.ss.android.ugc.trill`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `tiktok-speed` | Enables the playback speed option for all videos. | all |
|
||||||
|
| `tiktok-download` | Removes download restrictions and changes the default path to download to. | all |
|
||||||
|
| `tiktok-seekbar` | Show progress bar for all video. | all |
|
||||||
|
| `tiktok-force-login` | Do not force login. | all |
|
||||||
|
| `tiktok-settings` | Add settings menu to TikTok. | all |
|
||||||
|
| `tiktok-ads` | Removes ads from TikTok. | all |
|
||||||
|
| `tiktok-feed-filter` | Filters tiktok videos: removing ads, removing livestreams. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `com.zhiliaoapp.musically`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `tiktok-speed` | Enables the playback speed option for all videos. | all |
|
||||||
|
| `tiktok-download` | Removes download restrictions and changes the default path to download to. | all |
|
||||||
|
| `tiktok-seekbar` | Show progress bar for all video. | all |
|
||||||
|
| `tiktok-force-login` | Do not force login. | all |
|
||||||
|
| `tiktok-settings` | Add settings menu to TikTok. | all |
|
||||||
|
| `tiktok-ads` | Removes ads from TikTok. | all |
|
||||||
|
| `tiktok-feed-filter` | Filters tiktok videos: removing ads, removing livestreams. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `com.twitter.android`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `monochrome-icon` | Adds a monochrome icon. | all |
|
||||||
|
| `dynamic-color` | Replaces the default Twitter Blue with the users Material You palette. | all |
|
||||||
|
| `timeline-ads` | Removes ads from the Twitter timeline. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `com.google.android.apps.youtube.music`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `exclusive-audio-playback` | Enables the option to play music without video. | 5.25.51 |
|
||||||
|
| `codecs-unlock` | Adds more audio codec options. The new audio codecs usually result in better audio quality. | 5.25.51 |
|
||||||
|
| `music-microg-support` | Allows YouTube Music ReVanced to run without root and under a different package name. | 5.25.51 |
|
||||||
|
| `music-video-ads` | Removes ads in the music player. | 5.25.51 |
|
||||||
|
| `tasteBuilder-remover` | Removes the "Tell us which artists you like" card from the home screen. | 5.25.51 |
|
||||||
|
| `minimized-playback-music` | Enables minimized playback on Kids music. | 5.25.51 |
|
||||||
|
| `compact-header` | Hides the music category bar at the top of the homepage. | 5.25.51 |
|
||||||
|
| `upgrade-button-remover` | Removes the upgrade tab from the pivot bar. | 5.25.51 |
|
||||||
|
| `hide-get-premium` | Removes all "Get Premium" evidences from the avatar menu. | 5.25.51 |
|
||||||
|
| `background-play` | Enables playing music in the background. | 5.25.51 |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `com.google.android.youtube`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `swipe-controls` | Adds volume and brightness swipe controls. | 17.36.37 |
|
||||||
|
| `downloads` | Enables downloading music and videos from YouTube. | 17.36.37 |
|
||||||
|
| `seekbar-tapping` | Enables tap-to-seek on the seekbar of the video player. | 17.36.37 |
|
||||||
|
| `remember-video-quality` | Adds the ability to remember the video quality you chose in the video quality flyout. | 17.36.37 |
|
||||||
|
| `enable-debugging` | Enables app debugging by patching the manifest file. | all |
|
||||||
|
| `custom-playback-speed` | Adds more video playback speed options. | 17.36.37 |
|
||||||
|
| `minimized-playback` | Enables minimized and background playback. | 17.36.37 |
|
||||||
|
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
|
||||||
|
| `custom-video-buffer` | Lets you change the buffers of videos. | 17.36.37 |
|
||||||
|
| `settings` | Adds settings for ReVanced to YouTube. | all |
|
||||||
|
| `microg-support` | Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG. | 17.36.37 |
|
||||||
|
| `hdr-auto-brightness` | Makes the brightness of HDR videos follow the system default. | 17.36.37 |
|
||||||
|
| `always-autorepeat` | Always repeats the playing video again. | 17.36.37 |
|
||||||
|
| `general-ads` | Removes general ads. | 17.36.37 |
|
||||||
|
| `hide-infocard-suggestions` | Hides infocards in videos. | 17.36.37 |
|
||||||
|
| `video-ads` | Removes ads in the video player. | 17.36.37 |
|
||||||
|
| `hide-time-and-seekbar` | Hides progress bar and time counter on videos. | 17.36.37 |
|
||||||
|
| `old-quality-layout` | Enables the original quality flyout menu. | 17.36.37 |
|
||||||
|
| `enable-wide-searchbar` | Replaces the search icon with a wide search bar. This will hide the YouTube logo when active. | 17.36.37 |
|
||||||
|
| `disable-fullscreen-panels` | Disables video description and comments panel in fullscreen view. | 17.36.37 |
|
||||||
|
| `hide-autoplay-button` | Hides the autoplay button in the video player. | 17.36.37 |
|
||||||
|
| `disable-startup-shorts-player` | Disables playing YouTube Shorts when launching YouTube. | 17.36.37 |
|
||||||
|
| `premium-heading` | Shows premium branding on the home screen. | all |
|
||||||
|
| `custom-branding` | Changes the YouTube launcher icon and name to your choice (defaults to ReVanced). | all |
|
||||||
|
| `hide-create-button` | Hides the create button in the navigation bar. | 17.36.37 |
|
||||||
|
| `hide-shorts-button` | Hides the shorts button on the navigation bar. | 17.36.37 |
|
||||||
|
| `theme` | Applies a custom theme. | all |
|
||||||
|
| `hide-email-address` | Hides the email address in the account switcher. | 17.36.37 |
|
||||||
|
| `sponsorblock` | Integrate SponsorBlock. | 17.36.37 |
|
||||||
|
| `hide-cast-button` | Hides the cast button in the video player. | all |
|
||||||
|
| `tablet-mini-player` | Enables the tablet mini player layout. | 17.36.37 |
|
||||||
|
| `return-youtube-dislike` | Shows the dislike count of videos using the Return YouTube Dislike API. | 17.36.37 |
|
||||||
|
| `hide-watermark` | Hides creator's watermarks on videos. | 17.36.37 |
|
||||||
|
| `disable-auto-player-popup-panels` | Disable automatic popup panels (playlist or live chat) on video player. | 17.36.37 |
|
||||||
|
| `disable-auto-captions` | Disable forced captions from being automatically enabled. | 17.36.37 |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### 📦 `com.vanced.android.youtube`
|
||||||
|
<details>
|
||||||
|
|
||||||
|
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||||
|
|:--------:|:--------------:|:-----------------:|
|
||||||
|
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 📝 JSON Format
|
||||||
|
|
||||||
|
This section explains the JSON format for the [patches.json](patches.json) file.
|
||||||
|
|
||||||
|
The file contains an array of objects, each object representing a patch. The object contains the following properties:
|
||||||
|
|
||||||
|
| key | description |
|
||||||
|
|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `name` | The name of the patch. |
|
||||||
|
| `description` | The description of the patch. |
|
||||||
|
| `version` | The version of the patch. |
|
||||||
|
| `excluded` | Whether the patch is excluded by default. If `true`, the patch must never be included by default. |
|
||||||
|
| `deprecated` | Whether the patch is deprecated. |
|
||||||
|
| `options` | An array of options for this patch. |
|
||||||
|
| `options.key` | The key of the option. |
|
||||||
|
| `options.title` | The title of the option. |
|
||||||
|
| `options.description` | The description of the option. |
|
||||||
|
| `options.required` | Whether the option is required. |
|
||||||
|
| `options.choices?` | An array of choices of the option. This may be `null` if this option has no choices. The element type of this array may be any type. It can be a `String`, `Int` or something else. |
|
||||||
|
| `dependencies` | An array of dependencies, which are patch names. |
|
||||||
|
| `compatiblePackages` | An array of packages compatible with this patch. |
|
||||||
|
| `compatiblePackages.name` | The name of the package. |
|
||||||
|
| `compatiblePackages.versions` | An array of versions of the package compatible with this patch. If empty, all versions are seemingly compatible. |
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "remember-video-quality",
|
||||||
|
"description": "Adds the ability to remember the video quality you chose in the video quality flyout.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"excluded": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"options": [],
|
||||||
|
"dependencies": [
|
||||||
|
"integrations",
|
||||||
|
"video-id-hook"
|
||||||
|
],
|
||||||
|
"compatiblePackages": [
|
||||||
|
{
|
||||||
|
"name": "com.google.android.youtube",
|
||||||
|
"versions": [
|
||||||
|
"17.22.36",
|
||||||
|
"17.24.35",
|
||||||
|
"17.26.35",
|
||||||
|
"17.27.39",
|
||||||
|
"17.28.34",
|
||||||
|
"17.29.34",
|
||||||
|
"17.32.35",
|
||||||
|
"17.33.42"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "theme",
|
||||||
|
"description": "Enables a custom theme.",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"excluded": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"key": "theme",
|
||||||
|
"title": "Theme",
|
||||||
|
"description": "Select a theme.",
|
||||||
|
"required": true,
|
||||||
|
"choices": [
|
||||||
|
"Amoled"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"locale-config-fix"
|
||||||
|
],
|
||||||
|
"compatiblePackages": [
|
||||||
|
{
|
||||||
|
"name": "com.google.android.youtube",
|
||||||
|
"versions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "custom-branding",
|
||||||
|
"description": "Changes the YouTube launcher icon and name to your choice (defaults to ReVanced).",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"excluded": false,
|
||||||
|
"deprecated": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"key": "appName",
|
||||||
|
"title": "Application Name",
|
||||||
|
"description": "The name of the application it will show on your home screen.",
|
||||||
|
"required": true,
|
||||||
|
"choices": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "appIconPath",
|
||||||
|
"title": "Application Icon Path",
|
||||||
|
"description": "A path to the icon of the application.",
|
||||||
|
"required": false,
|
||||||
|
"choices": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"locale-config-fix"
|
||||||
|
],
|
||||||
|
"compatiblePackages": [
|
||||||
|
{
|
||||||
|
"name": "com.google.android.youtube",
|
||||||
|
"versions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
@@ -1,59 +1,66 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.6.21"
|
kotlin("jvm") version "1.7.0"
|
||||||
java
|
|
||||||
`maven-publish`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "app.revanced"
|
group = "app.revanced"
|
||||||
|
|
||||||
|
val githubUsername: String = project.findProperty("gpr.user") as? String ?: System.getenv("GITHUB_ACTOR")
|
||||||
|
val githubPassword: String = project.findProperty("gpr.key") as? String ?: System.getenv("GITHUB_TOKEN")
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
maven {
|
maven {
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher") // note the "r"!
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
||||||
credentials {
|
credentials {
|
||||||
// DO NOT set these variables in the project's gradle.properties.
|
username = githubUsername
|
||||||
// Instead, you should set them in:
|
password = githubPassword
|
||||||
// Windows: %homepath%\.gradle\gradle.properties
|
|
||||||
// Linux: ~/.gradle/gradle.properties
|
|
||||||
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") // DO NOT CHANGE!
|
|
||||||
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") // DO NOT CHANGE!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21")
|
implementation("app.revanced:revanced-patcher:5.1.2")
|
||||||
|
implementation("app.revanced:multidexlib2:2.5.2.r2")
|
||||||
implementation("app.revanced:revanced-patcher:1.0.0-dev.17")
|
// Required for meta
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.21")
|
implementation("com.google.code.gson:gson:2.9.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
tasks {
|
||||||
withSourcesJar()
|
register<DefaultTask>("generateBundle") {
|
||||||
withJavadocJar()
|
description = "Generate dex files from build and bundle them in the jar file"
|
||||||
}
|
dependsOn(build)
|
||||||
|
|
||||||
val isGitHubCI = System.getenv("GITHUB_ACTOR") != null
|
doLast {
|
||||||
|
val androidHome = System.getenv("ANDROID_HOME") ?: throw GradleException("ANDROID_HOME not found")
|
||||||
|
val d8 = "${androidHome}/build-tools/32.0.0/d8"
|
||||||
|
val input = configurations.archives.get().allArtifacts.files.files.first().absolutePath
|
||||||
|
val work = File("${buildDir}/libs")
|
||||||
|
|
||||||
publishing {
|
exec {
|
||||||
repositories {
|
workingDir = work
|
||||||
if (isGitHubCI) {
|
commandLine = listOf(d8, input)
|
||||||
maven {
|
}
|
||||||
name = "GitHubPackages"
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patches") // note the "s"!
|
exec {
|
||||||
credentials {
|
workingDir = work
|
||||||
username = System.getenv("GITHUB_ACTOR")
|
commandLine = listOf("zip", "-u", input, "classes.dex")
|
||||||
password = System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
mavenLocal()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
publications {
|
register<JavaExec>("generateMeta") {
|
||||||
register<MavenPublication>("gpr") {
|
description = "Generate metadata for this bundle"
|
||||||
from(components["java"])
|
dependsOn(build)
|
||||||
}
|
|
||||||
|
classpath = sourceSets["main"].runtimeClasspath
|
||||||
|
mainClass.set("app.revanced.meta.Meta")
|
||||||
|
}
|
||||||
|
// Dummy task to fix the Gradle semantic-release plugin.
|
||||||
|
// Remove this if you forked it to support building only.
|
||||||
|
// Tracking issue: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
|
||||||
|
register<DefaultTask>("publish") {
|
||||||
|
group = "publish"
|
||||||
|
description = "Dummy task"
|
||||||
|
dependsOn(named("generateBundle"), named("generateMeta"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 1.0.0-dev.13
|
version = 2.75.2
|
||||||
|
|||||||
1
patches.json
Normal file
1
patches.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,36 +1,212 @@
|
|||||||
package app.revanced.extensions
|
package app.revanced.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.PatchResultError
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patcher.util.smali.toInstruction
|
import app.revanced.patcher.util.smali.toInstruction
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction11n
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction11x
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction35c
|
||||||
|
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
// TODO: this method does not make sense here
|
||||||
internal fun MutableMethodImplementation.injectHideCall(
|
internal fun MutableMethodImplementation.injectHideCall(
|
||||||
index: Int,
|
index: Int,
|
||||||
register: Int
|
register: Int
|
||||||
) {
|
) {
|
||||||
this.addInstruction(
|
this.addInstruction(
|
||||||
index,
|
index,
|
||||||
"invoke-static { v$register }, Lfi/razerman/youtube/XAdRemover;->HideView(Landroid/view/View;)V".toInstruction()
|
"invoke-static { v$register }, Lapp/revanced/integrations/patches/HideHomeAdsPatch;->HideHomeAds(Landroid/view/View;)V".toInstruction()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* traverse the class hierarchy starting from the given root class
|
||||||
|
*
|
||||||
|
* @param targetClass the class to start traversing the class hierarchy from
|
||||||
|
* @param callback function that is called for every class in the hierarchy
|
||||||
|
*/
|
||||||
|
fun BytecodeData.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
|
||||||
|
callback(targetClass)
|
||||||
|
this.findClass(targetClass.superclass ?: return)?.resolve()?.let {
|
||||||
|
traverseClassHierarchy(it, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* apply a transform to all methods of the class
|
||||||
|
*
|
||||||
|
* @param transform the transformation function. original method goes in, transformed method goes out
|
||||||
|
*/
|
||||||
|
fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod) {
|
||||||
|
val transformedMethods = methods.map { it.transform() }
|
||||||
|
methods.clear()
|
||||||
|
methods.addAll(transformedMethods)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an event hook at the top of the method. If the hook returns true, the event is consumed and the method will return with true
|
||||||
|
*
|
||||||
|
* the hook method MUST return a boolean and MUST take two parameters, like so:
|
||||||
|
* fun hook(thisRef: Object, eventData: Object): Boolean {}
|
||||||
|
*
|
||||||
|
* The final injected code will resemble the following logic:
|
||||||
|
* if( YouHook(this, event) ) { return true; }
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* @param hookRef reference to the hook method
|
||||||
|
*/
|
||||||
|
internal fun MutableMethod.injectConsumableEventHook(hookRef: ImmutableMethodReference) {
|
||||||
|
val isStaticMethod = AccessFlags.STATIC.isSet(this.accessFlags)
|
||||||
|
this.implementation?.let { impl ->
|
||||||
|
// create label to index 0 to continue to the normal program flow
|
||||||
|
val lblContinueNormalFlow = impl.newLabelForIndex(0)
|
||||||
|
|
||||||
|
// define registers
|
||||||
|
/** V0 */
|
||||||
|
val regV0 = 0
|
||||||
|
|
||||||
|
/** this */
|
||||||
|
val regP0 = impl.registerCount - this.parameters.size - (if (isStaticMethod) 0 else 1)
|
||||||
|
|
||||||
|
/** motionEvent */
|
||||||
|
val regP1 = regP0 + 1
|
||||||
|
|
||||||
|
// insert instructions at the start of the method:
|
||||||
|
// if( Hook(this, event) ) { return true; }
|
||||||
|
impl.addInstructions(
|
||||||
|
0, listOf(
|
||||||
|
// invoke-static { p0, p1 } <hook>
|
||||||
|
BuilderInstruction35c(
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
2,
|
||||||
|
regP0,
|
||||||
|
regP1,
|
||||||
|
0, 0, 0,
|
||||||
|
hookRef
|
||||||
|
),
|
||||||
|
|
||||||
|
// move-result v0
|
||||||
|
BuilderInstruction11x(
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
regV0
|
||||||
|
),
|
||||||
|
|
||||||
|
// if-eqz v0, :continue_normal_flow
|
||||||
|
BuilderInstruction21t(
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
regV0,
|
||||||
|
lblContinueNormalFlow
|
||||||
|
),
|
||||||
|
|
||||||
|
// const/4 v0, 0x1
|
||||||
|
BuilderInstruction11n(
|
||||||
|
Opcode.CONST_4,
|
||||||
|
regV0,
|
||||||
|
0x1
|
||||||
|
),
|
||||||
|
|
||||||
|
// return v0
|
||||||
|
BuilderInstruction11x(
|
||||||
|
Opcode.RETURN,
|
||||||
|
regV0
|
||||||
|
)
|
||||||
|
|
||||||
|
// :continue_normal_flow
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inject resources into the patched app
|
||||||
|
*
|
||||||
|
* @param classLoader classloader to use for loading the resources
|
||||||
|
* @param patchDirectoryPath path to the files. this will be the directory you created under the 'resources' source folder
|
||||||
|
* @param resourceType the resource type, for example 'drawable'. this has to match both the source and the target
|
||||||
|
* @param resourceFileNames names of all resources of this type to inject
|
||||||
|
*/
|
||||||
|
fun ResourceData.injectResources(
|
||||||
|
classLoader: ClassLoader,
|
||||||
|
patchDirectoryPath: String,
|
||||||
|
resourceType: String,
|
||||||
|
resourceFileNames: List<String>
|
||||||
|
) {
|
||||||
|
resourceFileNames.forEach { name ->
|
||||||
|
val relativePath = "$resourceType/$name"
|
||||||
|
val sourceRes = classLoader.getResourceAsStream("$patchDirectoryPath/$relativePath")
|
||||||
|
?: throw PatchResultError("could not open resource '$patchDirectoryPath/$relativePath'")
|
||||||
|
|
||||||
|
Files.copy(
|
||||||
|
sourceRes,
|
||||||
|
this["res"].resolve(relativePath).toPath()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inject strings into the patched app
|
||||||
|
*
|
||||||
|
* @param classLoader classloader to use for loading the resources
|
||||||
|
* @param patchDirectoryPath path to the files. this will be the directory you created under the 'resources' source folder
|
||||||
|
* @param languageIdentifier ISO 639-2 two- letter language code identifier (aka the one android uses for values directory)
|
||||||
|
*/
|
||||||
|
fun ResourceData.injectStrings(
|
||||||
|
classLoader: ClassLoader,
|
||||||
|
patchDirectoryPath: String,
|
||||||
|
languageIdentifier: String? = null,
|
||||||
|
) {
|
||||||
|
val relativePath =
|
||||||
|
if (languageIdentifier.isNullOrBlank()) "values/strings.xml" else "values/strings-$languageIdentifier.xml"
|
||||||
|
|
||||||
|
// open source strings.xml
|
||||||
|
val sourceInputStream = classLoader.getResourceAsStream("$patchDirectoryPath/$relativePath")
|
||||||
|
?: throw PatchResultError("failed to open '$patchDirectoryPath/$relativePath'")
|
||||||
|
xmlEditor[sourceInputStream].use { sourceStringsXml ->
|
||||||
|
val strings = sourceStringsXml.file.getElementsByTagName("resources").item(0).childNodes
|
||||||
|
|
||||||
|
// open target strings.xml
|
||||||
|
xmlEditor["res/$relativePath"].use { targetStringsXml ->
|
||||||
|
val targetFile = targetStringsXml.file
|
||||||
|
val targetRootNode = targetFile.getElementsByTagName("resources").item(0)
|
||||||
|
|
||||||
|
// process all children strings in the source
|
||||||
|
for (i in 0 until strings.length) {
|
||||||
|
// clone the node from source to target
|
||||||
|
val node = strings.item(i).cloneNode(true)
|
||||||
|
targetFile.adoptNode(node)
|
||||||
|
targetRootNode.appendChild(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun Node.doRecursively(action: (Node) -> Unit) {
|
internal fun Node.doRecursively(action: (Node) -> Unit) {
|
||||||
action(this)
|
action(this)
|
||||||
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
|
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun String.startsWithAny(vararg prefix: String): Boolean {
|
internal fun String.startsWithAny(vararg prefixes: String): Boolean {
|
||||||
for (_prefix in prefix)
|
for (prefix in prefixes)
|
||||||
if (this.startsWith(_prefix))
|
if (this.startsWith(prefix))
|
||||||
return true
|
return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun String.containsAny(vararg others: String): Boolean {
|
internal fun String.equalsAny(vararg other: String): Boolean {
|
||||||
for (other in others)
|
for (_other in other)
|
||||||
if (this.contains(other))
|
if (this == _other)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/main/kotlin/app/revanced/meta/Meta.kt
Normal file
27
src/main/kotlin/app/revanced/meta/Meta.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package app.revanced.meta
|
||||||
|
|
||||||
|
import app.revanced.meta.json.generateJson
|
||||||
|
import app.revanced.meta.readme.generateText
|
||||||
|
import app.revanced.patcher.data.Data
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.util.patch.impl.JarPatchBundle
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
typealias Bundle = List<Class<out Patch<Data>>>
|
||||||
|
|
||||||
|
object Meta {
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val patches = accumulatePatches()
|
||||||
|
if (patches.isEmpty()) throw IllegalStateException("No patches found")
|
||||||
|
|
||||||
|
generateText(patches)
|
||||||
|
generateJson(patches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun accumulatePatches() = JarPatchBundle(
|
||||||
|
File("build/libs/").listFiles()!!.first {
|
||||||
|
it.name.startsWith("revanced-patches-") && it.name.endsWith(".jar")
|
||||||
|
}.absolutePath
|
||||||
|
).loadPatches()
|
||||||
50
src/main/kotlin/app/revanced/meta/json/Generator.kt
Normal file
50
src/main/kotlin/app/revanced/meta/json/Generator.kt
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package app.revanced.meta.json
|
||||||
|
|
||||||
|
import app.revanced.meta.Bundle
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.deprecated
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.description
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.include
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.options
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.version
|
||||||
|
import app.revanced.patcher.patch.PatchOption
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
private val gson = GsonBuilder().serializeNulls().create()
|
||||||
|
|
||||||
|
fun generateJson(bundle: Bundle) {
|
||||||
|
val patches = bundle.map {
|
||||||
|
JsonPatch(
|
||||||
|
it.patchName,
|
||||||
|
it.description ?: "This patch has no description.",
|
||||||
|
it.version ?: "0.0.0",
|
||||||
|
!it.include,
|
||||||
|
it.deprecated != null,
|
||||||
|
it.options?.map { option ->
|
||||||
|
Option(
|
||||||
|
option.key,
|
||||||
|
option.title,
|
||||||
|
option.description,
|
||||||
|
option.required,
|
||||||
|
option.let { lo ->
|
||||||
|
if (lo is PatchOption.ListOption<*>) {
|
||||||
|
lo.options.toMutableList().toTypedArray()
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}?.toTypedArray() ?: emptyArray(),
|
||||||
|
it.dependencies?.map { dep ->
|
||||||
|
dep.java.patchName
|
||||||
|
}?.toTypedArray() ?: emptyArray(),
|
||||||
|
it.compatiblePackages?.map { pkg ->
|
||||||
|
CompatiblePackage(pkg.name, pkg.versions)
|
||||||
|
}?.toTypedArray() ?: emptyArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val json = File("patches.json")
|
||||||
|
json.writeText(gson.toJson(patches))
|
||||||
|
}
|
||||||
27
src/main/kotlin/app/revanced/meta/json/JsonPatch.kt
Normal file
27
src/main/kotlin/app/revanced/meta/json/JsonPatch.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
@file:Suppress("ArrayInDataClass") // We don't need it here.
|
||||||
|
|
||||||
|
package app.revanced.meta.json
|
||||||
|
|
||||||
|
data class JsonPatch(
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val version: String,
|
||||||
|
val excluded: Boolean,
|
||||||
|
val deprecated: Boolean,
|
||||||
|
val options: Array<Option>,
|
||||||
|
val dependencies: Array<String>,
|
||||||
|
val compatiblePackages: Array<CompatiblePackage>,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CompatiblePackage(
|
||||||
|
val name: String,
|
||||||
|
val versions: Array<String>,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Option(
|
||||||
|
val key: String,
|
||||||
|
val title: String,
|
||||||
|
val description: String,
|
||||||
|
val required: Boolean,
|
||||||
|
val choices: Array<*>?,
|
||||||
|
)
|
||||||
10
src/main/kotlin/app/revanced/meta/readme/Extensions.kt
Normal file
10
src/main/kotlin/app/revanced/meta/readme/Extensions.kt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.meta.readme
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.Data
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
|
||||||
|
internal fun Class<out Patch<Data>>.getLatestVersion() =
|
||||||
|
this.compatiblePackages?.first()?.versions?.map {
|
||||||
|
SemanticVersion.fromString(it)
|
||||||
|
}?.maxWithOrNull(SemanticVersionComparator)
|
||||||
42
src/main/kotlin/app/revanced/meta/readme/Generator.kt
Normal file
42
src/main/kotlin/app/revanced/meta/readme/Generator.kt
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package app.revanced.meta.readme
|
||||||
|
|
||||||
|
import app.revanced.meta.Bundle
|
||||||
|
import app.revanced.patcher.data.Data
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.description
|
||||||
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
private const val TABLE_HEADER =
|
||||||
|
"| \uD83D\uDC8A Patch | \uD83D\uDCDC Description | \uD83C\uDFF9 Target Version |\n" + "|:--------:|:--------------:|:-----------------:|"
|
||||||
|
|
||||||
|
private val TABLE_REGEX = Regex("\\{\\{\\s?table\\s?}}")
|
||||||
|
|
||||||
|
fun generateText(bundle: Bundle) {
|
||||||
|
val output = StringBuilder()
|
||||||
|
val packages = mutableMapOf<String, MutableList<Class<out Patch<Data>>>>()
|
||||||
|
|
||||||
|
for (patch in bundle) {
|
||||||
|
patch.compatiblePackages?.forEach { pkg ->
|
||||||
|
if (!packages.contains(pkg.name)) packages[pkg.name] = mutableListOf()
|
||||||
|
packages[pkg.name]!!.add(patch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pkg in packages) {
|
||||||
|
output.appendLine("### \uD83D\uDCE6 `${pkg.key}`")
|
||||||
|
output.appendLine("<details>\n")
|
||||||
|
|
||||||
|
output.appendLine(TABLE_HEADER)
|
||||||
|
pkg.value.forEach { output.appendLine("| `${it.patchName}` | ${it.description} | ${it.getLatestVersion() ?: "all"} |") }
|
||||||
|
|
||||||
|
output.appendLine("</details>\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
val readmeTemplate = Template(File("README-template.md").readText())
|
||||||
|
readmeTemplate.replaceVariable(TABLE_REGEX, output.toString())
|
||||||
|
|
||||||
|
val readme = File("README.md")
|
||||||
|
readme.writeText(readmeTemplate.toString())
|
||||||
|
}
|
||||||
22
src/main/kotlin/app/revanced/meta/readme/SemanticVersion.kt
Normal file
22
src/main/kotlin/app/revanced/meta/readme/SemanticVersion.kt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package app.revanced.meta.readme
|
||||||
|
|
||||||
|
data class SemanticVersion(val major: Int, val minor: Int, val patch: Int) {
|
||||||
|
companion object {
|
||||||
|
fun fromString(version: String): SemanticVersion {
|
||||||
|
val parts = version.split(".")
|
||||||
|
if (parts.count() != 3) throw IllegalArgumentException("Invalid semantic version")
|
||||||
|
val versionNumbers = parts.map { it.toInt() }
|
||||||
|
return SemanticVersion(versionNumbers[0], versionNumbers[1], versionNumbers[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "$major.$minor.$patch"
|
||||||
|
}
|
||||||
|
|
||||||
|
object SemanticVersionComparator : Comparator<SemanticVersion> {
|
||||||
|
override fun compare(a: SemanticVersion, b: SemanticVersion): Int = when {
|
||||||
|
a.major != b.major -> a.major - b.major
|
||||||
|
a.minor != b.minor -> a.minor - b.minor
|
||||||
|
else -> a.patch - b.patch
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/main/kotlin/app/revanced/meta/readme/Template.kt
Normal file
12
src/main/kotlin/app/revanced/meta/readme/Template.kt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package app.revanced.meta.readme
|
||||||
|
|
||||||
|
class Template(template: String) {
|
||||||
|
val result = StringBuilder(template)
|
||||||
|
|
||||||
|
fun replaceVariable(regex: Regex, value: String) {
|
||||||
|
val range = regex.find(result)!!.range
|
||||||
|
result.replace(range.first, range.last + 1, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = result.toString()
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.music.ad.video.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class MusicVideoAdsCompatibility
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package app.revanced.patches.music.ad.video.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.ad.video.annotations.MusicVideoAdsCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("show-video-ads-constructor-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Laghd;",
|
||||||
|
"<init>",
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@MusicVideoAdsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ShowMusicVideoAdsConstructorFingerprint : MethodFingerprint(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
Opcode.RETURN_VOID
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package app.revanced.patches.music.ad.video.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.music.ad.video.annotations.MusicVideoAdsCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("show-video-ads-method-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Laghd;", "d"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@MusicVideoAdsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ShowMusicVideoAdsFingerprint : MethodFingerprint(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("Z"), listOf(
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.RETURN_VOID
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package app.revanced.patches.music.ad.video.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patches.music.ad.video.annotations.MusicVideoAdsCompatibility
|
||||||
|
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsConstructorFingerprint
|
||||||
|
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsFingerprint
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("music-video-ads")
|
||||||
|
@Description("Removes ads in the music player.")
|
||||||
|
@MusicVideoAdsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class MusicVideoAdsPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
ShowMusicVideoAdsConstructorFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
ShowMusicVideoAdsFingerprint.resolve(data, ShowMusicVideoAdsConstructorFingerprint.result!!.classDef)
|
||||||
|
|
||||||
|
val result = ShowMusicVideoAdsFingerprint.result!!
|
||||||
|
|
||||||
|
result.mutableMethod.addInstructions(
|
||||||
|
result.scanResult.patternScanResult!!.startIndex, """
|
||||||
|
const/4 p1, 0x0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,10 +5,9 @@ import app.revanced.patcher.annotation.Package
|
|||||||
|
|
||||||
@Compatibility(
|
@Compatibility(
|
||||||
[Package(
|
[Package(
|
||||||
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
internal annotation class CodecsUnlockCompatibility
|
internal annotation class CodecsUnlockCompatibility
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
package app.revanced.patches.music.audio.codecs.signatures
|
package app.revanced.patches.music.audio.codecs.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
import app.revanced.patcher. fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@Name("all-codecs-reference-signature")
|
@Name("all-codecs-reference-fingerprint")
|
||||||
@MatchingMethod(
|
@MatchingMethod(
|
||||||
"Laari;",
|
"Laari;",
|
||||||
"b",
|
"b",
|
||||||
@@ -18,7 +18,7 @@ import org.jf.dexlib2.Opcode
|
|||||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
@CodecsUnlockCompatibility
|
@CodecsUnlockCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
object AllCodecsReferenceSignature : MethodSignature(
|
object AllCodecsReferenceFingerprint : MethodFingerprint(
|
||||||
"J", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L"), listOf(
|
"J", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L"), listOf(
|
||||||
Opcode.INVOKE_STATIC,
|
Opcode.INVOKE_STATIC,
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
@@ -1,27 +1,25 @@
|
|||||||
package app.revanced.patches.music.audio.codecs.signatures
|
package app.revanced.patches.music.audio.codecs.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@Name("codec-lock-signature")
|
@Name("codec-lock-fingerprint")
|
||||||
@MatchingMethod(
|
@MatchingMethod(
|
||||||
"Labwj;",
|
"Lacfr;",
|
||||||
"a",
|
"a",
|
||||||
)
|
)
|
||||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
@CodecsUnlockCompatibility
|
@CodecsUnlockCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
object CodecsLockSignature : MethodSignature(
|
object CodecsLockFingerprint : MethodFingerprint(
|
||||||
"L", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "L", "L", "L"), listOf(
|
"L", AccessFlags.PUBLIC or AccessFlags.STATIC, opcodes = listOf(
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_DIRECT,
|
Opcode.INVOKE_DIRECT,
|
||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_VIRTUAL,
|
||||||
Opcode.MOVE_RESULT,
|
Opcode.MOVE_RESULT,
|
||||||
@@ -29,19 +27,13 @@ object CodecsLockSignature : MethodSignature(
|
|||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_VIRTUAL,
|
||||||
Opcode.MOVE_RESULT,
|
Opcode.MOVE_RESULT,
|
||||||
Opcode.IF_NEZ,
|
Opcode.IF_NEZ,
|
||||||
Opcode.SGET,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.SGET_OBJECT,
|
Opcode.SGET_OBJECT,
|
||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
Opcode.INVOKE_INTERFACE,
|
||||||
Opcode.INVOKE_DIRECT,
|
Opcode.INVOKE_DIRECT,
|
||||||
Opcode.RETURN_OBJECT
|
Opcode.RETURN_OBJECT
|
||||||
)
|
),
|
||||||
)
|
strings = listOf("eac3_supported")
|
||||||
|
)
|
||||||
@@ -3,41 +3,52 @@ package app.revanced.patches.music.audio.codecs.patch
|
|||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
import app.revanced.patcher.data.implementation.toMethodWalker
|
import app.revanced.patcher.data.impl.toMethodWalker
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.util.smali.toInstruction
|
import app.revanced.patcher.util.smali.toInstruction
|
||||||
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
|
||||||
import app.revanced.patches.music.audio.codecs.signatures.AllCodecsReferenceSignature
|
import app.revanced.patches.music.audio.codecs.fingerprints.AllCodecsReferenceFingerprint
|
||||||
import app.revanced.patches.music.audio.codecs.signatures.CodecsLockSignature
|
import app.revanced.patches.music.audio.codecs.fingerprints.CodecsLockFingerprint
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("codecs-unlock")
|
@Name("codecs-unlock")
|
||||||
@Description("Enables more audio codecs. Usually results in better audio quality but may depend on song and device.")
|
@Description("Adds more audio codec options. The new audio codecs usually result in better audio quality.")
|
||||||
@CodecsUnlockCompatibility
|
@CodecsUnlockCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class CodecsUnlockPatch : BytecodePatch(
|
class CodecsUnlockPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(
|
||||||
CodecsLockSignature, AllCodecsReferenceSignature
|
CodecsLockFingerprint, AllCodecsReferenceFingerprint
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
var result = signatures.first().result!!
|
val codecsLockResult = CodecsLockFingerprint.result!!
|
||||||
|
|
||||||
val implementation = result.method.implementation!!
|
val implementation = codecsLockResult.mutableMethod.implementation!!
|
||||||
|
|
||||||
val instructionIndex = result.scanResult.startIndex
|
val scanResultStartIndex = codecsLockResult.scanResult.patternScanResult!!.startIndex
|
||||||
|
val instructionIndex = scanResultStartIndex +
|
||||||
|
if (implementation.instructions[scanResultStartIndex - 1].opcode == Opcode.CHECK_CAST) {
|
||||||
|
// for 5.16.xx and lower
|
||||||
|
-3
|
||||||
|
} else {
|
||||||
|
// since 5.17.xx
|
||||||
|
-2
|
||||||
|
}
|
||||||
|
|
||||||
result = signatures.last().result!!
|
val allCodecsResult = AllCodecsReferenceFingerprint.result!!
|
||||||
val codecMethod =
|
val allCodecsMethod =
|
||||||
data.toMethodWalker(result.immutableMethod).nextMethod(result.scanResult.startIndex).getMethod()
|
data.toMethodWalker(allCodecsResult.method)
|
||||||
|
.nextMethod(allCodecsResult.scanResult.patternScanResult!!.startIndex)
|
||||||
|
.getMethod()
|
||||||
|
|
||||||
implementation.replaceInstruction(
|
implementation.replaceInstruction(
|
||||||
instructionIndex,
|
instructionIndex,
|
||||||
"invoke-static {}, ${codecMethod.definingClass}->${codecMethod.name}()Ljava/util/Set;".toInstruction()
|
"invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;".toInstruction()
|
||||||
)
|
)
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import app.revanced.patcher.annotation.Package
|
|||||||
|
|
||||||
@Compatibility(
|
@Compatibility(
|
||||||
[Package(
|
[Package(
|
||||||
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
internal annotation class ExclusiveAudioCompatibility
|
internal annotation class ExclusiveAudioCompatibility
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package app.revanced.patches.music.audio.exclusiveaudio.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("audio-only-enabler-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lgmd;",
|
||||||
|
"d"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@ExclusiveAudioCompatibility
|
||||||
|
@Version(
|
||||||
|
"0.0.1"
|
||||||
|
)
|
||||||
|
object AudioOnlyEnablerFingerprint: MethodFingerprint(
|
||||||
|
"Z", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.RETURN
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
package app.revanced.patches.music.audio.exclusiveaudio.signatures
|
package app.revanced.patches.music.audio.exclusiveaudio.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
|
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@Name("exclusive-audio-signature")
|
@Name("exclusive-audio-fingerprints")
|
||||||
@MatchingMethod(
|
@MatchingMethod(
|
||||||
"Lgmd;", "c"
|
"Lgmd;", "c"
|
||||||
)
|
)
|
||||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
@ExclusiveAudioCompatibility
|
@ExclusiveAudioCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
object ExclusiveAudioSignature : MethodSignature(
|
object ExclusiveAudioFingerprint : MethodFingerprint(
|
||||||
"V",
|
"V",
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
listOf("L", "Z"),
|
listOf("L", "Z"),
|
||||||
@@ -3,68 +3,31 @@ package app.revanced.patches.music.audio.exclusiveaudio.patch
|
|||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
|
||||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.DirectPatternScanMethod
|
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
|
||||||
import app.revanced.patcher.util.smali.toInstruction
|
|
||||||
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
|
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
|
||||||
import app.revanced.patches.music.audio.exclusiveaudio.signatures.ExclusiveAudioSignature
|
import app.revanced.patches.music.audio.exclusiveaudio.fingerprints.AudioOnlyEnablerFingerprint
|
||||||
import org.jf.dexlib2.AccessFlags
|
|
||||||
import org.jf.dexlib2.Opcode
|
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("exclusive-audio-playback")
|
@Name("exclusive-audio-playback")
|
||||||
@Description("Add the option to play music without video.")
|
@Description("Enables the option to play music without video.")
|
||||||
@ExclusiveAudioCompatibility
|
@ExclusiveAudioCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class ExclusiveAudioPatch : BytecodePatch(
|
class ExclusiveAudioPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(
|
||||||
ExclusiveAudioSignature
|
AudioOnlyEnablerFingerprint
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
val result = signatures.first().result!!.findParentMethod(@Name("audio-only-enabler-method") @MatchingMethod(
|
val method = AudioOnlyEnablerFingerprint.result!!.mutableMethod
|
||||||
"Lgmd;",
|
method.replaceInstruction(method.implementation!!.instructions.count() - 1, "const/4 v0, 0x1")
|
||||||
"d"
|
method.addInstruction("return v0")
|
||||||
) @DirectPatternScanMethod @ExclusiveAudioCompatibility @Version(
|
|
||||||
"0.0.1"
|
|
||||||
) object : MethodSignature(
|
|
||||||
"Z", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IF_NEZ,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.RETURN
|
|
||||||
)
|
|
||||||
) {}) ?: return PatchResultError("Required parent method could not be found.")
|
|
||||||
|
|
||||||
val implementation = result.method.implementation!!
|
|
||||||
implementation.replaceInstruction(
|
|
||||||
implementation.instructions.count() - 1, "const/4 v0, 0x1".toInstruction()
|
|
||||||
)
|
|
||||||
implementation.addInstruction(
|
|
||||||
"return v0".toInstruction()
|
|
||||||
)
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.music.layout.compactheader.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class CompactHeaderCompatibility
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package app.revanced.patches.music.layout.compactheader.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.layout.compactheader.annotations.CompactHeaderCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("compact-header-constructor-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Llcz;", "<init>"
|
||||||
|
)
|
||||||
|
@CompactHeaderCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object CompactHeaderConstructorFingerprint : MethodFingerprint(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L", "L", "L"), listOf(
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package app.revanced.patches.music.layout.compactheader.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patches.music.layout.compactheader.annotations.CompactHeaderCompatibility
|
||||||
|
import app.revanced.patches.music.layout.compactheader.fingerprints.CompactHeaderConstructorFingerprint
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction11x
|
||||||
|
|
||||||
|
@Patch(false)
|
||||||
|
@Name("compact-header")
|
||||||
|
@Description("Hides the music category bar at the top of the homepage.")
|
||||||
|
@CompactHeaderCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class CompactHeaderPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
CompactHeaderConstructorFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val result = CompactHeaderConstructorFingerprint.result!!
|
||||||
|
val method = result.mutableMethod
|
||||||
|
|
||||||
|
val insertIndex = result.scanResult.patternScanResult!!.endIndex
|
||||||
|
val register = (method.implementation!!.instructions[insertIndex - 1] as BuilderInstruction11x).registerA
|
||||||
|
method.addInstructions(
|
||||||
|
insertIndex, """
|
||||||
|
const/16 v2, 0x8
|
||||||
|
invoke-virtual {v${register}, v2}, Landroid/view/View;->setVisibility(I)V
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.music.layout.minimizedplayback.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class MinimizedPlaybackCompatibility
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package app.revanced.patches.music.layout.minimizedplayback.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.layout.minimizedplayback.annotations.MinimizedPlaybackCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("minimized-playback-manager-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Llxw;", "c"
|
||||||
|
)
|
||||||
|
@MinimizedPlaybackCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
listOf("I", "L", "Z"),
|
||||||
|
listOf(
|
||||||
|
Opcode.IGET,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.IF_EQ,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.RETURN_VOID,
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_NE,
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package app.revanced.patches.music.layout.minimizedplayback.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.data.impl.toMethodWalker
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.music.layout.minimizedplayback.annotations.MinimizedPlaybackCompatibility
|
||||||
|
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("minimized-playback-music")
|
||||||
|
@Description("Enables minimized playback on Kids music.")
|
||||||
|
@MinimizedPlaybackCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class MinimizedPlaybackPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
MinimizedPlaybackManagerFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
MinimizedPlaybackManagerFingerprint.result!!.mutableMethod.addInstructions(
|
||||||
|
0, """
|
||||||
|
return-void
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.music.layout.premium.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class HideGetPremiumCompatibility
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package app.revanced.patches.music.layout.premium.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.layout.premium.annotations.HideGetPremiumCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("hide-get-premium-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lktn;", "k"
|
||||||
|
)
|
||||||
|
@HideGetPremiumCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object HideGetPremiumFingerprint : MethodFingerprint(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.CONST_16,
|
||||||
|
Opcode.GOTO,
|
||||||
|
Opcode.NOP,
|
||||||
|
Opcode.INVOKE_VIRTUAL
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package app.revanced.patches.music.layout.premium.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patches.music.layout.premium.annotations.HideGetPremiumCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Name("hide-get-premium-parent-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lktn;", "k"
|
||||||
|
)
|
||||||
|
@HideGetPremiumCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object HideGetPremiumParentFingerprint : MethodFingerprint(
|
||||||
|
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
|
||||||
|
Opcode.IGET_BOOLEAN,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC
|
||||||
|
),
|
||||||
|
listOf("FEmusic_history"),
|
||||||
|
)
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package app.revanced.patches.music.layout.premium.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patches.music.layout.premium.annotations.HideGetPremiumCompatibility
|
||||||
|
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumFingerprint
|
||||||
|
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumParentFingerprint
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("hide-get-premium")
|
||||||
|
@Description("Removes all \"Get Premium\" evidences from the avatar menu.")
|
||||||
|
@HideGetPremiumCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class HideGetPremiumPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
HideGetPremiumParentFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val parentResult = HideGetPremiumParentFingerprint.result!!
|
||||||
|
HideGetPremiumFingerprint.resolve(data, parentResult.classDef)
|
||||||
|
|
||||||
|
val startIndex = parentResult.scanResult.patternScanResult!!.startIndex
|
||||||
|
|
||||||
|
val parentMethod = parentResult.mutableMethod
|
||||||
|
parentMethod.replaceInstruction(
|
||||||
|
startIndex, """
|
||||||
|
const/4 v1, 0x0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = HideGetPremiumFingerprint.result!!
|
||||||
|
val method = result.mutableMethod
|
||||||
|
method.addInstructions(
|
||||||
|
startIndex, """
|
||||||
|
const/16 v0, 0x8
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,12 +3,16 @@ package app.revanced.patches.music.layout.tastebuilder.annotations
|
|||||||
import app.revanced.patcher.annotation.Compatibility
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
import app.revanced.patcher.annotation.Package
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -- Note 2022-08-05 --
|
||||||
|
* Since 5.17.xx the tastebuilder component is dismissible, so this patch is less useful
|
||||||
|
* also it is partly litho now
|
||||||
|
*/
|
||||||
@Compatibility(
|
@Compatibility(
|
||||||
[Package(
|
[Package(
|
||||||
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
"com.google.android.apps.youtube.music", arrayOf("5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
internal annotation class RemoveTasteBuilderCompatibility
|
internal annotation class RemoveTasteBuilderCompatibility
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
package app.revanced.patches.music.layout.tastebuilder.signatures
|
package app.revanced.patches.music.layout.tastebuilder.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
|
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@Name("taste-builder-constructor")
|
@Name("taste-builder-constructor-fingerprint")
|
||||||
@MatchingMethod(
|
@MatchingMethod(
|
||||||
"Lkyu;", "<init>"
|
"Lkyu;", "<init>"
|
||||||
)
|
)
|
||||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
@RemoveTasteBuilderCompatibility
|
@RemoveTasteBuilderCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
object TasteBuilderConstructorSignature : MethodSignature(
|
object TasteBuilderConstructorFingerprint : MethodFingerprint(
|
||||||
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L", "L"), listOf(
|
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
|
||||||
Opcode.INVOKE_DIRECT,
|
Opcode.INVOKE_DIRECT,
|
||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_VIRTUAL,
|
||||||
Opcode.NEW_INSTANCE,
|
Opcode.NEW_INSTANCE,
|
||||||
@@ -35,9 +35,9 @@ object TasteBuilderConstructorSignature : MethodSignature(
|
|||||||
Opcode.INVOKE_VIRTUAL,
|
Opcode.INVOKE_VIRTUAL,
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.CHECK_CAST,
|
Opcode.CHECK_CAST,
|
||||||
Opcode.IPUT_OBJECT,
|
|
||||||
Opcode.NEW_INSTANCE,
|
Opcode.NEW_INSTANCE,
|
||||||
Opcode.INVOKE_DIRECT,
|
Opcode.INVOKE_DIRECT,
|
||||||
Opcode.IPUT_OBJECT
|
Opcode.IPUT_OBJECT,
|
||||||
|
Opcode.CONST
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -3,42 +3,37 @@ package app.revanced.patches.music.layout.tastebuilder.patch
|
|||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.util.smali.toInstructions
|
|
||||||
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
|
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
|
||||||
import app.revanced.patches.music.layout.tastebuilder.signatures.TasteBuilderConstructorSignature
|
import app.revanced.patches.music.layout.tastebuilder.fingerprints.TasteBuilderConstructorFingerprint
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("tasteBuilder-remover")
|
@Name("tasteBuilder-remover")
|
||||||
@Description("Removes the \"Tell us which artists you like\" card from the Home screen. The same functionality can be triggered from the settings anyway.")
|
@Description("Removes the \"Tell us which artists you like\" card from the home screen.")
|
||||||
@RemoveTasteBuilderCompatibility
|
@RemoveTasteBuilderCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class RemoveTasteBuilderPatch : BytecodePatch(
|
class RemoveTasteBuilderPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(
|
||||||
TasteBuilderConstructorSignature
|
TasteBuilderConstructorFingerprint
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
val result = signatures.first().result!!
|
val result = TasteBuilderConstructorFingerprint.result!!
|
||||||
val implementation = result.method.implementation!!
|
val method = result.mutableMethod
|
||||||
|
|
||||||
val insertIndex = result.scanResult.endIndex - 8
|
val insertIndex = result.scanResult.patternScanResult!!.endIndex - 8
|
||||||
|
val register = (method.implementation!!.instructions[insertIndex] as Instruction22c).registerA
|
||||||
val register = (implementation.instructions[insertIndex] as Instruction22c).registerA
|
method.addInstructions(
|
||||||
|
insertIndex, """
|
||||||
val instructionList = """
|
|
||||||
const/16 v1, 0x8
|
const/16 v1, 0x8
|
||||||
invoke-virtual {v${register}, v1}, Landroid/view/View;->setVisibility(I)V
|
invoke-virtual {v${register}, v1}, Landroid/view/View;->setVisibility(I)V
|
||||||
""".trimIndent().toInstructions().toMutableList()
|
"""
|
||||||
|
|
||||||
implementation.addInstructions(
|
|
||||||
insertIndex, instructionList
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ import app.revanced.patcher.annotation.Package
|
|||||||
|
|
||||||
@Compatibility(
|
@Compatibility(
|
||||||
[Package(
|
[Package(
|
||||||
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
internal annotation class RemoveUpgradeButtonCompatibility
|
internal annotation class RemoveUpgradeButtonCompatibility
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
package app.revanced.patches.music.layout.upgradebutton.signatures
|
package app.revanced.patches.music.layout.upgradebutton.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
|
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@Name("pivot-bar-constructor")
|
@Name("pivot-bar-constructor-fingerprint")
|
||||||
@MatchingMethod(
|
@MatchingMethod(
|
||||||
"Lhfu;", "<init2>"
|
"Lhfu;", "<init2>"
|
||||||
)
|
)
|
||||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
@RemoveUpgradeButtonCompatibility
|
@RemoveUpgradeButtonCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
object PivotBarConstructorSignature : MethodSignature(
|
object PivotBarConstructorFingerprint : MethodFingerprint(
|
||||||
"V",
|
"V",
|
||||||
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
listOf("L", "Z"),
|
listOf("L", "Z"),
|
||||||
@@ -3,15 +3,15 @@ package app.revanced.patches.music.layout.upgradebutton.patch
|
|||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.util.smali.toInstructions
|
import app.revanced.patcher.util.smali.toInstructions
|
||||||
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
|
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
|
||||||
import app.revanced.patches.music.layout.upgradebutton.signatures.PivotBarConstructorSignature
|
import app.revanced.patches.music.layout.upgradebutton.fingerprints.PivotBarConstructorFingerprint
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction22t
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction22t
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
|
||||||
@@ -20,20 +20,20 @@ import org.jf.dexlib2.iface.instruction.formats.Instruction35c
|
|||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("upgrade-button-remover")
|
@Name("upgrade-button-remover")
|
||||||
@Description("Remove the upgrade tab from t he pivot bar in YouTube music.")
|
@Description("Removes the upgrade tab from the pivot bar.")
|
||||||
@RemoveUpgradeButtonCompatibility
|
@RemoveUpgradeButtonCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class RemoveUpgradeButtonPatch : BytecodePatch(
|
class RemoveUpgradeButtonPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(
|
||||||
PivotBarConstructorSignature
|
PivotBarConstructorFingerprint
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
val result = signatures.first().result!!
|
val result = PivotBarConstructorFingerprint.result!!
|
||||||
val implementation = result.method.implementation!!
|
val implementation = result.mutableMethod.implementation!!
|
||||||
|
|
||||||
val pivotBarElementFieldRef =
|
val pivotBarElementFieldRef =
|
||||||
(implementation.instructions[result.scanResult.endIndex - 1] as Instruction22c).reference
|
(implementation.instructions[result.scanResult.patternScanResult!!.endIndex - 1] as Instruction22c).reference
|
||||||
|
|
||||||
val register = (implementation.instructions.first() as Instruction35c).registerC
|
val register = (implementation.instructions.first() as Instruction35c).registerC
|
||||||
// first compile all the needed instructions
|
// first compile all the needed instructions
|
||||||
@@ -43,19 +43,21 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
|
|||||||
const/4 v2, 0x3
|
const/4 v2, 0x3
|
||||||
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
|
invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object;
|
||||||
iput-object v0, v$register, $pivotBarElementFieldRef
|
iput-object v0, v$register, $pivotBarElementFieldRef
|
||||||
""".trimIndent().toInstructions().toMutableList()
|
""".toInstructions().toMutableList()
|
||||||
|
|
||||||
|
|
||||||
|
val endIndex = result.scanResult.patternScanResult!!.endIndex
|
||||||
|
|
||||||
// replace the instruction to retain the label at given index
|
// replace the instruction to retain the label at given index
|
||||||
implementation.replaceInstruction(
|
implementation.replaceInstruction(
|
||||||
result.scanResult.endIndex - 1, instructionList[0] // invoke-interface
|
endIndex - 1, instructionList[0] // invoke-interface
|
||||||
)
|
)
|
||||||
// do not forget to remove this instruction since we added it already
|
// do not forget to remove this instruction since we added it already
|
||||||
instructionList.removeFirst()
|
instructionList.removeFirst()
|
||||||
|
|
||||||
val exitInstruction = instructionList.last() // iput-object
|
val exitInstruction = instructionList.last() // iput-object
|
||||||
implementation.addInstruction(
|
implementation.addInstruction(
|
||||||
result.scanResult.endIndex, exitInstruction
|
endIndex, exitInstruction
|
||||||
)
|
)
|
||||||
// do not forget to remove this instruction since we added it already
|
// do not forget to remove this instruction since we added it already
|
||||||
instructionList.removeLast()
|
instructionList.removeLast()
|
||||||
@@ -64,13 +66,13 @@ class RemoveUpgradeButtonPatch : BytecodePatch(
|
|||||||
instructionList.add(
|
instructionList.add(
|
||||||
2, // if-le
|
2, // if-le
|
||||||
BuilderInstruction22t(
|
BuilderInstruction22t(
|
||||||
Opcode.IF_LE, 1, 2, implementation.newLabelForIndex(result.scanResult.endIndex)
|
Opcode.IF_LE, 1, 2, implementation.newLabelForIndex(endIndex)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
implementation.addInstructions(
|
implementation.addInstructions(
|
||||||
result.scanResult.endIndex, instructionList
|
endIndex, instructionList
|
||||||
)
|
)
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class MusicMicroGPatchCompatibility
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
|
||||||
|
|
||||||
|
@Name("cast-context-fetch-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lvvz;", "a"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@MicroGPatchCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object CastContextFetchFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("Error fetching CastContext.")
|
||||||
|
)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
|
||||||
|
|
||||||
|
@Name("cast-module-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Llqh;", "c"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@MicroGPatchCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object CastDynamiteModuleFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("com.google.android.gms.cast.framework.internal.CastDynamiteModuleImpl")
|
||||||
|
)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
|
||||||
|
|
||||||
|
@Name("cast-context-fetch-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lmcf;", "c"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@MicroGPatchCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object CastDynamiteModuleV2Fingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("Failed to load module via V2: ")
|
||||||
|
)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("google-play-utility-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lnuv;", "b"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@MusicMicroGPatchCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object GooglePlayUtilityFingerprint : MethodFingerprint(
|
||||||
|
"I",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
listOf("L", "I"),
|
||||||
|
strings = listOf(
|
||||||
|
"This should never happen.",
|
||||||
|
"MetadataValueReader",
|
||||||
|
"GooglePlayServicesUtil",
|
||||||
|
"com.android.vending",
|
||||||
|
"android.hardware.type.embedded"
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
|
||||||
|
|
||||||
|
@Name("google-play-prime-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lrwi;", "a"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@MusicMicroGPatchCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object PrimeFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
|
||||||
|
)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("google-play-service-checker-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lnuv;", "d"
|
||||||
|
)
|
||||||
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
|
@MusicMicroGPatchCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ServiceCheckFingerprint : MethodFingerprint(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
listOf("L", "I"),
|
||||||
|
strings = listOf("Google Play Services not available")
|
||||||
|
)
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.patch.bytecode
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
|
||||||
|
import app.revanced.patches.music.misc.microg.fingerprints.*
|
||||||
|
import app.revanced.patches.music.misc.microg.patch.resource.MusicMicroGResourcePatch
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
||||||
|
import app.revanced.patches.youtube.misc.microg.shared.Constants
|
||||||
|
import app.revanced.util.microg.MicroGBytecodeHelper
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@DependsOn([MusicMicroGResourcePatch::class])
|
||||||
|
@Name("music-microg-support")
|
||||||
|
@Description("Allows YouTube Music ReVanced to run without root and under a different package name.")
|
||||||
|
@MusicMicroGPatchCompatibility
|
||||||
|
@Version("0.0.2")
|
||||||
|
class MusicMicroGBytecodePatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
ServiceCheckFingerprint,
|
||||||
|
GooglePlayUtilityFingerprint,
|
||||||
|
CastDynamiteModuleFingerprint,
|
||||||
|
CastDynamiteModuleV2Fingerprint,
|
||||||
|
CastContextFetchFingerprint,
|
||||||
|
PrimeFingerprint,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// NOTE: the previous patch also replaced the following strings, but it seems like they are not needed:
|
||||||
|
// - "com.google.android.gms.chimera.GmsIntentOperationService",
|
||||||
|
// - "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
|
||||||
|
// - "com.google.android.gms.phenotype.internal.IPhenotypeService",
|
||||||
|
// - "com.google.android.gms.phenotype.PACKAGE_NAME",
|
||||||
|
// - "com.google.android.gms.phenotype.UPDATE",
|
||||||
|
// - "com.google.android.gms.phenotype",
|
||||||
|
override fun execute(data: BytecodeData) =
|
||||||
|
// apply common microG patch
|
||||||
|
MicroGBytecodeHelper.patchBytecode(
|
||||||
|
data,
|
||||||
|
arrayOf(
|
||||||
|
MicroGBytecodeHelper.packageNameTransform(
|
||||||
|
Constants.PACKAGE_NAME,
|
||||||
|
Constants.REVANCED_PACKAGE_NAME
|
||||||
|
)
|
||||||
|
),
|
||||||
|
MicroGBytecodeHelper.PrimeMethodTransformationData(
|
||||||
|
PrimeFingerprint,
|
||||||
|
MUSIC_PACKAGE_NAME,
|
||||||
|
REVANCED_MUSIC_PACKAGE_NAME
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
ServiceCheckFingerprint,
|
||||||
|
GooglePlayUtilityFingerprint,
|
||||||
|
CastDynamiteModuleFingerprint,
|
||||||
|
CastDynamiteModuleV2Fingerprint,
|
||||||
|
CastContextFetchFingerprint
|
||||||
|
)
|
||||||
|
).let { PatchResultSuccess() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.patch.resource
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||||
|
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_APP_NAME
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
|
||||||
|
import app.revanced.util.microg.MicroGManifestHelper
|
||||||
|
import app.revanced.util.microg.MicroGResourceHelper
|
||||||
|
|
||||||
|
@Name("music-microg-resource-patch")
|
||||||
|
@Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.")
|
||||||
|
@MusicMicroGPatchCompatibility
|
||||||
|
@Version("0.0.2")
|
||||||
|
class MusicMicroGResourcePatch : ResourcePatch() {
|
||||||
|
override fun execute(data: ResourceData): PatchResult {
|
||||||
|
// update manifest
|
||||||
|
MicroGResourceHelper.patchManifest(
|
||||||
|
data,
|
||||||
|
MUSIC_PACKAGE_NAME,
|
||||||
|
REVANCED_MUSIC_PACKAGE_NAME,
|
||||||
|
REVANCED_MUSIC_APP_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
// add metadata to the manifest
|
||||||
|
MicroGManifestHelper.addSpoofingMetadata(
|
||||||
|
data,
|
||||||
|
SPOOFED_PACKAGE_NAME,
|
||||||
|
SPOOFED_PACKAGE_SIGNATURE
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.music.misc.microg.shared
|
||||||
|
|
||||||
|
object Constants {
|
||||||
|
internal const val REVANCED_MUSIC_APP_NAME = "YouTube Music ReVanced"
|
||||||
|
internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music"
|
||||||
|
internal const val MUSIC_PACKAGE_NAME = "com.google.android.apps.youtube.music"
|
||||||
|
internal const val SPOOFED_PACKAGE_NAME = MUSIC_PACKAGE_NAME
|
||||||
|
internal const val SPOOFED_PACKAGE_SIGNATURE = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"
|
||||||
|
}
|
||||||
@@ -5,10 +5,9 @@ import app.revanced.patcher.annotation.Package
|
|||||||
|
|
||||||
@Compatibility(
|
@Compatibility(
|
||||||
[Package(
|
[Package(
|
||||||
"com.google.android.apps.youtube.music", arrayOf("5.03.50")
|
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50","5.25.51")
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
internal annotation class BackgroundPlayCompatibility
|
internal annotation class BackgroundPlayCompatibility
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
package app.revanced.patches.music.premium.backgroundplay.signatures
|
package app.revanced.patches.music.premium.backgroundplay.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
|
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
@Name("background-playback-disabler-signature")
|
@Name("background-playback-disabler-fingerprint")
|
||||||
@MatchingMethod(
|
@MatchingMethod(
|
||||||
"Lafgf;", "e"
|
"Lafgf;", "e"
|
||||||
)
|
)
|
||||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
||||||
@BackgroundPlayCompatibility
|
@BackgroundPlayCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
object BackgroundPlaybackDisableSignature : MethodSignature(
|
object BackgroundPlaybackDisableFingerprint : MethodFingerprint(
|
||||||
"Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), listOf(
|
"Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), listOf(
|
||||||
Opcode.CONST_4,
|
Opcode.CONST_4,
|
||||||
Opcode.IF_EQZ,
|
Opcode.IF_EQZ,
|
||||||
@@ -3,33 +3,32 @@ package app.revanced.patches.music.premium.backgroundplay.patch
|
|||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.implementation.BytecodeData
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.util.smali.toInstructions
|
|
||||||
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
|
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
|
||||||
import app.revanced.patches.music.premium.backgroundplay.signatures.BackgroundPlaybackDisableSignature
|
import app.revanced.patches.music.premium.backgroundplay.fingerprints.BackgroundPlaybackDisableFingerprint
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("background-play")
|
@Name("background-play")
|
||||||
@Description("Enable playing music in the background.")
|
@Description("Enables playing music in the background.")
|
||||||
@BackgroundPlayCompatibility
|
@BackgroundPlayCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.1")
|
||||||
class BackgroundPlayPatch : BytecodePatch(
|
class BackgroundPlayPatch : BytecodePatch(
|
||||||
listOf(
|
listOf(
|
||||||
BackgroundPlaybackDisableSignature
|
BackgroundPlaybackDisableFingerprint
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
signatures.first().result!!.method.implementation!!.addInstructions(
|
BackgroundPlaybackDisableFingerprint.result!!.mutableMethod.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
const/4 v0, 0x1
|
const/4 v0, 0x1
|
||||||
return v0
|
return v0
|
||||||
""".trimIndent().toInstructions()
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.reddit.ad.general.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility([Package("com.reddit.frontpage")])
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class GeneralAdsCompatibility
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package app.revanced.patches.reddit.ad.general.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patches.reddit.ad.general.annotations.GeneralAdsCompatibility
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.StringReference
|
||||||
|
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("general-reddit-ads")
|
||||||
|
@Description("Removes general ads from the Reddit frontpage and subreddits.")
|
||||||
|
@GeneralAdsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class GeneralAdsPatch : BytecodePatch() {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
data.classes.forEach { classDef ->
|
||||||
|
classDef.methods.forEach methodLoop@{ method ->
|
||||||
|
val implementation = method.implementation ?: return@methodLoop
|
||||||
|
|
||||||
|
implementation.instructions.forEachIndexed { i, instruction ->
|
||||||
|
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
|
||||||
|
if (((instruction as ReferenceInstruction).reference as StringReference).string != "AdPost") return@forEachIndexed
|
||||||
|
|
||||||
|
val proxiedClass = data.proxy(classDef).resolve()
|
||||||
|
|
||||||
|
val proxiedImplementation = proxiedClass.methods.first {
|
||||||
|
it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes)
|
||||||
|
}.implementation!!
|
||||||
|
|
||||||
|
var newString = "AdPost1"
|
||||||
|
if (proxiedImplementation.instructions[i - 1].opcode == Opcode.CONST_STRING) {
|
||||||
|
newString = "SubredditPost"
|
||||||
|
}
|
||||||
|
proxiedImplementation.replaceInstruction(
|
||||||
|
i, BuilderInstruction21c(
|
||||||
|
Opcode.CONST_STRING, (proxiedImplementation.instructions[i] as BuilderInstruction21c).registerA, ImmutableStringReference(newString)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.reddit.layout.premiumicon.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility([Package("com.reddit.frontpage")])
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class PremiumIconCompatibility
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package app.revanced.patches.reddit.layout.premiumicon.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.reddit.layout.premiumicon.annotations.PremiumIconCompatibility
|
||||||
|
|
||||||
|
@Name("premium-icon-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lcom/reddit/domain/model/MyAccount;", "isPremiumSubscriber"
|
||||||
|
)
|
||||||
|
@PremiumIconCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object PremiumIconFingerprint : MethodFingerprint(
|
||||||
|
"Z",
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("MyAccount;") && methodDef.name == "isPremiumSubscriber"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package app.revanced.patches.reddit.layout.premiumicon.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patches.reddit.layout.premiumicon.annotations.PremiumIconCompatibility
|
||||||
|
import app.revanced.patches.reddit.layout.premiumicon.fingerprints.PremiumIconFingerprint
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("premium-icon-reddit")
|
||||||
|
@Description("Unlocking Premium Icons in reddit app.")
|
||||||
|
@PremiumIconCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class PremiumIconPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
PremiumIconFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val method = PremiumIconFingerprint.result!!.mutableMethod
|
||||||
|
method.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const/4 v0, 0x1
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package app.revanced.patches.spotify.audio.annotation
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package("com.spotify.music")]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class DisableCaptureRestrictionCompatibility
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package app.revanced.patches.spotify.audio.bytecode.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.instruction
|
||||||
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility
|
||||||
|
import app.revanced.patches.spotify.audio.fingerprints.DisableCaptureRestrictionAudioDriverFingerprint
|
||||||
|
import app.revanced.patches.spotify.audio.resource.patch.DisableCaptureRestrictionResourcePatch
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("disable-capture-restriction")
|
||||||
|
@DependsOn([DisableCaptureRestrictionResourcePatch::class])
|
||||||
|
@Description("Allows capturing Spotify's audio output while screen sharing or screen recording.")
|
||||||
|
@DisableCaptureRestrictionCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class DisableCaptureRestrictionBytecodePatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
DisableCaptureRestrictionAudioDriverFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
private fun MutableMethod.replaceConstant4Instruction(index: Int, instruction: Instruction, with: Int) {
|
||||||
|
val register = (instruction as OneRegisterInstruction).registerA
|
||||||
|
this.replaceInstruction(
|
||||||
|
index, "const/4 v$register, $with"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val method = DisableCaptureRestrictionAudioDriverFingerprint.result!!.mutableMethod
|
||||||
|
|
||||||
|
// Replace constant that contains the capture policy parameter for AudioAttributesBuilder.setAllowedCapturePolicy()
|
||||||
|
val instruction = method.instruction(CONST_INSTRUCTION_POSITION)
|
||||||
|
method.replaceConstant4Instruction(CONST_INSTRUCTION_POSITION, instruction, ALLOW_CAPTURE_BY_ALL)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val CONST_INSTRUCTION_POSITION = 2
|
||||||
|
const val ALLOW_CAPTURE_BY_ALL = 0x01
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package app.revanced.patches.spotify.audio.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility
|
||||||
|
|
||||||
|
@Name("disable-capture-restriction-audio-driver-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lcom/spotify/playback/playbacknative/AudioDriver;", "constructAudioAttributes"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@DisableCaptureRestrictionCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object DisableCaptureRestrictionAudioDriverFingerprint : MethodFingerprint(
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass == "Lcom/spotify/playback/playbacknative/AudioDriver;" && methodDef.name == "constructAudioAttributes"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package app.revanced.patches.spotify.audio.resource.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||||
|
import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility
|
||||||
|
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
@Name("disable-capture-restriction-resource-patch")
|
||||||
|
@Description("Sets allowAudioPlaybackCapture in manifest to true.")
|
||||||
|
@DisableCaptureRestrictionCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class DisableCaptureRestrictionResourcePatch : ResourcePatch() {
|
||||||
|
override fun execute(data: ResourceData): PatchResult {
|
||||||
|
// create an xml editor instance
|
||||||
|
data.xmlEditor["AndroidManifest.xml"].use { dom ->
|
||||||
|
// get the application node
|
||||||
|
val applicationNode = dom
|
||||||
|
.file
|
||||||
|
.getElementsByTagName("application")
|
||||||
|
.item(0) as Element
|
||||||
|
|
||||||
|
// set allowAudioPlaybackCapture attribute to true
|
||||||
|
applicationNode.setAttribute("android:allowAudioPlaybackCapture", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.spotify.layout.theme.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility([Package("com.spotify.music")])
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class ThemeCompatibility
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package app.revanced.patches.spotify.layout.theme.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
import app.revanced.patcher.patch.OptionsContainer
|
||||||
|
import app.revanced.patcher.patch.PatchOption
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||||
|
import app.revanced.patches.spotify.layout.theme.annotations.ThemeCompatibility
|
||||||
|
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@DependsOn([FixLocaleConfigErrorPatch::class])
|
||||||
|
@Name("spotify-theme")
|
||||||
|
@Description("Applies a custom theme.")
|
||||||
|
@ThemeCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class ThemePatch : ResourcePatch() {
|
||||||
|
override fun execute(data: ResourceData): PatchResult {
|
||||||
|
data.xmlEditor["res/values/colors.xml"].use { editor ->
|
||||||
|
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element
|
||||||
|
|
||||||
|
for (i in 0 until resourcesNode.childNodes.length) {
|
||||||
|
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
|
||||||
|
|
||||||
|
node.textContent = when (node.getAttribute("name")) {
|
||||||
|
"gray_7" -> backgroundColor!!
|
||||||
|
"dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light" -> accentColor!!
|
||||||
|
"dark_brightaccent_background_press" -> accentPressedColor!!
|
||||||
|
else -> continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : OptionsContainer() {
|
||||||
|
var backgroundColor: String? by option(
|
||||||
|
PatchOption.StringOption(
|
||||||
|
key = "backgroundColor",
|
||||||
|
default = "@android:color/black",
|
||||||
|
title = "Background color",
|
||||||
|
description = "The background color. Can be a hex color or a resource reference.",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var accentColor: String? by option(
|
||||||
|
PatchOption.StringOption(
|
||||||
|
key = "accentColor",
|
||||||
|
default = "#ff1ed760",
|
||||||
|
title = "Accent color",
|
||||||
|
description = "The accent color ('spotify green' by default). Can be a hex color or a resource reference.",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var accentPressedColor: String? by option(
|
||||||
|
PatchOption.StringOption(
|
||||||
|
key = "accentPressedColor",
|
||||||
|
default = "#ff169c46",
|
||||||
|
title = "Pressed accent for the dark theme",
|
||||||
|
description = "The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.spotify.premium_navbar_tab.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility([Package("com.spotify.music")])
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class PremiumNavbarTabCompatibility
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.spotify.premium_navbar_tab.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.spotify.premium_navbar_tab.annotations.PremiumNavbarTabCompatibility
|
||||||
|
|
||||||
|
@Name("premium-navbar-fingerprint")
|
||||||
|
@Version("0.0.1")
|
||||||
|
@PremiumNavbarTabCompatibility
|
||||||
|
object AddPremiumNavbarTabFingerprint : MethodFingerprint(
|
||||||
|
parameters = listOf("L", "L", "L", "L", "L", "L")
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.spotify.premium_navbar_tab.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.spotify.premium_navbar_tab.annotations.PremiumNavbarTabCompatibility
|
||||||
|
|
||||||
|
@Name("add-premium-navbar-tab-parent-fingerprint")
|
||||||
|
@Version("0.0.1")
|
||||||
|
@PremiumNavbarTabCompatibility
|
||||||
|
object AddPremiumNavbarTabParentFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("com.samsung.android.samsungaccount.action.REQUEST_AUTHCODE")
|
||||||
|
)
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package app.revanced.patches.spotify.premium_navbar_tab.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.removeInstruction
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patches.spotify.premium_navbar_tab.annotations.PremiumNavbarTabCompatibility
|
||||||
|
import app.revanced.patches.spotify.premium_navbar_tab.fingerprints.AddPremiumNavbarTabFingerprint
|
||||||
|
import app.revanced.patches.spotify.premium_navbar_tab.fingerprints.AddPremiumNavbarTabParentFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("hide-premium-navbar")
|
||||||
|
@Description("Removes the premium tab from the navbar.")
|
||||||
|
@PremiumNavbarTabCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
@DependsOn([ResourceMappingResourcePatch::class])
|
||||||
|
class PremiumNavbarTabPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
AddPremiumNavbarTabParentFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val parentResult = AddPremiumNavbarTabParentFingerprint.result!!
|
||||||
|
AddPremiumNavbarTabFingerprint.resolve(data, parentResult.classDef)
|
||||||
|
|
||||||
|
val result = AddPremiumNavbarTabFingerprint.result!!
|
||||||
|
|
||||||
|
val method = result.mutableMethod
|
||||||
|
val methodInstructions = method.implementation!!.instructions
|
||||||
|
val lastInstructionIdx = methodInstructions.size - 1
|
||||||
|
|
||||||
|
val premiumTabId = ResourceMappingResourcePatch.resourceMappings.single{it.type == "id" && it.name == "premium_tab"}.id
|
||||||
|
|
||||||
|
var removeAmount = 2
|
||||||
|
// 2nd const remove method
|
||||||
|
for ((i, instruction) in methodInstructions.asReversed().withIndex()) {
|
||||||
|
if (instruction.opcode.ordinal != Opcode.CONST.ordinal) continue
|
||||||
|
if ((instruction as WideLiteralInstruction).wideLiteral != premiumTabId) continue
|
||||||
|
|
||||||
|
val findThreshold = 10
|
||||||
|
val constIndex = lastInstructionIdx - i
|
||||||
|
val invokeInstruction = methodInstructions.subList(constIndex, constIndex + findThreshold).first {
|
||||||
|
it.opcode.ordinal == Opcode.INVOKE_VIRTUAL_RANGE.ordinal
|
||||||
|
}
|
||||||
|
method.removeInstruction(methodInstructions.indexOf(invokeInstruction))
|
||||||
|
|
||||||
|
if (--removeAmount == 0) break
|
||||||
|
}
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.tiktok.ad.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[
|
||||||
|
Package("com.ss.android.ugc.trill"),
|
||||||
|
Package("com.zhiliaoapp.musically")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class TiktokAdsCompatibility
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package app.revanced.patches.tiktok.ad.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.ad.annotations.TiktokAdsCompatibility
|
||||||
|
|
||||||
|
@Name("convert-help-v2-feeditemlist-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lbeancopy/ConvertHelp;",
|
||||||
|
"com${'$'}ss${'$'}ugc${'$'}aweme${'$'}proto${'$'}aweme_v2_feed_response${'$'}${'$'}com${'$'}ss${'$'}android${'$'}ugc${'$'}aweme${'$'}feed${'$'}model${'$'}FeedItemList",
|
||||||
|
)
|
||||||
|
@TiktokAdsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ConvertHelpFeedItemListFingerprint : MethodFingerprint(
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/ConvertHelp;") &&
|
||||||
|
methodDef.name.endsWith("${'$'}FeedItemList")
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package app.revanced.patches.tiktok.ad.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.ad.annotations.TiktokAdsCompatibility
|
||||||
|
|
||||||
|
@Name("feed-item-clone-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;",
|
||||||
|
"clone",
|
||||||
|
)
|
||||||
|
@TiktokAdsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object FeedItemListCloneFingerprint : MethodFingerprint(
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/FeedItemList;") && methodDef.name == "clone"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package app.revanced.patches.tiktok.ad.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstruction
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patches.tiktok.ad.annotations.TiktokAdsCompatibility
|
||||||
|
import app.revanced.patches.tiktok.ad.fingerprints.ConvertHelpFeedItemListFingerprint
|
||||||
|
import app.revanced.patches.tiktok.ad.fingerprints.FeedItemListCloneFingerprint
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("tiktok-ads")
|
||||||
|
@Description("Removes ads from TikTok.")
|
||||||
|
@TiktokAdsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class TiktokAdsPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
FeedItemListCloneFingerprint,
|
||||||
|
ConvertHelpFeedItemListFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
listOf(
|
||||||
|
FeedItemListCloneFingerprint,
|
||||||
|
ConvertHelpFeedItemListFingerprint
|
||||||
|
).forEach { fingerprint ->
|
||||||
|
val method = fingerprint.result!!.mutableMethod
|
||||||
|
// iterate all instructions in the clone method
|
||||||
|
for ((index, instruction) in method.implementation!!.instructions.withIndex()) {
|
||||||
|
// conditions for the instruction we need
|
||||||
|
if (instruction.opcode.ordinal != Opcode.IPUT_OBJECT.ordinal) continue
|
||||||
|
val preloadAdsFieldInstruction = (instruction as? ReferenceInstruction)
|
||||||
|
if ((preloadAdsFieldInstruction?.reference as? FieldReference)?.name != "preloadAds") continue
|
||||||
|
|
||||||
|
// set null instead of the field "preloadAds"
|
||||||
|
val overrideRegister = (preloadAdsFieldInstruction as TwoRegisterInstruction).registerA
|
||||||
|
method.addInstruction(
|
||||||
|
index,
|
||||||
|
"const/4 v$overrideRegister, 0x0"
|
||||||
|
)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
return PatchResultError("Can not find required instruction.")
|
||||||
|
}
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.tiktok.feedfilter.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[
|
||||||
|
Package("com.ss.android.ugc.trill"),
|
||||||
|
Package("com.zhiliaoapp.musically")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class FeedFilterCompatibility
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package app.revanced.patches.tiktok.feedfilter.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.feedfilter.annotations.FeedFilterCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("feed-api-service-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lcom/ss/android/ugc/aweme/feed/FeedApiService;",
|
||||||
|
"LIZ",
|
||||||
|
)
|
||||||
|
@FeedFilterCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object FeedApiServiceLIZFingerprint : MethodFingerprint(
|
||||||
|
access = AccessFlags.PUBLIC or AccessFlags.STATIC or AccessFlags.FINAL or AccessFlags.SYNTHETIC,
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/FeedApiService;") && methodDef.name == "LIZ"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package app.revanced.patches.tiktok.feedfilter.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstruction
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patches.tiktok.feedfilter.annotations.FeedFilterCompatibility
|
||||||
|
import app.revanced.patches.tiktok.feedfilter.fingerprints.FeedApiServiceLIZFingerprint
|
||||||
|
import app.revanced.patches.tiktok.misc.integrations.patch.TikTokIntegrationsPatch
|
||||||
|
import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsStatusLoadFingerprint
|
||||||
|
import app.revanced.patches.tiktok.misc.settings.patch.TikTokSettingsPatch
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@DependsOn([TikTokIntegrationsPatch::class, TikTokSettingsPatch::class])
|
||||||
|
@Name("tiktok-feed-filter")
|
||||||
|
@Description("Filters tiktok videos: removing ads, removing livestreams.")
|
||||||
|
@FeedFilterCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class TiktokFeedFilter : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
FeedApiServiceLIZFingerprint,
|
||||||
|
SettingsStatusLoadFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val method = FeedApiServiceLIZFingerprint.result!!.mutableMethod
|
||||||
|
for ((index, instruction) in method.implementation!!.instructions.withIndex()) {
|
||||||
|
if (instruction.opcode != Opcode.RETURN_OBJECT) continue
|
||||||
|
val feedItemsRegister = (instruction as OneRegisterInstruction).registerA
|
||||||
|
method.addInstruction(
|
||||||
|
index,
|
||||||
|
"invoke-static {v$feedItemsRegister}, Lapp/revanced/tiktok/feedfilter/FeedItemsFilter;->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V"
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
val method2 = SettingsStatusLoadFingerprint.result!!.mutableMethod
|
||||||
|
method2.addInstruction(
|
||||||
|
0,
|
||||||
|
"invoke-static {}, Lapp/revanced/tiktok/settingsmenu/SettingsStatus;->enableFeedFilter()V"
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.downloads.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[
|
||||||
|
Package("com.ss.android.ugc.trill"),
|
||||||
|
Package("com.zhiliaoapp.musically")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class DownloadsCompatibility
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.downloads.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.interaction.downloads.annotations.DownloadsCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("acl-common-share-get-code")
|
||||||
|
@MatchingMethod("ACLCommonShare", "getCode")
|
||||||
|
@DownloadsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ACLCommonShareFingerprint : MethodFingerprint(
|
||||||
|
"I",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/ACLCommonShare;") &&
|
||||||
|
methodDef.name == "getCode"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.downloads.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.interaction.downloads.annotations.DownloadsCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("acl-common-share-get-show-type")
|
||||||
|
@MatchingMethod("ACLCommonShare", "getShowType")
|
||||||
|
@DownloadsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ACLCommonShareFingerprint2 : MethodFingerprint(
|
||||||
|
"I",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/ACLCommonShare;") &&
|
||||||
|
methodDef.name == "getShowType"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.downloads.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.interaction.downloads.annotations.DownloadsCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("acl-common-share-get-transcode")
|
||||||
|
@MatchingMethod("ACLCommonShare", "getTranscode")
|
||||||
|
@DownloadsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object ACLCommonShareFingerprint3 : MethodFingerprint(
|
||||||
|
"I",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/ACLCommonShare;") &&
|
||||||
|
methodDef.name == "getTranscode"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.downloads.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.interaction.downloads.annotations.DownloadsCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("download-path-parent-fingerprint")
|
||||||
|
@MatchingMethod("LX/4bF;", "LIZ")
|
||||||
|
@DownloadsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object DownloadPathParentFingerprint : MethodFingerprint(
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
strings = listOf(
|
||||||
|
"code",
|
||||||
|
"reason",
|
||||||
|
"params insufficient"
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.downloads.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.data.impl.toMethodWalker
|
||||||
|
import app.revanced.patcher.extensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.extensions.replaceInstructions
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.tiktok.interaction.downloads.annotations.DownloadsCompatibility
|
||||||
|
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.ACLCommonShareFingerprint3
|
||||||
|
import app.revanced.patches.tiktok.interaction.downloads.fingerprints.DownloadPathParentFingerprint
|
||||||
|
import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsStatusLoadFingerprint
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.StringReference
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("tiktok-download")
|
||||||
|
@Description("Removes download restrictions and changes the default path to download to.")
|
||||||
|
@DownloadsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class DownloadsPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
ACLCommonShareFingerprint,
|
||||||
|
ACLCommonShareFingerprint2,
|
||||||
|
ACLCommonShareFingerprint3,
|
||||||
|
DownloadPathParentFingerprint,
|
||||||
|
SettingsStatusLoadFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val method1 = ACLCommonShareFingerprint.result!!.mutableMethod
|
||||||
|
method1.replaceInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const/4 v0, 0x0
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
val method2 = ACLCommonShareFingerprint2.result!!.mutableMethod
|
||||||
|
method2.replaceInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const/4 v0, 0x2
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
//Download videos without watermark.
|
||||||
|
val method3 = ACLCommonShareFingerprint3.result!!.mutableMethod
|
||||||
|
method3.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static {}, Lapp/revanced/tiktok/download/DownloadsPatch;->shouldRemoveWatermark()Z
|
||||||
|
move-result v0
|
||||||
|
if-eqz v0, :noremovewatermark
|
||||||
|
const/4 v0, 0x1
|
||||||
|
return v0
|
||||||
|
:noremovewatermark
|
||||||
|
nop
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
//Change the download path patch
|
||||||
|
val method4 = DownloadPathParentFingerprint.result!!.mutableMethod
|
||||||
|
val implementation4 = method4.implementation
|
||||||
|
val instructions = implementation4!!.instructions
|
||||||
|
var targetOffset = -1
|
||||||
|
//Search for the target method called instruction offset.
|
||||||
|
for ((index, instruction) in instructions.withIndex()) {
|
||||||
|
if (instruction.opcode != Opcode.CONST_STRING) continue
|
||||||
|
val reference = (instruction as ReferenceInstruction).reference as StringReference
|
||||||
|
if (reference.string != "video/mp4") continue
|
||||||
|
val targetInstruction = instructions[index + 1]
|
||||||
|
if (targetInstruction.opcode != Opcode.INVOKE_STATIC) continue
|
||||||
|
targetOffset = index + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (targetOffset == -1) return PatchResultError("Can not find download path uri method.")
|
||||||
|
//Change videos' download path.
|
||||||
|
val downloadUriMethod = data
|
||||||
|
.toMethodWalker(DownloadPathParentFingerprint.result!!.method)
|
||||||
|
.nextMethod(targetOffset, true)
|
||||||
|
.getMethod() as MutableMethod
|
||||||
|
downloadUriMethod.implementation!!.instructions.forEachIndexed { index, instruction ->
|
||||||
|
if (instruction.opcode == Opcode.SGET_OBJECT) {
|
||||||
|
val overrideRegister = (instruction as OneRegisterInstruction).registerA
|
||||||
|
downloadUriMethod.addInstructions(
|
||||||
|
index + 1,
|
||||||
|
"""
|
||||||
|
invoke-static {}, Lapp/revanced/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String;
|
||||||
|
move-result-object v$overrideRegister
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (instruction.opcode == Opcode.CONST_STRING) {
|
||||||
|
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||||
|
if (string.contains("/Camera")) {
|
||||||
|
val overrideRegister = (instruction as OneRegisterInstruction).registerA
|
||||||
|
val overrideString = string.replace("/Camera", "")
|
||||||
|
downloadUriMethod.replaceInstruction(
|
||||||
|
index,
|
||||||
|
"""
|
||||||
|
const-string v$overrideRegister, "$overrideString"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val method5 = SettingsStatusLoadFingerprint.result!!.mutableMethod
|
||||||
|
method5.addInstruction(
|
||||||
|
0,
|
||||||
|
"invoke-static {}, Lapp/revanced/tiktok/settingsmenu/SettingsStatus;->enableDownload()V"
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.seekbar.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[
|
||||||
|
Package("com.ss.android.ugc.trill"),
|
||||||
|
Package("com.zhiliaoapp.musically")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class SeekbarCompatibility
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.seekbar.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.interaction.seekbar.annotations.SeekbarCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("aweme-get-video-control")
|
||||||
|
@MatchingMethod("Aweme", "getVideoControl")
|
||||||
|
@SeekbarCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object AwemeGetVideoControlFingerprint : MethodFingerprint(
|
||||||
|
"L",
|
||||||
|
AccessFlags.PUBLIC.value,
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/Aweme;") && methodDef.name == "getVideoControl"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.seekbar.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultError
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patches.tiktok.interaction.seekbar.annotations.SeekbarCompatibility
|
||||||
|
import app.revanced.patches.tiktok.interaction.seekbar.fingerprints.*
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction11n
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction22c
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("tiktok-seekbar")
|
||||||
|
@Description("Show progress bar for all video.")
|
||||||
|
@SeekbarCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class TiktokSeekbarPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
AwemeGetVideoControlFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
//Get VideoControl FieldReference
|
||||||
|
val videoControl = data.findClass { it.type.endsWith("/VideoControl;") }
|
||||||
|
?: return PatchResultError("Can not find target class")
|
||||||
|
val fieldList = videoControl.immutableClass.fields.associateBy { field -> field.name }
|
||||||
|
|
||||||
|
val method = AwemeGetVideoControlFingerprint.result!!.mutableMethod
|
||||||
|
val implementation = method.implementation!!
|
||||||
|
implementation.addInstructions(
|
||||||
|
1, listOf(
|
||||||
|
BuilderInstruction11n(Opcode.CONST_4, 1, 1),
|
||||||
|
BuilderInstruction22c(Opcode.IPUT, 1, 0, fieldList["showProgressBar"]!!),
|
||||||
|
BuilderInstruction22c(Opcode.IPUT, 1, 0, fieldList["draftProgressBar"]!!)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.speed.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[
|
||||||
|
Package("com.ss.android.ugc.trill"),
|
||||||
|
Package("com.zhiliaoapp.musically")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class SpeedCompatibility
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.interaction.speed.annotations.SpeedCompatibility
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
@Name("speed-control-parent-fingerprint")
|
||||||
|
@MatchingMethod("LX/4cP;", "LJIILL")
|
||||||
|
@SpeedCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object SpeedControlParentFingerprint : MethodFingerprint(
|
||||||
|
returnType = "L",
|
||||||
|
access = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||||
|
parameters = listOf(
|
||||||
|
"L"
|
||||||
|
),
|
||||||
|
strings = listOf(
|
||||||
|
"playback_speed"
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.speed.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.data.impl.toMethodWalker
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.tiktok.interaction.speed.annotations.SpeedCompatibility
|
||||||
|
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SpeedControlParentFingerprint
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("tiktok-speed")
|
||||||
|
@Description("Enables the playback speed option for all videos.")
|
||||||
|
@SpeedCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class SpeedPatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
SpeedControlParentFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val parentMethod = SpeedControlParentFingerprint.result!!.mutableMethod
|
||||||
|
val parentMethodInstructions = parentMethod.implementation!!.instructions
|
||||||
|
for ((index, instruction) in parentMethodInstructions.withIndex()) {
|
||||||
|
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) continue
|
||||||
|
val isSpeedEnableMethod = data
|
||||||
|
.toMethodWalker(parentMethod)
|
||||||
|
.nextMethod(index, true)
|
||||||
|
.getMethod() as MutableMethod
|
||||||
|
isSpeedEnableMethod.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const/4 v0, 0x1
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.tiktok.misc.forcelogin.annotations
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[
|
||||||
|
Package("com.ss.android.ugc.trill"),
|
||||||
|
Package("com.zhiliaoapp.musically")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class DisableForceLoginCompatibility
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package app.revanced.patches.tiktok.misc.forcelogin.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.misc.forcelogin.annotations.DisableForceLoginCompatibility
|
||||||
|
|
||||||
|
@Name("mandatory-login-service-fingerprint")
|
||||||
|
@MatchingMethod("/MandatoryLoginService;", "enableForcedLogin")
|
||||||
|
@DisableForceLoginCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object MandatoryLoginServiceFingerprint : MethodFingerprint(
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/MandatoryLoginService;") &&
|
||||||
|
methodDef.name == "enableForcedLogin"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package app.revanced.patches.tiktok.misc.forcelogin.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.tiktok.misc.forcelogin.annotations.DisableForceLoginCompatibility
|
||||||
|
|
||||||
|
@Name("mandatory-login-service-fingerprint2")
|
||||||
|
@MatchingMethod("/MandatoryLoginService;", "shouldShowForcedLogin")
|
||||||
|
@DisableForceLoginCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object MandatoryLoginServiceFingerprint2 : MethodFingerprint(
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("/MandatoryLoginService;") &&
|
||||||
|
methodDef.name == "shouldShowForcedLogin"
|
||||||
|
}
|
||||||
|
)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user