1
mirror of https://github.com/revanced/revanced-integrations synced 2025-11-21 18:35:37 +01:00

Compare commits

...

102 Commits

Author SHA1 Message Date
semantic-release-bot
78b5fe2128 chore(release): 0.119.0-dev.5 [skip ci]
# [0.119.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v0.119.0-dev.4...v0.119.0-dev.5) (2023-10-07)

### Features

* **YouTube - Return YouTube Dislike:** Support version `18.37.36` ([#490](https://github.com/ReVanced/revanced-integrations/issues/490)) ([245c3b3](245c3b3537))
2023-10-07 09:35:05 +00:00
oSumAtrIX
245c3b3537 feat(YouTube - Return YouTube Dislike): Support version 18.37.36 (#490)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2023-10-07 13:31:25 +04:00
semantic-release-bot
d4b859d6fb chore(release): 0.119.0-dev.4 [skip ci]
# [0.119.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.119.0-dev.3...v0.119.0-dev.4) (2023-10-07)

### Features

* **YouTube:** Add `Disable fine scrubbing gesture` patch ([4498f39](4498f39b8c))
2023-10-07 01:54:40 +00:00
oSumAtrIX
4498f39b8c feat(YouTube): Add Disable fine scrubbing gesture patch 2023-10-07 03:50:41 +02:00
semantic-release-bot
176196b52c chore(release): 0.119.0-dev.3 [skip ci]
# [0.119.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.119.0-dev.2...v0.119.0-dev.3) (2023-10-06)

### Bug Fixes

* **YouTube - Hide shorts components:** Add filter to filter group list ([30788ba](30788ba1a3))
2023-10-06 10:42:02 +00:00
oSumAtrIX
30788ba1a3 fix(YouTube - Hide shorts components): Add filter to filter group list 2023-10-06 12:38:05 +02:00
semantic-release-bot
1d13cf2d5d chore(release): 0.119.0-dev.2 [skip ci]
# [0.119.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.119.0-dev.1...v0.119.0-dev.2) (2023-10-06)

### Bug Fixes

* **YouTube - Hide shorts components:** Do not prevent filtering components ([6eb3017](6eb301776e))
2023-10-06 00:40:20 +00:00
oSumAtrIX
6eb301776e fix(YouTube - Hide shorts components): Do not prevent filtering components
Previously, the exceptions group prevented filtering the "Notify me" button and in feed surveys.
2023-10-06 02:36:38 +02:00
semantic-release-bot
ed6571734e chore(release): 0.119.0-dev.1 [skip ci]
# [0.119.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.118.1-dev.1...v0.119.0-dev.1) (2023-10-05)

### Features

* **YouTube - Hide shorts components:** Hide subscribe button when paused separately from subscribe button ([3ac869f](3ac869fa6a))
2023-10-05 16:53:23 +00:00
oSumAtrIX
3ac869fa6a feat(YouTube - Hide shorts components): Hide subscribe button when paused separately from subscribe button 2023-10-05 18:48:53 +02:00
semantic-release-bot
d47fd19d0b chore(release): 0.118.1-dev.1 [skip ci]
## [0.118.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0...v0.118.1-dev.1) (2023-10-05)

### Bug Fixes

* **YouTube - Hide shorts components:** Hide the subscribe button when paused ([6730aaf](6730aaf2b8))
2023-10-05 02:44:51 +00:00
oSumAtrIX
6730aaf2b8 fix(YouTube - Hide shorts components): Hide the subscribe button when paused 2023-10-05 03:50:05 +02:00
semantic-release-bot
1efd5c8315 chore(release): 0.118.0 [skip ci]
# [0.118.0](https://github.com/ReVanced/revanced-integrations/compare/v0.117.1...v0.118.0) (2023-10-04)

### Bug Fixes

* Do not always hide the component ([3d0fc1d](3d0fc1d610))
* Remove parameter from route ([4b0925e](4b0925e337))
* **YouTube - Client spoof:** Display seekbar thumbnails in high quality ([f71c1a0](f71c1a0c15))
* **YouTube - Client spoof:** Do not record feed videos to history by default ([#478](https://github.com/ReVanced/revanced-integrations/issues/478)) ([ef1cca0](ef1cca02c1))
* **YouTube - Client spoof:** fix occasionally frozen video playback ([#486](https://github.com/ReVanced/revanced-integrations/issues/486)) ([b0b6ff6](b0b6ff6a82))
* **YouTube - Client spoof:** fix storyboard fetched out of order ([#481](https://github.com/ReVanced/revanced-integrations/issues/481)) ([8398774](83987747e6))
* **YouTube - Client spoof:** Fix toast shown for live streams ([#489](https://github.com/ReVanced/revanced-integrations/issues/489)) ([27f49df](27f49dfd1e))
* **YouTube - Client spoof:** fix toast shown if opening paid or age restricted video ([#482](https://github.com/ReVanced/revanced-integrations/issues/482)) ([e72b65b](e72b65b599))
* **YouTube - Client spoof:** Removed unused code ([#480](https://github.com/ReVanced/revanced-integrations/issues/480)) ([e6903bf](e6903bff95))
* **YouTube - Client spoof:** Restore clipping videos functionality ([2cd1738](2cd1738d24))
* **YouTube - Client spoof:** Restore seekbar thumbnails ([978f630](978f630c02))
* **YouTube - Client spoof:** Show seekbar thumbnail for age restricted and paid videos ([01019b0](01019b09c1))
* **YouTube - Custom filter:** Use new lines between components instead of commas ([#475](https://github.com/ReVanced/revanced-integrations/issues/475)) ([17ed396](17ed396739))
* **YouTube - Hide info cards:** Fix info cards not hiding for some users ([#487](https://github.com/ReVanced/revanced-integrations/issues/487)) ([00c4c40](00c4c4025b))
* **YouTube - Hide layout components:** Always hide redundant 'player audio track' button ([#473](https://github.com/ReVanced/revanced-integrations/issues/473)) ([d86851b](d86851baf1))
* **YouTube - Hide layout components:** Do not hide chapters in feed unexpectedly ([bedb02e](bedb02e4f6))
* **YouTube - Hide shorts components:** Hide subscribe button in paused state ([9685070](9685070eda))
* **YouTube - ReturnYouTubeDislike:** Add debug logging to litho text ([#476](https://github.com/ReVanced/revanced-integrations/issues/476)) ([e3b8e8b](e3b8e8be41))
* **YouTube - ReturnYouTubeDislike:** Revert support for 18.37.36 ([#488](https://github.com/ReVanced/revanced-integrations/issues/488)) ([165b061](165b061fa9))
* **YouTube - SponsorBlock:** Adjust import/export UI text ([#491](https://github.com/ReVanced/revanced-integrations/issues/491)) ([4215be4](4215be4250))
* **YouTube - Video Id:** Fix video id not showing the currently playing video ([#484](https://github.com/ReVanced/revanced-integrations/issues/484)) ([da923a3](da923a38a0))
* **YouTube:** fix old quality and custom speed not working on tablets ([#477](https://github.com/ReVanced/revanced-integrations/issues/477)) ([2352fa5](2352fa5426))

### Features

* **TU Dortmund:** Add `Show on lockscreen` patch ([#472](https://github.com/ReVanced/revanced-integrations/issues/472)) ([526d66f](526d66f6a9))
* **Twitch - Block embedded ads:** Switch from `ttv.lol` to `luminous.dev` ([2c34180](2c3418041c))
* **YouTube - Hide layout components:** Disable hiding search result shelf header by default ([b280de3](b280de3195))
* **YouTube - Hide layout components:** Hide "Join" button ([e225468](e2254681cd))
* **YouTube - Hide layout components:** Hide "Notify me" button ([b87d806](b87d806659))
* **YouTube - Hide layout components:** Hide search result shelf header ([93a3045](93a30453d9))
* **YouTube - Hide layout components:** Hide timed reactions ([b472aee](b472aeeed7))
* **YouTube:** Add `Bypass URL redirects` patch ([9109653](91096532ee))
* **YouTube:** Bump compatibility to `18.37.36` ([#483](https://github.com/ReVanced/revanced-integrations/issues/483)) ([5dadb0d](5dadb0d523))

### Performance Improvements

* Only request required fields ([d20b768](d20b768bc2))
* Remove unnecessary api key parameter ([ba5e7d8](ba5e7d870e))
2023-10-04 23:41:56 +00:00
oSumAtrIX
63ddb0b31d chore: Merge branch dev to main (#474) 2023-10-05 01:37:48 +02:00
semantic-release-bot
1a702bce60 chore(release): 0.118.0-dev.24 [skip ci]
# [0.118.0-dev.24](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.23...v0.118.0-dev.24) (2023-10-03)

### Bug Fixes

* **YouTube - Hide layout components:** Do not hide chapters in feed unexpectedly ([bedb02e](bedb02e4f6))
2023-10-03 01:32:28 +00:00
oSumAtrIX
bedb02e4f6 fix(YouTube - Hide layout components): Do not hide chapters in feed unexpectedly 2023-10-03 03:28:28 +02:00
semantic-release-bot
6075c9ed17 chore(release): 0.118.0-dev.23 [skip ci]
# [0.118.0-dev.23](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.22...v0.118.0-dev.23) (2023-10-02)

### Bug Fixes

* **YouTube - SponsorBlock:** Adjust import/export UI text ([#491](https://github.com/ReVanced/revanced-integrations/issues/491)) ([4215be4](4215be4250))
2023-10-02 21:31:31 +00:00
LisoUseInAIKyrios
4215be4250 fix(YouTube - SponsorBlock): Adjust import/export UI text (#491) 2023-10-03 01:27:27 +04:00
semantic-release-bot
a198ef83dd chore(release): 0.118.0-dev.22 [skip ci]
# [0.118.0-dev.22](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.21...v0.118.0-dev.22) (2023-10-02)

### Bug Fixes

* Do not always hide the component ([3d0fc1d](3d0fc1d610))

### Features

* **YouTube - Hide layout components:** Disable hiding search result shelf header by default ([b280de3](b280de3195))
2023-10-02 20:20:31 +00:00
oSumAtrIX
b280de3195 feat(YouTube - Hide layout components): Disable hiding search result shelf header by default
The reason for this is that it also hides Shorts shelves unintentionally.
2023-10-02 22:16:06 +02:00
oSumAtrIX
3d0fc1d610 fix: Do not always hide the component 2023-10-02 22:15:29 +02:00
oSumAtrIX
25f73eb3a9 refactor: Simplify filtering a component 2023-10-02 22:05:31 +02:00
semantic-release-bot
f451d67dcb chore(release): 0.118.0-dev.21 [skip ci]
# [0.118.0-dev.21](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.20...v0.118.0-dev.21) (2023-10-02)

### Features

* **YouTube - Hide layout components:** Hide search result shelf header ([93a3045](93a30453d9))
2023-10-02 14:29:22 +00:00
oSumAtrIX
5185673780 refactor: Move classes to correct path 2023-10-02 16:25:12 +02:00
oSumAtrIX
93a30453d9 feat(YouTube - Hide layout components): Hide search result shelf header 2023-10-02 16:25:11 +02:00
semantic-release-bot
d156951460 chore(release): 0.118.0-dev.20 [skip ci]
# [0.118.0-dev.20](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.19...v0.118.0-dev.20) (2023-10-01)

### Bug Fixes

* **YouTube - Hide shorts components:** Hide subscribe button in paused state ([9685070](9685070eda))
2023-10-01 22:54:38 +00:00
oSumAtrIX
9685070eda fix(YouTube - Hide shorts components): Hide subscribe button in paused state 2023-10-02 00:50:18 +02:00
Temm
1689bf4125 refactor(Tumblr): Use a common filter patch (#479)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-10-01 05:12:00 +02:00
semantic-release-bot
c992289d59 chore(release): 0.118.0-dev.19 [skip ci]
# [0.118.0-dev.19](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.18...v0.118.0-dev.19) (2023-10-01)

### Features

* **YouTube - Hide layout components:** Hide "Join" button ([e225468](e2254681cd))
* **YouTube - Hide layout components:** Hide "Notify me" button ([b87d806](b87d806659))
* **YouTube - Hide layout components:** Hide timed reactions ([b472aee](b472aeeed7))
2023-10-01 03:11:33 +00:00
oSumAtrIX
e2254681cd feat(YouTube - Hide layout components): Hide "Join" button 2023-10-01 05:07:47 +02:00
oSumAtrIX
b87d806659 feat(YouTube - Hide layout components): Hide "Notify me" button 2023-10-01 05:07:47 +02:00
oSumAtrIX
b472aeeed7 feat(YouTube - Hide layout components): Hide timed reactions 2023-10-01 05:07:46 +02:00
semantic-release-bot
7dfa0e4653 chore(release): 0.118.0-dev.18 [skip ci]
# [0.118.0-dev.18](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.17...v0.118.0-dev.18) (2023-09-28)

### Bug Fixes

* **YouTube - ReturnYouTubeDislike:** Revert support for 18.37.36 ([#488](https://github.com/ReVanced/revanced-integrations/issues/488)) ([165b061](165b061fa9))
2023-09-28 13:42:52 +00:00
LisoUseInAIKyrios
165b061fa9 fix(YouTube - ReturnYouTubeDislike): Revert support for 18.37.36 (#488) 2023-09-28 17:39:11 +04:00
semantic-release-bot
78bea48e40 chore(release): 0.118.0-dev.17 [skip ci]
# [0.118.0-dev.17](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.16...v0.118.0-dev.17) (2023-09-28)

### Bug Fixes

* **YouTube - Client spoof:** Fix toast shown for live streams ([#489](https://github.com/ReVanced/revanced-integrations/issues/489)) ([27f49df](27f49dfd1e))
* **YouTube - Video Id:** Fix video id not showing the currently playing video ([#484](https://github.com/ReVanced/revanced-integrations/issues/484)) ([da923a3](da923a38a0))
2023-09-28 13:16:02 +00:00
LisoUseInAIKyrios
27f49dfd1e fix(YouTube - Client spoof): Fix toast shown for live streams (#489) 2023-09-28 17:12:14 +04:00
LisoUseInAIKyrios
da923a38a0 fix(YouTube - Video Id): Fix video id not showing the currently playing video (#484)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-09-28 15:11:54 +02:00
semantic-release-bot
4b256f501b chore(release): 0.118.0-dev.16 [skip ci]
# [0.118.0-dev.16](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.15...v0.118.0-dev.16) (2023-09-28)

### Features

* **YouTube:** Add `Bypass URL redirects` patch ([9109653](91096532ee))
2023-09-28 07:52:02 +00:00
LisoUseInAIKyrios
e7e02e1e30 chore: fix build 2023-09-28 11:48:27 +04:00
nullptr
cc416bc4f6 chore: Log hiding mix playlists (#485)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-09-28 04:49:12 +02:00
oSumAtrIX
91096532ee feat(YouTube): Add Bypass URL redirects patch 2023-09-28 04:47:40 +02:00
semantic-release-bot
444b80026d chore(release): 0.118.0-dev.15 [skip ci]
# [0.118.0-dev.15](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.14...v0.118.0-dev.15) (2023-09-27)

### Features

* **YouTube:** Bump compatibility to `18.37.36` ([#483](https://github.com/ReVanced/revanced-integrations/issues/483)) ([5dadb0d](5dadb0d523))
2023-09-27 23:32:17 +00:00
oSumAtrIX
5dadb0d523 feat(YouTube): Bump compatibility to 18.37.36 (#483)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2023-09-28 01:28:34 +02:00
semantic-release-bot
1e6fabceea chore(release): 0.118.0-dev.14 [skip ci]
# [0.118.0-dev.14](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.13...v0.118.0-dev.14) (2023-09-27)

### Bug Fixes

* **YouTube - Hide info cards:** Fix info cards not hiding for some users ([#487](https://github.com/ReVanced/revanced-integrations/issues/487)) ([00c4c40](00c4c4025b))
2023-09-27 20:24:38 +00:00
LisoUseInAIKyrios
00c4c4025b fix(YouTube - Hide info cards): Fix info cards not hiding for some users (#487) 2023-09-28 00:18:21 +04:00
oSumAtrIX
a01db14c1e ci: Bump checkout action 2023-09-27 18:03:11 +02:00
semantic-release-bot
67ada816d4 chore(release): 0.118.0-dev.13 [skip ci]
# [0.118.0-dev.13](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.12...v0.118.0-dev.13) (2023-09-27)

### Bug Fixes

* **YouTube - Client spoof:** fix occasionally frozen video playback ([#486](https://github.com/ReVanced/revanced-integrations/issues/486)) ([b0b6ff6](b0b6ff6a82))
2023-09-27 14:59:03 +00:00
LisoUseInAIKyrios
b0b6ff6a82 fix(YouTube - Client spoof): fix occasionally frozen video playback (#486) 2023-09-27 18:54:47 +04:00
LisoUseInAIKyrios
ab9989d41f chore(YouTube - Client spoof): add logging. fix fall over logic if android client call fails 2023-09-26 13:05:25 +04:00
semantic-release-bot
09e8b3e345 chore(release): 0.118.0-dev.12 [skip ci]
# [0.118.0-dev.12](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.11...v0.118.0-dev.12) (2023-09-26)

### Bug Fixes

* **YouTube - Client spoof:** Show seekbar thumbnail for age restricted and paid videos ([01019b0](01019b09c1))
2023-09-26 02:53:16 +00:00
oSumAtrIX
01019b09c1 fix(YouTube - Client spoof): Show seekbar thumbnail for age restricted and paid videos 2023-09-26 04:45:48 +02:00
semantic-release-bot
62f92c38c1 chore(release): 0.118.0-dev.11 [skip ci]
# [0.118.0-dev.11](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.10...v0.118.0-dev.11) (2023-09-26)

### Bug Fixes

* **YouTube - Client spoof:** fix toast shown if opening paid or age restricted video ([#482](https://github.com/ReVanced/revanced-integrations/issues/482)) ([e72b65b](e72b65b599))
2023-09-26 01:43:27 +00:00
LisoUseInAIKyrios
e72b65b599 fix(YouTube - Client spoof): fix toast shown if opening paid or age restricted video (#482) 2023-09-26 05:39:44 +04:00
semantic-release-bot
245265587a chore(release): 0.118.0-dev.10 [skip ci]
# [0.118.0-dev.10](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.9...v0.118.0-dev.10) (2023-09-26)

### Bug Fixes

* **YouTube - Client spoof:** fix storyboard fetched out of order ([#481](https://github.com/ReVanced/revanced-integrations/issues/481)) ([8398774](83987747e6))
2023-09-26 01:13:00 +00:00
LisoUseInAIKyrios
83987747e6 fix(YouTube - Client spoof): fix storyboard fetched out of order (#481) 2023-09-26 05:09:01 +04:00
semantic-release-bot
a591c62543 chore(release): 0.118.0-dev.9 [skip ci]
# [0.118.0-dev.9](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.8...v0.118.0-dev.9) (2023-09-25)

### Bug Fixes

* **YouTube - Client spoof:** Removed unused code ([#480](https://github.com/ReVanced/revanced-integrations/issues/480)) ([e6903bf](e6903bff95))
2023-09-25 22:50:24 +00:00
LisoUseInAIKyrios
e6903bff95 fix(YouTube - Client spoof): Removed unused code (#480)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-09-26 00:47:01 +02:00
semantic-release-bot
89993619fd chore(release): 0.118.0-dev.8 [skip ci]
# [0.118.0-dev.8](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.7...v0.118.0-dev.8) (2023-09-25)

### Bug Fixes

* **YouTube - Client spoof:** Display seekbar thumbnails in high quality ([f71c1a0](f71c1a0c15))
2023-09-25 21:55:32 +00:00
oSumAtrIX
f71c1a0c15 fix(YouTube - Client spoof): Display seekbar thumbnails in high quality 2023-09-25 23:51:03 +02:00
semantic-release-bot
b76794b08c chore(release): 0.118.0-dev.7 [skip ci]
# [0.118.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.6...v0.118.0-dev.7) (2023-09-25)

### Bug Fixes

* Remove parameter from route ([4b0925e](4b0925e337))
2023-09-25 19:52:03 +00:00
oSumAtrIX
4b0925e337 fix: Remove parameter from route 2023-09-25 21:46:20 +02:00
semantic-release-bot
aa0282f300 chore(release): 0.118.0-dev.6 [skip ci]
# [0.118.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.5...v0.118.0-dev.6) (2023-09-25)

### Performance Improvements

* Only request required fields ([d20b768](d20b768bc2))
* Remove unnecessary api key parameter ([ba5e7d8](ba5e7d870e))
2023-09-25 18:12:35 +00:00
oSumAtrIX
ba5e7d870e perf: Remove unnecessary api key parameter 2023-09-25 19:57:53 +02:00
oSumAtrIX
d20b768bc2 perf: Only request required fields 2023-09-25 19:52:27 +02:00
semantic-release-bot
a113905e5c chore(release): 0.118.0-dev.5 [skip ci]
# [0.118.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.4...v0.118.0-dev.5) (2023-09-25)

### Bug Fixes

* **YouTube - Client spoof:** Restore clipping videos functionality ([2cd1738](2cd1738d24))
* **YouTube - Client spoof:** Restore seekbar thumbnails ([978f630](978f630c02))
2023-09-25 14:36:47 +00:00
oSumAtrIX
2cd1738d24 fix(YouTube - Client spoof): Restore clipping videos functionality 2023-09-25 16:33:06 +02:00
oSumAtrIX
978f630c02 fix(YouTube - Client spoof): Restore seekbar thumbnails
This works around the issue, but causes seekbar thumbnails to be in low quality.
2023-09-25 16:28:23 +02:00
semantic-release-bot
bfae6b56ab chore(release): 0.118.0-dev.4 [skip ci]
# [0.118.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.3...v0.118.0-dev.4) (2023-09-23)

### Bug Fixes

* **YouTube - Client spoof:** Do not record feed videos to history by default ([#478](https://github.com/ReVanced/revanced-integrations/issues/478)) ([ef1cca0](ef1cca02c1))
2023-09-23 21:53:51 +00:00
oSumAtrIX
ef1cca02c1 fix(YouTube - Client spoof): Do not record feed videos to history by default (#478) 2023-09-23 23:48:53 +02:00
oSumAtrIX
b193b3dbc1 ci: Use better workflow name and PR message 2023-09-23 18:34:16 +02:00
semantic-release-bot
9272df52f3 chore(release): 0.118.0-dev.3 [skip ci]
# [0.118.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.2...v0.118.0-dev.3) (2023-09-19)

### Bug Fixes

* **YouTube:** fix old quality and custom speed not working on tablets ([#477](https://github.com/ReVanced/revanced-integrations/issues/477)) ([2352fa5](2352fa5426))
2023-09-19 02:26:25 +00:00
LisoUseInAIKyrios
2352fa5426 fix(YouTube): fix old quality and custom speed not working on tablets (#477) 2023-09-19 06:22:15 +04:00
semantic-release-bot
aa10de41b6 chore(release): 0.118.0-dev.2 [skip ci]
# [0.118.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.118.0-dev.1...v0.118.0-dev.2) (2023-09-14)

### Features

* **Twitch - Block embedded ads:** Switch from `ttv.lol` to `luminous.dev` ([2c34180](2c3418041c))
2023-09-14 21:26:51 +00:00
oSumAtrIX
2c3418041c feat(Twitch - Block embedded ads): Switch from ttv.lol to luminous.dev 2023-09-14 21:27:33 +02:00
oSumAtrIX
4a242c7a91 refactor: Use try block for auto closeable 2023-09-14 16:57:50 +02:00
semantic-release-bot
7b524efdbd chore(release): 0.118.0-dev.1 [skip ci]
# [0.118.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.117.2-dev.3...v0.118.0-dev.1) (2023-09-14)

### Features

* **TU Dortmund:** Add `Show on lockscreen` patch ([#472](https://github.com/ReVanced/revanced-integrations/issues/472)) ([526d66f](526d66f6a9))
2023-09-14 01:15:52 +00:00
Traktores
526d66f6a9 feat(TU Dortmund): Add Show on lockscreen patch (#472)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-09-14 03:11:49 +02:00
semantic-release-bot
f0bfcef0d7 chore(release): 0.117.2-dev.3 [skip ci]
## [0.117.2-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.117.2-dev.2...v0.117.2-dev.3) (2023-09-09)

### Bug Fixes

* **YouTube - ReturnYouTubeDislike:** Add debug logging to litho text ([#476](https://github.com/ReVanced/revanced-integrations/issues/476)) ([e3b8e8b](e3b8e8be41))
2023-09-09 08:56:11 +00:00
LisoUseInAIKyrios
e3b8e8be41 fix(YouTube - ReturnYouTubeDislike): Add debug logging to litho text (#476) 2023-09-09 12:52:47 +04:00
semantic-release-bot
acf7e2d1dd chore(release): 0.117.2-dev.2 [skip ci]
## [0.117.2-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.117.2-dev.1...v0.117.2-dev.2) (2023-09-07)

### Bug Fixes

* **YouTube - Custom filter:** Use new lines between components instead of commas ([#475](https://github.com/ReVanced/revanced-integrations/issues/475)) ([17ed396](17ed396739))
2023-09-07 20:15:50 +00:00
LisoUseInAIKyrios
17ed396739 fix(YouTube - Custom filter): Use new lines between components instead of commas (#475) 2023-09-08 00:10:38 +04:00
semantic-release-bot
830cfb561f chore(release): 0.117.2-dev.1 [skip ci]
## [0.117.2-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.117.1...v0.117.2-dev.1) (2023-09-07)

### Bug Fixes

* **YouTube - Hide layout components:** Always hide redundant 'player audio track' button ([#473](https://github.com/ReVanced/revanced-integrations/issues/473)) ([d86851b](d86851baf1))
2023-09-07 06:44:50 +00:00
LisoUseInAIKyrios
d86851baf1 fix(YouTube - Hide layout components): Always hide redundant 'player audio track' button (#473) 2023-09-07 10:41:06 +04:00
semantic-release-bot
c034d474ff chore(release): 0.117.1 [skip ci]
## [0.117.1](https://github.com/ReVanced/revanced-integrations/compare/v0.117.0...v0.117.1) (2023-09-03)

### Bug Fixes

* **YouTube - ExternalDownloads:** Trim whitespace from package name ([#469](https://github.com/ReVanced/revanced-integrations/issues/469)) ([61d997e](61d997e1db))
2023-09-03 03:44:54 +00:00
oSumAtrIX
690fec6b5a chore: merge branch dev to main (#470) 2023-09-03 05:41:02 +02:00
semantic-release-bot
20e2ecf199 chore(release): 0.117.1-dev.1 [skip ci]
## [0.117.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.117.0...v0.117.1-dev.1) (2023-08-30)

### Bug Fixes

* **YouTube - ExternalDownloads:** Trim whitespace from package name ([#469](https://github.com/ReVanced/revanced-integrations/issues/469)) ([61d997e](61d997e1db))
2023-08-30 21:11:04 +00:00
Autist69420
61d997e1db fix(YouTube - ExternalDownloads): Trim whitespace from package name (#469)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-08-30 23:06:47 +02:00
semantic-release-bot
04c81b39e9 chore(release): 0.117.0 [skip ci]
# [0.117.0](https://github.com/ReVanced/revanced-integrations/compare/v0.116.2...v0.117.0) (2023-08-27)

### Features

* Restore previous release ([dc955d1](dc955d1bc2))
2023-08-27 23:57:50 +00:00
oSumAtrIX
b7a176ff3e chore: merge branch dev to main (#468) 2023-08-28 01:53:07 +02:00
semantic-release-bot
1afd520c7b chore(release): 0.117.0-dev.1 [skip ci]
# [0.117.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.116.2...v0.117.0-dev.1) (2023-08-27)

### Features

* Restore previous release ([dc955d1](dc955d1bc2))
2023-08-27 20:30:34 +00:00
oSumAtrIX
dc955d1bc2 feat: Restore previous release 2023-08-27 21:41:23 +02:00
semantic-release-bot
f3fc0d1f7d chore(release): 0.116.2 [skip ci]
## [0.116.2](https://github.com/ReVanced/revanced-integrations/compare/v0.116.1...v0.116.2) (2023-08-27)

### Bug Fixes

* Add back missing class ([6e8d13b](6e8d13bfbb))
2023-08-27 03:21:10 +00:00
oSumAtrIX
ca4927e2a9 chore: merge branch dev to main (#467) 2023-08-27 05:17:10 +02:00
oSumAtrIX
6e8d13bfbb fix: Add back missing class 2023-08-27 05:16:07 +02:00
semantic-release-bot
90a7e604d3 chore(release): 0.116.1 [skip ci]
## [0.116.1](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0...v0.116.1) (2023-08-27)

### Bug Fixes

* Revert previous release ([a178a22](a178a223c2))
2023-08-27 02:51:33 +00:00
oSumAtrIX
6801ed8d28 chore: merge branch dev to main (#466) 2023-08-27 04:47:35 +02:00
semantic-release-bot
ba8f4f9b5f chore(release): 0.116.1-dev.1 [skip ci]
## [0.116.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0...v0.116.1-dev.1) (2023-08-27)

### Bug Fixes

* Revert previous release ([a178a22](a178a223c2))
2023-08-27 02:47:15 +00:00
oSumAtrIX
a178a223c2 fix: Revert previous release 2023-08-27 04:43:10 +02:00
semantic-release-bot
fb3b7b4c80 chore(release): 0.116.0 [skip ci]
# [0.116.0](https://github.com/ReVanced/revanced-integrations/compare/v0.115.1...v0.116.0) (2023-08-26)

### Bug Fixes

* use correct logging tags ([420b926](420b9263b6))
* **YouTube - Hide Shorts components:** Hide `shorts header` ([#455](https://github.com/ReVanced/revanced-integrations/issues/455)) ([13afac9](13afac906a))
* **YouTube - Litho Filter:** Don't remove the buffer until the thread stops ([#461](https://github.com/ReVanced/revanced-integrations/issues/461)) ([2188607](2188607340))
* **YouTube - Player Flyout Menu:** Change 'audio track flyout menu' to show up by default ([#458](https://github.com/ReVanced/revanced-integrations/issues/458)) ([97880ea](97880eaf72))

### Features

* **YouTube - Debug:** Logging of layout proto buffer strings ([#456](https://github.com/ReVanced/revanced-integrations/issues/456)) ([769a200](769a2006ce))
* **YouTube - Hide video action buttons:** Hide individual action buttons ([#451](https://github.com/ReVanced/revanced-integrations/issues/451)) ([8e5ca65](8e5ca65286))
* **YouTube:** Add `Alternative thumbnails` patch ([#462](https://github.com/ReVanced/revanced-integrations/issues/462)) ([d354356](d354356b2d))
* **YouTube:** Add `Custom player overlay opacity` patch ([#449](https://github.com/ReVanced/revanced-integrations/issues/449)) ([44dccb3](44dccb3d65))
* **YouTube:** Add `Enable tablet layout` patch ([d4570de](d4570de0a7))
* **YouTube:** Support version 18.32.39 ([#464](https://github.com/ReVanced/revanced-integrations/issues/464)) ([aa9d892](aa9d892cde))
2023-08-26 23:25:40 +00:00
oSumAtrIX
297d76f176 chore: merge branch dev to main (#457) 2023-08-27 01:21:37 +02:00
semantic-release-bot
acae4b1bc8 chore(release): 0.116.0-dev.10 [skip ci]
# [0.116.0-dev.10](https://github.com/ReVanced/revanced-integrations/compare/v0.116.0-dev.9...v0.116.0-dev.10) (2023-08-26)

### Features

* **YouTube:** Add `Enable tablet layout` patch ([d4570de](d4570de0a7))
2023-08-26 15:14:13 +00:00
oSumAtrIX
d4570de0a7 feat(YouTube): Add Enable tablet layout patch 2023-08-26 17:10:35 +02:00
43 changed files with 1990 additions and 847 deletions

View File

@@ -1,4 +1,4 @@
name: PR to main
name: Open a PR to main
on:
push:
@@ -7,7 +7,7 @@ on:
workflow_dispatch:
env:
MESSAGE: merge branch `${{ github.head_ref || github.ref_name }}` to `main`
MESSAGE: Merge branch `${{ github.head_ref || github.ref_name }}` to `main`
jobs:
pull-request:
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Open pull request
uses: repo-sync/pull-request@v2
with:

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Make sure the release step uses its own credentials:
# https://github.com/cycjimmy/semantic-release-action#private-packages

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
package app.revanced.integrations.patches;
import android.net.Uri;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
public class BypassURLRedirectsPatch {
private static final String YOUTUBE_REDIRECT_PATH = "/redirect";
/**
* Convert the YouTube redirect URI string to the redirect query URI.
*
* @param uri The YouTube redirect URI string.
* @return The redirect query URI.
*/
public static Uri parseRedirectUri(String uri) {
final var parsed = Uri.parse(uri);
if (SettingsEnum.BYPASS_URL_REDIRECTS.getBoolean() && parsed.getPath().equals(YOUTUBE_REDIRECT_PATH)) {
var query = Uri.parse(Uri.decode(parsed.getQueryParameter("q")));
LogHelper.printDebug(() -> "Bypassing YouTube redirect URI: " + query);
return query;
}
return parsed;
}
}

View File

@@ -0,0 +1,18 @@
package app.revanced.integrations.patches;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import app.revanced.integrations.settings.SettingsEnum;
public final class DisableFineScrubbingGesturePatch {
/**
* Disables the fine scrubbing gesture.
* @param tracker The velocity tracker that is used to determine the gesture.
* @param event The motion event that is used to determine the gesture.
*/
public static void disableGesture(VelocityTracker tracker, MotionEvent event) {
if (SettingsEnum.DISABLE_FINE_SCRUBBING_GESTURE.getBoolean()) return;
tracker.addMovement(event);
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.integrations.patches;
import app.revanced.integrations.settings.SettingsEnum;
public final class EnableTabletLayoutPatch {
public static boolean enableTabletLayout() {
return SettingsEnum.TABLET_LAYOUT.getBoolean();
}
}

View File

@@ -1,95 +0,0 @@
package app.revanced.integrations.patches;
import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.shared.PlayerType;
import app.revanced.integrations.utils.LogHelper;
public class SpoofSignatureVerificationPatch {
/**
* Enable/disable all workarounds that are required due to signature spoofing.
*/
private static final boolean WORKAROUND = true;
/**
* Protobuf parameters used for autoplay in scrim.
* Prepend this parameter to mute video playback (for autoplay in feed)
*/
private static final String PROTOBUF_PARAMETER_SCRIM = "SAFgAXgB";
/**
* Protobuf parameter also used by
* <a href="https://github.com/yt-dlp/yt-dlp/blob/81ca451480051d7ce1a31c017e005358345a9149/yt_dlp/extractor/youtube.py#L3602">yt-dlp</a>
* <br>
* Known issue: captions are positioned on upper area in the player.
*/
private static final String PROTOBUF_PLAYER_PARAMS = "CgIQBg==";
/**
* Target Protobuf parameters.
*/
private static final String[] PROTOBUF_PARAMETER_TARGETS = {
"YAHI", // Autoplay in feed
"SAFg" // Autoplay in scrim
};
/**
* Injection point.
*
* @param originalValue originalValue protobuf parameter
*/
public static String overrideProtobufParameter(String originalValue) {
try {
if (!SettingsEnum.SPOOF_SIGNATURE_VERIFICATION.getBoolean()) {
return originalValue;
}
LogHelper.printDebug(() -> "Original protobuf parameter value: " + originalValue);
if (!WORKAROUND) return PROTOBUF_PLAYER_PARAMS;
var isPlayingVideo = originalValue.contains(PROTOBUF_PLAYER_PARAMS);
if (isPlayingVideo) return originalValue;
boolean isPlayingFeed = containsAny(originalValue, PROTOBUF_PARAMETER_TARGETS) && PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL;
if (isPlayingFeed) {
// Videos in feed won't autoplay with sound.
return PROTOBUF_PARAMETER_SCRIM + PROTOBUF_PLAYER_PARAMS;
} else {
// Spoof the parameter to prevent playback issues.
return PROTOBUF_PLAYER_PARAMS;
}
} catch (Exception ex) {
LogHelper.printException(() -> "overrideProtobufParameter failure", ex);
}
return originalValue;
}
/**
* Injection point.
*/
public static boolean getSeekbarThumbnailOverrideValue() {
return SettingsEnum.SPOOF_SIGNATURE_VERIFICATION.getBoolean();
}
/**
* Injection point.
*
* @param view seekbar thumbnail view. Includes both shorts and regular videos.
*/
public static void seekbarImageViewCreated(ImageView view) {
if (SettingsEnum.SPOOF_SIGNATURE_VERIFICATION.getBoolean()) {
view.setVisibility(View.GONE);
// Also hide the border around the thumbnail (otherwise a 1 pixel wide bordered frame is visible).
ViewGroup parentLayout = (ViewGroup) view.getParent();
parentLayout.setPadding(0, 0, 0, 0);
}
}
}

View File

@@ -1,16 +1,15 @@
package app.revanced.integrations.patches;
import androidx.annotation.NonNull;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.Objects;
import app.revanced.integrations.patches.playback.speed.RememberPlaybackSpeedPatch;
import app.revanced.integrations.shared.VideoState;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* Hooking class for the current playing video.
*/
@@ -25,6 +24,10 @@ public final class VideoInformation {
private static String videoId = "";
private static long videoLength = 0;
private static long videoTime = -1;
@NonNull
private static volatile String playerResponseVideoId = "";
/**
* The current playback speed
*/
@@ -61,6 +64,18 @@ public final class VideoInformation {
}
}
/**
* Injection point. Called off the main thread.
*
* @param videoId The id of the last video loaded.
*/
public static void setPlayerResponseVideoId(@NonNull String videoId) {
if (!playerResponseVideoId.equals(videoId)) {
LogHelper.printDebug(() -> "New player response video id: " + videoId);
playerResponseVideoId = videoId;
}
}
/**
* Injection point.
* Called when user selects a playback speed.
@@ -141,6 +156,22 @@ public final class VideoInformation {
return videoId;
}
/**
* Differs from {@link #videoId} as this is the video id for the
* last player response received, which may not be the current video playing.
*
* If Shorts are loading the background, this commonly will be
* different from the Short that is currently on screen.
*
* For most use cases, you should instead use {@link #getVideoId()}.
*
* @return The id of the last video loaded. Empty string if not set yet.
*/
@NonNull
public static String getPlayerResponseVideoId() {
return playerResponseVideoId;
}
/**
* @return The current playback speed.
*/

View File

@@ -0,0 +1,15 @@
package app.revanced.integrations.patches.components;
import app.revanced.integrations.settings.SettingsEnum;
public final class HideInfoCardsFilterPatch extends Filter {
public HideInfoCardsFilterPatch() {
identifierFilterGroupList.addAll(
new StringFilterGroup(
SettingsEnum.HIDE_INFO_CARDS,
"info_card_teaser_overlay.eml"
)
);
}
}

View File

@@ -7,6 +7,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.StringTrieSearch;
@RequiresApi(api = Build.VERSION_CODES.N)
@@ -18,14 +19,17 @@ public final class LayoutComponentsFilter extends Filter {
SettingsEnum.HIDE_MIX_PLAYLISTS,
"&list="
);
private final StringFilterGroup searchResultShelfHeader;
private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup notifyMe;
@RequiresApi(api = Build.VERSION_CODES.N)
public LayoutComponentsFilter() {
exceptions.addPatterns(
"home_video_with_context",
"related_video_with_context",
"comment_thread", // skip filtering anything in the comments
"|comment.", // skip filtering anything in the comments replies
"comment_thread", // Whitelist comments
"|comment.", // Whitelist comment replies
"library_recent_shelf"
);
@@ -60,7 +64,7 @@ public final class LayoutComponentsFilter extends Filter {
"compact_banner"
);
final var inFeedSurvey = new StringFilterGroup(
inFeedSurvey = new StringFilterGroup(
SettingsEnum.HIDE_FEED_SURVEY,
"in_feed_survey",
"slimline_survey"
@@ -92,8 +96,16 @@ public final class LayoutComponentsFilter extends Filter {
"channel_guidelines_entry_banner"
);
// The player audio track button does the exact same function as the audio track flyout menu option.
// But if the copy url button is shown, these button clashes and the the audio button does not work.
// Previously this was a setting to show/hide the player button.
// But it was decided it's simpler to always hide this button because:
// - it doesn't work with copy video url feature
// - the button is rare
// - always hiding makes the ReVanced settings simpler and easier to understand
// - nobody is going to notice the redundant button is always hidden
final var audioTrackButton = new StringFilterGroup(
SettingsEnum.HIDE_AUDIO_TRACK_BUTTON,
null,
"multi_feed_icon_button"
);
@@ -137,6 +149,27 @@ public final class LayoutComponentsFilter extends Filter {
"cell_divider" // layout residue (gray line above the buttoned ad),
);
final var timedReactions = new StringFilterGroup(
SettingsEnum.HIDE_TIMED_REACTIONS,
"emoji_control_panel",
"timed_reaction"
);
searchResultShelfHeader = new StringFilterGroup(
SettingsEnum.HIDE_SEARCH_RESULT_SHELF_HEADER,
"shelf_header.eml"
);
notifyMe = new StringFilterGroup(
SettingsEnum.HIDE_NOTIFY_ME_BUTTON,
"set_reminder_button"
);
final var joinMembership = new StringFilterGroup(
SettingsEnum.HIDE_JOIN_MEMBERSHIP_BUTTON,
"compact_sponsor_button"
);
final var chipsShelf = new StringFilterGroup(
SettingsEnum.HIDE_CHIPS_SHELF,
"chips_shelf"
@@ -147,36 +180,45 @@ public final class LayoutComponentsFilter extends Filter {
communityPosts,
paidContent,
latestPosts,
chapters,
communityGuidelines,
quickActions,
expandableMetadata,
relatedVideos,
compactBanner,
inFeedSurvey,
joinMembership,
medicalPanel,
notifyMe,
infoPanel,
subscribersCommunityGuidelines,
channelGuidelines,
audioTrackButton,
artistCard,
timedReactions,
imageShelf,
subscribersCommunityGuidelines,
channelMemberShelf,
custom
);
this.identifierFilterGroupList.addAll(
graySeparator,
chipsShelf
chipsShelf,
chapters
);
}
@Override
public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey) return true;
if (matchedGroup != custom && exceptions.matches(path))
return false; // Exceptions are not filtered.
// TODO: This also hides the feed Shorts shelf header
if (matchedGroup == searchResultShelfHeader && matchedIndex != 0) return false;
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
}
@@ -187,6 +229,11 @@ public final class LayoutComponentsFilter extends Filter {
* Called from a different place then the other filters.
*/
public static boolean filterMixPlaylists(final byte[] bytes) {
return mixPlaylists.check(bytes).isFiltered();
final boolean isMixPlaylistFiltered = mixPlaylists.check(bytes).isFiltered();
if (isMixPlaylistFiltered)
LogHelper.printDebug(() -> "Filtered mix playlist");
return isMixPlaylistFiltered;
}
}

View File

@@ -4,6 +4,7 @@ import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.*;
@@ -13,12 +14,24 @@ import java.util.function.Consumer;
abstract class FilterGroup<T> {
final static class FilterGroupResult {
SettingsEnum setting;
boolean filtered;
private SettingsEnum setting;
private int matchedIndex;
private int matchedLength;
// In the future it might be useful to include which pattern matched,
// but for now that is not needed.
FilterGroupResult(SettingsEnum setting, boolean filtered) {
FilterGroupResult() {
this(null, -1, 0);
}
FilterGroupResult(SettingsEnum setting, int matchedIndex, int matchedLength) {
setValues(setting, matchedIndex, matchedLength);
}
public void setValues(SettingsEnum setting, int matchedIndex, int matchedLength) {
this.setting = setting;
this.filtered = filtered;
this.matchedIndex = matchedIndex;
this.matchedLength = matchedLength;
}
/**
@@ -30,7 +43,21 @@ abstract class FilterGroup<T> {
}
public boolean isFiltered() {
return filtered;
return matchedIndex >= 0;
}
/**
* Matched index of first pattern that matched, or -1 if nothing matched.
*/
public int getMatchedIndex() {
return matchedIndex;
}
/**
* Length of the matched filter pattern.
*/
public int getMatchedLength() {
return matchedLength;
}
}
@@ -81,14 +108,28 @@ class StringFilterGroup extends FilterGroup<String> {
@Override
public FilterGroupResult check(final String string) {
return new FilterGroupResult(setting, isEnabled() && ReVancedUtils.containsAny(string, filters));
int matchedIndex = -1;
int matchedLength = 0;
if (isEnabled()) {
for (String pattern : filters) {
if (!string.isEmpty()) {
final int indexOf = pattern.indexOf(string);
if (indexOf >= 0) {
matchedIndex = indexOf;
matchedLength = pattern.length();
break;
}
}
}
}
return new FilterGroupResult(setting, matchedIndex, matchedLength);
}
}
final class CustomFilterGroup extends StringFilterGroup {
public CustomFilterGroup(final SettingsEnum setting, final SettingsEnum filter) {
super(setting, filter.getString().split(","));
super(setting, filter.getString().split("\\s+"));
}
}
@@ -155,19 +196,22 @@ class ByteArrayFilterGroup extends FilterGroup<byte[]> {
@Override
public FilterGroupResult check(final byte[] bytes) {
var matched = false;
int matchedLength = 0;
int matchedIndex = -1;
if (isEnabled()) {
if (failurePatterns == null) {
buildFailurePatterns(); // Lazy load.
}
for (int i = 0, length = filters.length; i < length; i++) {
if (indexOf(bytes, filters[i], failurePatterns[i]) >= 0) {
matched = true;
byte[] filter = filters[i];
matchedIndex = indexOf(bytes, filter, failurePatterns[i]);
if (matchedIndex >= 0) {
matchedLength = filter.length;
break;
}
}
}
return new FilterGroupResult(setting, matched);
return new FilterGroupResult(setting, matchedIndex, matchedLength);
}
}
@@ -204,11 +248,10 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
continue;
}
for (V pattern : group.filters) {
search.addPattern(pattern, (textSearched, matchedStartIndex, callbackParameter) -> {
search.addPattern(pattern, (textSearched, matchedStartIndex, matchedLength, callbackParameter) -> {
if (group.isEnabled()) {
FilterGroup.FilterGroupResult result = (FilterGroup.FilterGroupResult) callbackParameter;
result.setting = group.setting;
result.filtered = true;
result.setValues(group.setting, matchedStartIndex, matchedLength);
return true;
}
return false;
@@ -241,9 +284,10 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
if (search == null) {
buildSearch(); // Lazy load.
}
FilterGroup.FilterGroupResult result = new FilterGroup.FilterGroupResult(null, false);
FilterGroup.FilterGroupResult result = new FilterGroup.FilterGroupResult();
search.matches(stack, result);
return result;
}
protected abstract TrieSearch<V> createSearchGraph();
@@ -292,10 +336,10 @@ abstract class Filter {
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
if (SettingsEnum.DEBUG.getBoolean()) {
if (pathFilterGroupList == matchedList) {
LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered path: " + path);
} else if (identifierFilterGroupList == matchedList) {
if (matchedList == identifierFilterGroupList) {
LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered identifier: " + identifier);
} else {
LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered path: " + path);
}
}
return true;
@@ -399,7 +443,7 @@ public final class LithoFilterPatch {
continue;
}
for (T pattern : group.filters) {
pathSearchTree.addPattern(pattern, (textSearched, matchedStartIndex, callbackParameter) -> {
pathSearchTree.addPattern(pattern, (textSearched, matchedStartIndex, matchedLength, callbackParameter) -> {
if (!group.isEnabled()) return false;
LithoFilterParameters parameters = (LithoFilterParameters) callbackParameter;
return filter.isFiltered(parameters.identifier, parameters.path, parameters.protoBuffer,

View File

@@ -0,0 +1,76 @@
package app.revanced.integrations.patches.components;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.nio.charset.StandardCharsets;
import app.revanced.integrations.patches.ReturnYouTubeDislikePatch;
import app.revanced.integrations.settings.SettingsEnum;
@RequiresApi(api = Build.VERSION_CODES.N)
public final class ReturnYouTubeDislikeFilterPatch extends Filter {
private final ByteArrayFilterGroupList videoIdFilterGroup = new ByteArrayFilterGroupList();
public ReturnYouTubeDislikeFilterPatch() {
pathFilterGroupList.addAll(
new StringFilterGroup(SettingsEnum.RYD_SHORTS, "|shorts_dislike_button.eml|")
);
// After the dislikes icon name is some binary data and then the video id for that specific short.
videoIdFilterGroup.addAll(
// Video was previously disliked before video was opened.
new ByteArrayAsStringFilterGroup(null, "ic_right_dislike_on_shadowed"),
// Video was not already disliked.
new ByteArrayAsStringFilterGroup(null, "ic_right_dislike_off_shadowed")
);
}
@Override
public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
FilterGroup.FilterGroupResult result = videoIdFilterGroup.check(protobufBufferArray);
if (result.isFiltered()) {
// The video length must be hard coded to 11, as there is additional ASCII text that
// appears immediately after the id if the dislike button is already selected.
final int videoIdLength = 11;
final int subStringSearchStartIndex = result.getMatchedIndex() + result.getMatchedLength();
String videoId = findSubString(protobufBufferArray, subStringSearchStartIndex, videoIdLength);
if (videoId != null) {
ReturnYouTubeDislikePatch.newVideoLoaded(videoId, true);
}
}
return false;
}
/**
* Find an exact length ASCII substring starting from a given index.
*
* Similar to the String finding code in {@link LithoFilterPatch},
* but refactoring it to also handle this use case became messy and overly complicated.
*/
@Nullable
private static String findSubString(byte[] buffer, int bufferStartIndex, int subStringLength) {
// Valid ASCII values (ignore control characters).
final int minimumAscii = 32; // 32 = space character
final int maximumAscii = 126; // 127 = delete character
final int bufferLength = buffer.length;
int start = bufferStartIndex;
int end = bufferStartIndex;
do {
final int value = buffer[end];
if (value < minimumAscii || value > maximumAscii) {
start = end + 1;
} else if (end - start == subStringLength) {
return new String(buffer, start, subStringLength, StandardCharsets.US_ASCII);
}
end++;
} while (end < bufferLength);
return null;
}
}

View File

@@ -1,31 +1,31 @@
package app.revanced.integrations.patches.components;
import android.os.Build;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.integrations.settings.SettingsEnum;
import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar;
import static app.revanced.integrations.utils.ReVancedUtils.hideViewBy1dpUnderCondition;
import static app.revanced.integrations.utils.ReVancedUtils.hideViewUnderCondition;
import android.view.View;
import androidx.annotation.Nullable;
import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar;
import app.revanced.integrations.settings.SettingsEnum;
@RequiresApi(api = Build.VERSION_CODES.N)
public final class ShortsFilter extends Filter {
private static final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar.eml";
public static PivotBar pivotBar; // Set by patch.
private final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar.eml";
private final StringFilterGroup channelBar;
private final StringFilterGroup subscribeButton;
private final StringFilterGroup subscribeButtonPaused;
private final StringFilterGroup soundButton;
private final StringFilterGroup infoPanel;
private final StringFilterGroup shortsShelfHeader;
private final StringFilterGroup shelfHeader;
private final StringFilterGroup videoActionButton;
private final ByteArrayFilterGroupList videoActionButtonGroupList = new ByteArrayFilterGroupList();
public ShortsFilter() {
// Home / subscription feed components.
var thanksButton = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_THANKS_BUTTON,
"suggested_action"
);
var shorts = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS,
"shorts_shelf",
@@ -33,37 +33,80 @@ public final class ShortsFilter extends Filter {
"shorts_grid",
"shorts_video_cell",
"shorts_pivot_item"
);
// Feed Shorts shelf header.
// Use a different filter group for this pattern, as it requires an additional check after matching.
shortsShelfHeader = new StringFilterGroup(
shelfHeader = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS,
"shelf_header.eml"
);
identifierFilterGroupList.addAll(shorts, shortsShelfHeader, thanksButton);
// Home / subscription feed components.
var thanksButton = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_THANKS_BUTTON,
"suggested_action"
);
identifierFilterGroupList.addAll(shorts, shelfHeader, thanksButton);
// Shorts player components.
var joinButton = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_JOIN_BUTTON,
"sponsor_button"
);
var subscribeButton = new StringFilterGroup(
subscribeButton = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_SUBSCRIBE_BUTTON,
"subscribe_button"
);
subscribeButtonPaused = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_SUBSCRIBE_BUTTON_PAUSED,
"shorts_paused_state"
);
channelBar = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_CHANNEL_BAR,
REEL_CHANNEL_BAR_PATH
);
soundButton = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_SOUND_BUTTON,
"reel_pivot_button"
);
infoPanel = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_INFO_PANEL,
"shorts_info_panel_overview"
);
pathFilterGroupList.addAll(joinButton, subscribeButton, channelBar, soundButton, infoPanel);
videoActionButton = new StringFilterGroup(
null,
"ContainerType|shorts_video_action_button"
);
pathFilterGroupList.addAll(
joinButton, subscribeButton, subscribeButtonPaused,
channelBar, soundButton, infoPanel, videoActionButton
);
var shortsCommentButton = new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_SHORTS_COMMENTS_BUTTON,
"reel_comment_button"
);
var shortsShareButton = new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_SHORTS_SHARE_BUTTON,
"reel_share_button"
);
var shortsRemixButton = new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_SHORTS_REMIX_BUTTON,
"reel_remix_button"
);
videoActionButtonGroupList.addAll(shortsCommentButton, shortsShareButton, shortsRemixButton);
}
@Override
@@ -71,28 +114,39 @@ public final class ShortsFilter extends Filter {
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
if (matchedList == pathFilterGroupList) {
// Always filter if matched.
if (matchedGroup == soundButton || matchedGroup == infoPanel || matchedGroup == channelBar)
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
if (matchedGroup == soundButton ||
matchedGroup == infoPanel ||
matchedGroup == channelBar ||
matchedGroup == subscribeButton ||
matchedGroup == subscribeButtonPaused
) return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
// Video action buttons (comment, share, remix) have the same path.
if (matchedGroup == videoActionButton) {
if (videoActionButtonGroupList.check(protobufBufferArray).isFiltered())
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
return false;
}
// Filter other path groups from pathFilterGroupList, only when reelChannelBar is visible
// to avoid false positives.
if (!path.startsWith(REEL_CHANNEL_BAR_PATH))
return false;
} else if (matchedGroup == shortsShelfHeader) {
} else if (matchedGroup == shelfHeader) {
// Because the header is used in watch history and possibly other places, check for the index,
// which is 0 when the shelf header is used for Shorts.
if (matchedIndex != 0) return false;
}
// Super class handles logging.
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
}
public static void hideShortsShelf(final View shortsShelfView) {
hideViewBy1dpUnderCondition(SettingsEnum.HIDE_SHORTS, shortsShelfView);
}
// Additional components that have to be hidden by setting their visibility
// region Hide the buttons in older versions of YouTube. New versions use Litho.
public static void hideShortsCommentsButton(final View commentsButtonView) {
hideViewUnderCondition(SettingsEnum.HIDE_SHORTS_COMMENTS_BUTTON, commentsButtonView);
@@ -106,6 +160,8 @@ public final class ShortsFilter extends Filter {
hideViewUnderCondition(SettingsEnum.HIDE_SHORTS_SHARE_BUTTON, shareButtonView);
}
// endregion
public static void hideNavigationBar() {
if (!SettingsEnum.HIDE_SHORTS_NAVIGATION_BAR.getBoolean()) return;
if (pivotBar == null) return;

View File

@@ -3,71 +3,44 @@ package app.revanced.integrations.patches.playback.quality;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.LinearLayout;
import android.widget.ListView;
import androidx.annotation.NonNull;
import app.revanced.integrations.patches.components.VideoQualityMenuFilterPatch;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
import com.facebook.litho.ComponentHost;
import kotlin.Deprecated;
// This patch contains the logic to show the old video quality menu.
// Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
// and a ListView in the old one.
/**
* This patch contains the logic to show the old video quality menu.
* Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
* and a ListView in the old one.
*/
public final class OldVideoQualityMenuPatch {
public static void onFlyoutMenuCreate(final LinearLayout linearLayout) {
/**
* Injection point.
*/
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
if (!SettingsEnum.SHOW_OLD_VIDEO_QUALITY_MENU.getBoolean()) return;
// The quality menu is a RecyclerView with 3 children. The third child is the "Advanced" quality menu.
addRecyclerListener(linearLayout, 3, 2, recyclerView -> {
// Check if the current view is the quality menu.
if (VideoQualityMenuFilterPatch.isVideoQualityMenuVisible) {
VideoQualityMenuFilterPatch.isVideoQualityMenuVisible = false;
linearLayout.setVisibility(View.GONE);
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
try {
// Check if the current view is the quality menu.
if (VideoQualityMenuFilterPatch.isVideoQualityMenuVisible) {
VideoQualityMenuFilterPatch.isVideoQualityMenuVisible = false;
((ViewGroup) recyclerView.getParent().getParent().getParent()).setVisibility(View.GONE);
// Click the "Advanced" quality menu to show the "old" quality menu.
((ComponentHost) recyclerView.getChildAt(0)).getChildAt(3).performClick();
LogHelper.printDebug(() -> "Advanced quality menu in new type of quality menu clicked");
// Click the "Advanced" quality menu to show the "old" quality menu.
((ViewGroup) recyclerView.getChildAt(0)).getChildAt(3).performClick();
}
} catch (Exception ex) {
LogHelper.printException(() -> "onFlyoutMenuCreate failure", ex);
}
});
}
public static void addRecyclerListener(@NonNull LinearLayout linearLayout,
int expectedLayoutChildCount, int recyclerViewIndex,
@NonNull RecyclerViewGlobalLayoutListener listener) {
if (linearLayout.getChildCount() != expectedLayoutChildCount) return;
var layoutChild = linearLayout.getChildAt(recyclerViewIndex);
if (!(layoutChild instanceof RecyclerView)) return;
final var recyclerView = (RecyclerView) layoutChild;
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
try {
listener.recyclerOnGlobalLayout(recyclerView);
} catch (Exception ex) {
LogHelper.printException(() -> "addRecyclerListener failure", ex);
} finally {
// Remove the listener because it will be added again.
recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
}
);
}
public interface RecyclerViewGlobalLayoutListener {
void recyclerOnGlobalLayout(@NonNull RecyclerView recyclerView);
}
@Deprecated(message = "This patch is deprecated because the quality menu is not a ListView anymore")
/**
* Injection point. Only used if spoofing to an old app version.
*/
public static void showOldVideoQualityMenu(final ListView listView) {
if (!SettingsEnum.SHOW_OLD_VIDEO_QUALITY_MENU.getBoolean()) return;

View File

@@ -1,23 +1,23 @@
package app.revanced.integrations.patches.playback.speed;
import android.preference.ListPreference;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import java.util.Arrays;
import app.revanced.integrations.patches.components.PlaybackSpeedMenuFilterPatch;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
import com.facebook.litho.ComponentHost;
import java.util.Arrays;
import static app.revanced.integrations.patches.playback.quality.OldVideoQualityMenuPatch.addRecyclerListener;
public class CustomPlaybackSpeedPatch {
/**
* Maximum playback speed, exclusive value. Custom speeds must be less than this value.
* Limit is required otherwise double digit speeds show up out of order in the UI selector.
*/
public static final float MAXIMUM_PLAYBACK_SPEED = 10;
@@ -26,16 +26,6 @@ public class CustomPlaybackSpeedPatch {
*/
public static float[] customPlaybackSpeeds;
/**
* Minimum value of {@link #customPlaybackSpeeds}
*/
public static float minPlaybackSpeed;
/**
* Maxium value of {@link #customPlaybackSpeeds}
*/
public static float maxPlaybackSpeed;
/**
* PreferenceList entries and values, of all available playback speeds.
*/
@@ -69,8 +59,6 @@ public class CustomPlaybackSpeedPatch {
loadCustomSpeeds();
return;
}
minPlaybackSpeed = Math.min(minPlaybackSpeed, speed);
maxPlaybackSpeed = Math.max(maxPlaybackSpeed, speed);
customPlaybackSpeeds[i] = speed;
}
} catch (Exception ex) {
@@ -106,25 +94,37 @@ public class CustomPlaybackSpeedPatch {
preference.setEntryValues(preferenceListEntryValues);
}
/*
* To reduce copy and paste between two similar code paths.
/**
* Injection point.
*/
public static void onFlyoutMenuCreate(final LinearLayout linearLayout) {
// The playback rate menu is a RecyclerView with 2 children. The third child is the "Advanced" quality menu.
addRecyclerListener(linearLayout, 2, 1, recyclerView -> {
if (PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible) {
PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
try {
// For some reason, the custom playback speed flyout panel is activated when the user opens the share panel. (A/B tests)
// Check the child count of playback speed flyout panel to prevent this issue.
// Child count of playback speed flyout panel is always 8.
if (PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible
&& ((ViewGroup) recyclerView.getChildAt(0)).getChildCount() == 8) {
PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
ViewGroup parentView3rd = (ViewGroup) recyclerView.getParent().getParent().getParent();
ViewGroup parentView4th = (ViewGroup) parentView3rd.getParent();
if (recyclerView.getChildCount() == 1 && recyclerView.getChildAt(0) instanceof ComponentHost) {
linearLayout.setVisibility(View.GONE);
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
// This only shows in phone layout.
parentView4th.getChildAt(0).performClick();
// Close the new Playback speed menu and instead show the old one.
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
parentView3rd.setVisibility(View.GONE);
parentView4th.setVisibility(View.GONE);
// This works without issues for both tablet and phone layouts,
// So no code is needed to check whether the current device is a tablet or phone.
// Close the new Playback speed menu and show the old one.
showOldPlaybackSpeedMenu();
// DismissView [R.id.touch_outside] is the 1st ChildView of the 3rd ParentView.
((ViewGroup) linearLayout.getParent().getParent().getParent())
.getChildAt(0).performClick();
}
} catch (Exception ex) {
LogHelper.printException(() -> "onFlyoutMenuCreate failure", ex);
}
});
}

View File

@@ -1,4 +1,4 @@
package app.revanced.integrations.patches;
package app.revanced.integrations.patches.spoof;
import app.revanced.integrations.settings.SettingsEnum;

View File

@@ -0,0 +1,153 @@
package app.revanced.integrations.patches.spoof;
import static app.revanced.integrations.patches.spoof.requests.StoryboardRendererRequester.getStoryboardRenderer;
import static app.revanced.integrations.utils.ReVancedUtils.containsAny;
import androidx.annotation.Nullable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import app.revanced.integrations.patches.VideoInformation;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.shared.PlayerType;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
/** @noinspection unused*/
public class SpoofSignaturePatch {
/**
* Parameter (also used by
* <a href="https://github.com/yt-dlp/yt-dlp/blob/81ca451480051d7ce1a31c017e005358345a9149/yt_dlp/extractor/youtube.py#L3602">yt-dlp</a>)
* to fix playback issues.
*/
private static final String INCOGNITO_PARAMETERS = "CgIQBg==";
/**
* Parameters causing playback issues.
*/
private static final String[] AUTOPLAY_PARAMETERS = {
"YAHI", // Autoplay in feed.
"SAFg" // Autoplay in scrim.
};
/**
* Parameter used for autoplay in scrim.
* Prepend this parameter to mute video playback (for autoplay in feed).
*/
private static final String SCRIM_PARAMETER = "SAFgAXgB";
/**
* Parameters used in YouTube Shorts.
*/
private static final String SHORTS_PLAYER_PARAMETERS = "8AEB";
/**
* Last video id loaded. Used to prevent reloading the same spec multiple times.
*/
private static volatile String lastPlayerResponseVideoId;
private static volatile Future<StoryboardRenderer> rendererFuture;
@Nullable
private static StoryboardRenderer getRenderer() {
if (rendererFuture != null) {
try {
return rendererFuture.get(5000, TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
LogHelper.printDebug(() -> "Could not get renderer (get timed out)");
} catch (ExecutionException | InterruptedException ex) {
// Should never happen.
LogHelper.printException(() -> "Could not get renderer", ex);
}
}
return null;
}
/**
* Injection point.
*
* Called off the main thread, and called multiple times for each video.
*
* @param parameters Original protobuf parameter value.
*/
public static String spoofParameter(String parameters) {
LogHelper.printDebug(() -> "Original protobuf parameter value: " + parameters);
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()) return parameters;
// Clip's player parameters contain a lot of information (e.g. video start and end time or whether it loops)
// For this reason, the player parameters of a clip are usually very long (150~300 characters).
// Clips are 60 seconds or less in length, so no spoofing.
var isClip = parameters.length() > 150;
if (isClip) return parameters;
// Shorts do not need to be spoofed.
if (parameters.startsWith(SHORTS_PLAYER_PARAMETERS)) return parameters;
boolean isPlayingFeed = PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL && containsAny(parameters, AUTOPLAY_PARAMETERS);
if (isPlayingFeed) return SettingsEnum.SPOOF_SIGNATURE_IN_FEED.getBoolean() ?
// Prepend the scrim parameter to mute videos in feed.
SCRIM_PARAMETER + INCOGNITO_PARAMETERS :
// In order to prevent videos that are auto-played in feed to be added to history,
// only spoof the parameter if the video is not playing in the feed.
// This will cause playback issues in the feed, but it's better than manipulating the history.
parameters;
fetchStoryboardRenderer();
return INCOGNITO_PARAMETERS;
}
private static void fetchStoryboardRenderer() {
String videoId = VideoInformation.getPlayerResponseVideoId();
if (!videoId.equals(lastPlayerResponseVideoId)) {
rendererFuture = ReVancedUtils.submitOnBackgroundThread(() -> getStoryboardRenderer(videoId));
lastPlayerResponseVideoId = videoId;
}
// Block until the fetch is completed. Without this, occasionally when a new video is opened
// the video will be frozen a few seconds while the audio plays.
// This is because the main thread is calling to get the storyboard but the fetch is not completed.
// To prevent this, call get() here and block until the fetch is completed.
// So later when the main thread calls to get the renderer it will never block as the future is done.
getRenderer();
}
/**
* Injection point.
*/
public static boolean getSeekbarThumbnailOverrideValue() {
return SettingsEnum.SPOOF_SIGNATURE.getBoolean();
}
/**
* Injection point.
* Called from background threads and from the main thread.
*/
@Nullable
public static String getStoryboardRendererSpec(String originalStoryboardRendererSpec) {
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean()) {
StoryboardRenderer renderer = getRenderer();
if (renderer != null) return renderer.getSpec();
}
return originalStoryboardRendererSpec;
}
/**
* Injection point.
*/
public static int getRecommendedLevel(int originalLevel) {
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean()) {
StoryboardRenderer renderer = getRenderer();
if (renderer != null) {
Integer recommendedLevel = renderer.getRecommendedLevel();
if (recommendedLevel != null) return recommendedLevel;
}
}
return originalLevel;
}
}

View File

@@ -0,0 +1,39 @@
package app.revanced.integrations.patches.spoof;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
public final class StoryboardRenderer {
private final String spec;
@Nullable
private final Integer recommendedLevel;
public StoryboardRenderer(String spec, @Nullable Integer recommendedLevel) {
this.spec = spec;
this.recommendedLevel = recommendedLevel;
}
@NonNull
public String getSpec() {
return spec;
}
/**
* @return Recommended image quality level, or NULL if no recommendation exists.
*/
@Nullable
public Integer getRecommendedLevel() {
return recommendedLevel;
}
@NotNull
@Override
public String toString() {
return "StoryboardRenderer{" +
"spec='" + spec + '\'' +
", recommendedLevel=" + recommendedLevel +
'}';
}
}

View File

@@ -0,0 +1,89 @@
package app.revanced.integrations.patches.spoof.requests;
import app.revanced.integrations.requests.Requester;
import app.revanced.integrations.requests.Route;
import app.revanced.integrations.utils.LogHelper;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
final class PlayerRoutes {
private static final String YT_API_URL = "https://www.youtube.com/youtubei/v1/";
static final Route.CompiledRoute GET_STORYBOARD_SPEC_RENDERER = new Route(
Route.Method.POST,
"player" +
"?fields=storyboards.playerStoryboardSpecRenderer," +
"storyboards.playerLiveStoryboardSpecRenderer," +
"playabilityStatus.status"
).compile();
static final String ANDROID_INNER_TUBE_BODY;
static final String TV_EMBED_INNER_TUBE_BODY;
static {
JSONObject innerTubeBody = new JSONObject();
try {
JSONObject context = new JSONObject();
JSONObject client = new JSONObject();
client.put("clientName", "ANDROID");
client.put("clientVersion", "18.37.36");
client.put("androidSdkVersion", 34);
context.put("client", client);
innerTubeBody.put("context", context);
innerTubeBody.put("videoId", "%s");
} catch (JSONException e) {
LogHelper.printException(() -> "Failed to create innerTubeBody", e);
}
ANDROID_INNER_TUBE_BODY = innerTubeBody.toString();
JSONObject tvEmbedInnerTubeBody = new JSONObject();
try {
JSONObject context = new JSONObject();
JSONObject client = new JSONObject();
client.put("clientName", "TVHTML5_SIMPLY_EMBEDDED_PLAYER");
client.put("clientVersion", "2.0");
client.put("platform", "TV");
client.put("clientScreen", "EMBED");
JSONObject thirdParty = new JSONObject();
thirdParty.put("embedUrl", "https://www.youtube.com/watch?v=%s");
context.put("thirdParty", thirdParty);
context.put("client", client);
tvEmbedInnerTubeBody.put("context", context);
tvEmbedInnerTubeBody.put("videoId", "%s");
} catch (JSONException e) {
LogHelper.printException(() -> "Failed to create tvEmbedInnerTubeBody", e);
}
TV_EMBED_INNER_TUBE_BODY = tvEmbedInnerTubeBody.toString();
}
private PlayerRoutes() {
}
/** @noinspection SameParameterValue*/
static HttpURLConnection getPlayerResponseConnectionFromRoute(Route.CompiledRoute route) throws IOException {
var connection = Requester.getConnectionFromCompiledRoute(YT_API_URL, route);
connection.setRequestProperty("User-Agent", "com.google.android.youtube/18.37.36 (Linux; U; Android 12; GB) gzip");
connection.setRequestProperty("X-Goog-Api-Format-Version", "2");
connection.setRequestProperty("Content-Type", "application/json");
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
return connection;
}
}

View File

@@ -0,0 +1,119 @@
package app.revanced.integrations.patches.spoof.requests;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.integrations.patches.spoof.StoryboardRenderer;
import app.revanced.integrations.requests.Requester;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import static app.revanced.integrations.patches.spoof.requests.PlayerRoutes.*;
public class StoryboardRendererRequester {
private StoryboardRendererRequester() {
}
@Nullable
private static JSONObject fetchPlayerResponse(@NonNull String requestBody) {
try {
ReVancedUtils.verifyOffMainThread();
Objects.requireNonNull(requestBody);
final byte[] innerTubeBody = requestBody.getBytes(StandardCharsets.UTF_8);
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STORYBOARD_SPEC_RENDERER);
connection.getOutputStream().write(innerTubeBody, 0, innerTubeBody.length);
final int responseCode = connection.getResponseCode();
if (responseCode == 200) return Requester.parseJSONObject(connection);
LogHelper.printException(() -> "API not available: " + responseCode);
connection.disconnect();
} catch (SocketTimeoutException ex) {
LogHelper.printException(() -> "API timed out", ex);
} catch (Exception ex) {
LogHelper.printException(() -> "Failed to fetch storyboard URL", ex);
}
return null;
}
private static boolean isPlayabilityStatusOk(@NonNull JSONObject playerResponse) {
try {
return playerResponse.getJSONObject("playabilityStatus").getString("status").equals("OK");
} catch (JSONException e) {
LogHelper.printDebug(() -> "Failed to get playabilityStatus for response: " + playerResponse);
}
return false;
}
/**
* Fetches the storyboardRenderer from the innerTubeBody.
* @param innerTubeBody The innerTubeBody to use to fetch the storyboardRenderer.
* @return StoryboardRenderer or null if playabilityStatus is not OK.
*/
@Nullable
private static StoryboardRenderer getStoryboardRendererUsingBody(@NonNull String innerTubeBody) {
final JSONObject playerResponse = fetchPlayerResponse(innerTubeBody);
if (playerResponse != null && isPlayabilityStatusOk(playerResponse))
return getStoryboardRendererUsingResponse(playerResponse);
return null;
}
@Nullable
private static StoryboardRenderer getStoryboardRendererUsingResponse(@NonNull JSONObject playerResponse) {
try {
final JSONObject storyboards = playerResponse.getJSONObject("storyboards");
final String storyboardsRendererTag = storyboards.has("playerLiveStoryboardSpecRenderer")
? "playerLiveStoryboardSpecRenderer"
: "playerStoryboardSpecRenderer";
final var rendererElement = storyboards.getJSONObject(storyboardsRendererTag);
StoryboardRenderer renderer = new StoryboardRenderer(
rendererElement.getString("spec"),
rendererElement.has("recommendedLevel")
? rendererElement.getInt("recommendedLevel")
: null
);
LogHelper.printDebug(() -> "Fetched: " + renderer);
return renderer;
} catch (JSONException e) {
LogHelper.printException(() -> "Failed to get storyboardRenderer", e);
}
return null;
}
@Nullable
public static StoryboardRenderer getStoryboardRenderer(@NonNull String videoId) {
try {
Objects.requireNonNull(videoId);
var renderer = getStoryboardRendererUsingBody(String.format(ANDROID_INNER_TUBE_BODY, videoId));
if (renderer == null) {
LogHelper.printDebug(() -> videoId + " not available using Android client");
renderer = getStoryboardRendererUsingBody(String.format(TV_EMBED_INNER_TUBE_BODY, videoId, videoId));
if (renderer == null) {
LogHelper.printDebug(() -> videoId + " not available using TV embedded client");
}
}
return renderer;
} catch (Exception ex) {
LogHelper.printException(() -> "Failed to fetch storyboard URL", ex);
}
return null;
}
}

View File

@@ -16,7 +16,11 @@ public class Requester {
}
public static HttpURLConnection getConnectionFromRoute(String apiUrl, Route route, String... params) throws IOException {
String url = apiUrl + route.compile(params).getCompiledRoute();
return getConnectionFromCompiledRoute(apiUrl, route.compile(params));
}
public static HttpURLConnection getConnectionFromCompiledRoute(String apiUrl, Route.CompiledRoute route) throws IOException {
String url = apiUrl + route.getCompiledRoute();
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod(route.getMethod().name());
connection.setRequestProperty("User-agent", System.getProperty("http.agent") + ";revanced");

View File

@@ -7,8 +7,6 @@ import androidx.annotation.NonNull;
import org.json.JSONException;
import org.json.JSONObject;
import app.revanced.integrations.utils.LogHelper;
/**
* ReturnYouTubeDislike API estimated like/dislike/view counts.
*
@@ -81,17 +79,21 @@ public final class RYDVoteData {
}
public void updateUsingVote(Vote vote) {
if (vote == Vote.LIKE) {
likeCount = fetchedLikeCount + 1;
dislikeCount = fetchedDislikeCount;
} else if (vote == Vote.DISLIKE) {
likeCount = fetchedLikeCount;
dislikeCount = fetchedDislikeCount + 1;
} else if (vote == Vote.LIKE_REMOVE) {
likeCount = fetchedLikeCount;
dislikeCount = fetchedDislikeCount;
} else {
throw new IllegalStateException();
switch (vote) {
case LIKE:
likeCount = fetchedLikeCount + 1;
dislikeCount = fetchedDislikeCount;
break;
case DISLIKE:
likeCount = fetchedLikeCount;
dislikeCount = fetchedDislikeCount + 1;
break;
case LIKE_REMOVE:
likeCount = fetchedLikeCount;
dislikeCount = fetchedDislikeCount;
break;
default:
throw new IllegalStateException();
}
updatePercentages();
}

View File

@@ -391,13 +391,37 @@ public class ReturnYouTubeDislikeApi {
return null;
}
public static boolean sendVote(String videoId, String userId, ReturnYouTubeDislike.Vote vote) {
/**
* Must call off main thread, as this will make a network call if user is not yet registered.
*
* @return ReturnYouTubeDislike user ID. If user registration has never happened
* and the network call fails, this returns NULL.
*/
@Nullable
private static String getUserId() {
ReVancedUtils.verifyOffMainThread();
String userId = SettingsEnum.RYD_USER_ID.getString();
if (!userId.isEmpty()) {
return userId;
}
userId = registerAsNewUser();
if (userId != null) {
SettingsEnum.RYD_USER_ID.saveValue(userId);
}
return userId;
}
public static boolean sendVote(String videoId, ReturnYouTubeDislike.Vote vote) {
ReVancedUtils.verifyOffMainThread();
Objects.requireNonNull(videoId);
Objects.requireNonNull(userId);
Objects.requireNonNull(vote);
try {
String userId = getUserId();
if (userId == null) return false;
if (checkIfRateLimitInEffect("sendVote")) {
return false;
}

View File

@@ -36,8 +36,6 @@ public enum SettingsEnum {
// Video
HDR_AUTO_BRIGHTNESS("revanced_hdr_auto_brightness", BOOLEAN, TRUE),
SHOW_OLD_VIDEO_QUALITY_MENU("revanced_show_old_video_quality_menu", BOOLEAN, TRUE),
@Deprecated
DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU("revanced_show_old_video_menu", BOOLEAN, TRUE),
REMEMBER_VIDEO_QUALITY_LAST_SELECTED("revanced_remember_video_quality_last_selected", BOOLEAN, TRUE),
VIDEO_QUALITY_DEFAULT_WIFI("revanced_video_quality_default_wifi", INTEGER, -2),
VIDEO_QUALITY_DEFAULT_MOBILE("revanced_video_quality_default_mobile", INTEGER, -2),
@@ -67,6 +65,10 @@ public enum SettingsEnum {
HIDE_EMERGENCY_BOX("revanced_hide_emergency_box", BOOLEAN, TRUE),
HIDE_FEED_SURVEY("revanced_hide_feed_survey", BOOLEAN, TRUE),
HIDE_GRAY_SEPARATOR("revanced_hide_gray_separator", BOOLEAN, TRUE),
HIDE_TIMED_REACTIONS("revanced_hide_timed_reactions", BOOLEAN, TRUE),
HIDE_SEARCH_RESULT_SHELF_HEADER("revanced_hide_search_result_shelf_header", BOOLEAN, FALSE),
HIDE_NOTIFY_ME_BUTTON("revanced_hide_notify_me_button", BOOLEAN, TRUE),
HIDE_JOIN_MEMBERSHIP_BUTTON("revanced_hide_join_membership_button", BOOLEAN, TRUE),
HIDE_HIDE_CHANNEL_GUIDELINES("revanced_hide_channel_guidelines", BOOLEAN, TRUE),
HIDE_IMAGE_SHELF("revanced_hide_image_shelf", BOOLEAN, TRUE),
HIDE_HIDE_INFO_PANELS("revanced_hide_info_panels", BOOLEAN, TRUE),
@@ -97,7 +99,6 @@ public enum SettingsEnum {
DISABLE_RESUMING_SHORTS_PLAYER("revanced_disable_resuming_shorts_player", BOOLEAN, FALSE),
HIDE_ALBUM_CARDS("revanced_hide_album_cards", BOOLEAN, FALSE, true),
HIDE_ARTIST_CARDS("revanced_hide_artist_cards", BOOLEAN, FALSE),
HIDE_AUDIO_TRACK_BUTTON("revanced_hide_audio_track_button", BOOLEAN, FALSE),
HIDE_AUTOPLAY_BUTTON("revanced_hide_autoplay_button", BOOLEAN, TRUE, true),
HIDE_BREAKING_NEWS("revanced_hide_breaking_news", BOOLEAN, TRUE, true),
HIDE_CAPTIONS_BUTTON("revanced_hide_captions_button", BOOLEAN, FALSE),
@@ -127,16 +128,16 @@ public enum SettingsEnum {
SPOOF_APP_VERSION("revanced_spoof_app_version", BOOLEAN, FALSE, true, "revanced_spoof_app_version_user_dialog_message"),
SPOOF_APP_VERSION_TARGET("revanced_spoof_app_version_target", STRING, "17.08.35", true, parents(SPOOF_APP_VERSION)),
USE_TABLET_MINIPLAYER("revanced_tablet_miniplayer", BOOLEAN, FALSE, true),
TABLET_LAYOUT("revanced_tablet_layout", BOOLEAN, FALSE, true, "revanced_tablet_layout_user_dialog_message"),
WIDE_SEARCHBAR("revanced_wide_searchbar", BOOLEAN, FALSE, true),
@Deprecated
DEPRECATED_SEEKBAR_COLOR("revanced_seekbar_color", STRING, "#FF0000"), // TODO: delete this
SEEKBAR_CUSTOM_COLOR("revanced_seekbar_custom_color", BOOLEAN, TRUE, true),
SEEKBAR_CUSTOM_COLOR_VALUE("revanced_seekbar_custom_color_value", STRING, "#FF0000", true, parents(SEEKBAR_CUSTOM_COLOR)),
HIDE_FILTER_BAR_FEED_IN_FEED("revanced_hide_filter_bar_feed_in_feed", BOOLEAN, FALSE, true),
HIDE_FILTER_BAR_FEED_IN_SEARCH("revanced_hide_filter_bar_feed_in_search", BOOLEAN, FALSE, true),
HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS("revanced_hide_filter_bar_feed_in_related_videos", BOOLEAN, FALSE, true),
HIDE_SHORTS_JOIN_BUTTON("revanced_hide_shorts_join_button", BOOLEAN, TRUE),
HIDE_SHORTS_SUBSCRIBE_BUTTON("revanced_hide_shorts_subscribe_button", BOOLEAN, FALSE),
HIDE_SHORTS_SUBSCRIBE_BUTTON("revanced_hide_shorts_subscribe_button", BOOLEAN, TRUE),
HIDE_SHORTS_SUBSCRIBE_BUTTON_PAUSED("revanced_hide_shorts_subscribe_button_paused", BOOLEAN, FALSE),
HIDE_SHORTS_THANKS_BUTTON("revanced_hide_shorts_thanks_button", BOOLEAN, TRUE),
HIDE_SHORTS_COMMENTS_BUTTON("revanced_hide_shorts_comments_button", BOOLEAN, FALSE),
HIDE_SHORTS_REMIX_BUTTON("revanced_hide_shorts_remix_button", BOOLEAN, TRUE),
@@ -169,8 +170,12 @@ public enum SettingsEnum {
EXTERNAL_BROWSER("revanced_external_browser", BOOLEAN, TRUE, true),
AUTO_REPEAT("revanced_auto_repeat", BOOLEAN, FALSE),
SEEKBAR_TAPPING("revanced_seekbar_tapping", BOOLEAN, TRUE),
SPOOF_SIGNATURE_VERIFICATION("revanced_spoof_signature_verification", BOOLEAN, TRUE, true,
"revanced_spoof_signature_verification_user_dialog_message"),
DISABLE_FINE_SCRUBBING_GESTURE("revanced_disable_fine_scrubbing_gesture", BOOLEAN, TRUE),
SPOOF_SIGNATURE("revanced_spoof_signature_verification_enabled", BOOLEAN, TRUE, true,
"revanced_spoof_signature_verification_enabled_user_dialog_message"),
SPOOF_SIGNATURE_IN_FEED("revanced_spoof_signature_in_feed_enabled", BOOLEAN, FALSE, false,
parents(SPOOF_SIGNATURE)),
BYPASS_URL_REDIRECTS("revanced_bypass_url_redirects", BOOLEAN, TRUE),
// Swipe controls
SWIPE_BRIGHTNESS("revanced_swipe_brightness", BOOLEAN, TRUE),
@@ -369,20 +374,19 @@ public enum SettingsEnum {
// region Migration
// TODO: do _not_ delete this SB private user id migration property until sometime in 2024.
// Do _not_ delete this SB private user id migration property until sometime in 2024.
// This is the only setting that cannot be reconfigured if lost,
// and more time should be given for users who rarely upgrade.
migrateOldSettingToNew(DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING, SB_PRIVATE_USER_ID);
// TODO: delete DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU (When? anytime).
migrateOldSettingToNew(DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU, SHOW_OLD_VIDEO_QUALITY_MENU);
// TODO: delete this seekbar color migration code
String oldSeekbarColorValue = DEPRECATED_SEEKBAR_COLOR.getString();
if (!oldSeekbarColorValue.equalsIgnoreCase((String) DEPRECATED_SEEKBAR_COLOR.defaultValue)) {
SEEKBAR_CUSTOM_COLOR_VALUE.saveValue(oldSeekbarColorValue);
SEEKBAR_CUSTOM_COLOR.saveValue(true);
DEPRECATED_SEEKBAR_COLOR.saveValue(DEPRECATED_SEEKBAR_COLOR.defaultValue);
// This migration may need to remain here for a while.
// Older online guides will still reference using commas,
// and this code will automatically convert anything the user enters to newline format,
// and also migrate any imported older settings that using commas.
String componentsToFilter = SettingsEnum.CUSTOM_FILTER_STRINGS.getString();
if (componentsToFilter.contains(",")) {
LogHelper.printInfo(() -> "Migrating custom filter strings to new line format");
SettingsEnum.CUSTOM_FILTER_STRINGS.saveValue(componentsToFilter.replace(",", "\n"));
}
// endregion

View File

@@ -12,6 +12,7 @@ import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import app.revanced.integrations.patches.ReturnYouTubeDislikePatch;
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.integrations.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
import app.revanced.integrations.settings.SettingsEnum;
@@ -63,7 +64,7 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
enabledPreference.setOnPreferenceChangeListener((pref, newValue) -> {
final boolean rydIsEnabled = (Boolean) newValue;
SettingsEnum.RYD_ENABLED.saveValue(rydIsEnabled);
ReturnYouTubeDislike.onEnabledChange(rydIsEnabled);
ReturnYouTubeDislikePatch.onRYDStatusChange(rydIsEnabled);
updateUIState();
return true;
@@ -89,7 +90,7 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
percentagePreference.setSummaryOff(str("revanced_ryd_dislike_percentage_summary_off"));
percentagePreference.setOnPreferenceChangeListener((pref, newValue) -> {
SettingsEnum.RYD_DISLIKE_PERCENTAGE.saveValue(newValue);
ReturnYouTubeDislike.clearCache();
ReturnYouTubeDislike.clearAllUICaches();
updateUIState();
return true;
});
@@ -102,7 +103,7 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment {
compactLayoutPreference.setSummaryOff(str("revanced_ryd_compact_layout_summary_off"));
compactLayoutPreference.setOnPreferenceChangeListener((pref, newValue) -> {
SettingsEnum.RYD_COMPACT_LAYOUT.saveValue(newValue);
ReturnYouTubeDislike.clearCache();
ReturnYouTubeDislike.clearAllUICaches();
updateUIState();
return true;
});

View File

@@ -72,7 +72,7 @@ public class SponsorBlockSettingsFragment extends PreferenceFragment {
} else if (!SettingsEnum.SB_CREATE_NEW_SEGMENT.getBoolean()) {
SponsorBlockViewController.hideNewSegmentLayout();
}
// voting and add new segment buttons automatically shows/hides themselves
// Voting and add new segment buttons automatically shows/hide themselves.
sbEnabled.setChecked(enabled);
@@ -109,6 +109,12 @@ public class SponsorBlockSettingsFragment extends PreferenceFragment {
privateUserId.setText(SettingsEnum.SB_PRIVATE_USER_ID.getString());
privateUserId.setEnabled(enabled);
// If the user has a private user id, then include a subtext that mentions not to share it.
String exportSummarySubText = SponsorBlockSettings.userHasSBPrivateId()
? str("sb_settings_ie_sum_warning")
: "";
importExport.setSummary(str("sb_settings_ie_sum", exportSummarySubText));
apiUrl.setEnabled(enabled);
importExport.setEnabled(enabled);
segmentCategory.setEnabled(enabled);
@@ -329,6 +335,7 @@ public class SponsorBlockSettingsFragment extends PreferenceFragment {
return false;
}
SettingsEnum.SB_PRIVATE_USER_ID.saveValue(newUUID);
updateUI();
fetchAndDisplayStats();
return true;
});
@@ -375,7 +382,7 @@ public class SponsorBlockSettingsFragment extends PreferenceFragment {
}
};
importExport.setTitle(str("sb_settings_ie"));
importExport.setSummary(str("sb_settings_ie_sum"));
// Summary is set in updateUI()
importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_MULTI_LINE
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);

View File

@@ -10,25 +10,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.Toolbar;
import android.widget.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.integrations.settings.SettingsEnum;
import java.text.Bidi;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import app.revanced.integrations.settings.SettingsEnum;
import java.util.concurrent.*;
public class ReVancedUtils {
@@ -93,9 +83,17 @@ public class ReVancedUtils {
}
public static boolean containsAny(@NonNull String value, @NonNull String... targets) {
for (String string : targets)
if (!string.isEmpty() && value.contains(string)) return true;
return false;
return indexOfFirstFound(value, targets) >= 0;
}
public static int indexOfFirstFound(@NonNull String value, @NonNull String... targets) {
for (String string : targets) {
if (!string.isEmpty()) {
final int indexOf = value.indexOf(string);
if (indexOf >= 0) return indexOf;
}
}
return -1;
}
/**

View File

@@ -23,11 +23,12 @@ public abstract class TrieSearch<T> {
*
* @param textSearched Text that was searched.
* @param matchedStartIndex Start index of the search text, where the pattern was matched.
* @param matchedLength Length of the match.
* @param callbackParameter Optional parameter passed into {@link TrieSearch#matches(Object, Object)}.
* @return True, if the search should stop here.
* If false, searching will continue to look for other matches.
*/
boolean patternMatched(T textSearched, int matchedStartIndex, Object callbackParameter);
boolean patternMatched(T textSearched, int matchedStartIndex, int matchedLength, Object callbackParameter);
}
/**
@@ -64,8 +65,8 @@ public abstract class TrieSearch<T> {
return false;
}
}
return callback == null
|| callback.patternMatched(searchText, searchTextIndex - patternStartIndex, callbackParameter);
return callback == null || callback.patternMatched(searchText,
searchTextIndex - patternStartIndex, patternLength, callbackParameter);
}
}
@@ -161,7 +162,7 @@ public abstract class TrieSearch<T> {
if (callback == null) {
return true; // No callback and all matches are valid.
}
if (callback.patternMatched(searchText, matchStartIndex, callbackParameter)) {
if (callback.patternMatched(searchText, matchStartIndex, currentMatchLength, callbackParameter)) {
return true; // Callback confirmed the match.
}
}

View File

@@ -49,7 +49,8 @@ public class ExternalDownloadButton extends BottomControlButton {
LogHelper.printDebug(() -> "External download button clicked");
final var context = view.getContext();
var downloaderPackageName = SettingsEnum.EXTERNAL_DOWNLOADER_PACKAGE_NAME.getString();
// Trim string to avoid any accidental whitespace.
var downloaderPackageName = SettingsEnum.EXTERNAL_DOWNLOADER_PACKAGE_NAME.getString().trim();
boolean packageEnabled = false;
try {

View File

@@ -0,0 +1,46 @@
package app.revanced.tudortmund.lockscreen;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.view.Display;
import android.view.Window;
import androidx.appcompat.app.AppCompatActivity;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
public class ShowOnLockscreenPatch {
/**
* @noinspection deprecation
*/
public static Window getWindow(AppCompatActivity activity, float brightness) {
Window window = activity.getWindow();
if (brightness >= 0) {
// High brightness set, therefore show on lockscreen.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) activity.setShowWhenLocked(true);
else window.addFlags(FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD);
} else {
// Ignore brightness reset when the screen is turned off.
DisplayManager displayManager = (DisplayManager) activity.getSystemService(Context.DISPLAY_SERVICE);
boolean isScreenOn = false;
for (Display display : displayManager.getDisplays()) {
if (display.getState() == Display.STATE_OFF) continue;
isScreenOn = true;
break;
}
if (isScreenOn) {
// Hide on lockscreen.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) activity.setShowWhenLocked(false);
else window.clearFlags(FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD);
}
}
return window;
}
}

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