1
mirror of https://github.com/revanced/revanced-integrations synced 2025-11-19 03:23:27 +01:00

Compare commits

...

77 Commits

Author SHA1 Message Date
semantic-release-bot
56cf001964 chore(release): 1.0.0 [skip ci]
# [1.0.0](https://github.com/ReVanced/revanced-integrations/compare/v0.125.0...v1.0.0) (2023-12-12)

### Bug Fixes

* **YouTube - Announcements:** Don't show error toast if there is no internet connection ([#537](https://github.com/ReVanced/revanced-integrations/issues/537)) ([0ce92c2](0ce92c284d))
* **YouTube - Client spoof:** Do not break clips ([f9102fa](f9102fa83b))
* **YouTube - Minimized playback:** Fix PIP incorrectly shown for some Shorts playback ([#533](https://github.com/ReVanced/revanced-integrations/issues/533)) ([fb433da](fb433da6ad))
* **YouTube - Return YouTube Dislike:** Fix dislikes sometimes not showing for non English language ([5d4c8b0](5d4c8b0a1b))
* **YouTube - Return YouTube Dislike:** Prevent the first Short opened from freezing the UI ([#532](https://github.com/ReVanced/revanced-integrations/issues/532)) ([0bb8669](0bb86694e2))
* **YouTube - Return YouTube Dislike:** Wait until fetch is complete before allowing the first Short to start playback ([#538](https://github.com/ReVanced/revanced-integrations/issues/538)) ([1c9c51c](1c9c51ca5f))
* **YouTube - SponsorBlock:** Allow autoplay when skipping to the end of the video ([3d660e1](3d660e1b5e))
* **YouTube - SponsorBlock:** Prevent autoplay from stopping to work ([f4e2d56](f4e2d56b18))
* **YouTube - Spoof signature:** Wait until storyboard fetch is done ([#535](https://github.com/ReVanced/revanced-integrations/issues/535)) ([92e8619](92e8619cd7))

### Features

* Allow choosing the vendor of GmsCore via patch options ([#529](https://github.com/ReVanced/revanced-integrations/issues/529)) ([fba7181](fba7181e70))
* **Tiktok:** Bump compatibility to `32.5.3` ([#536](https://github.com/ReVanced/revanced-integrations/issues/536)) ([10a1e16](10a1e168d0))
* **YouTube - Alternative Thumbnails:** Add option to use DeArrow ([#534](https://github.com/ReVanced/revanced-integrations/issues/534)) ([c4ee6ca](c4ee6ca4dd))
* **YouTube:** Add `Change start page` patch ([792dc0c](792dc0c522))

### BREAKING CHANGES

* The class `MicroGSupport` has been renamed to `GmsCoreSupport`
2023-12-12 00:13:59 +00:00
oSumAtrIX
69c5028661 chore: Merge branch dev to main (#530) 2023-12-12 01:11:30 +01:00
semantic-release-bot
30f2c4f4c9 chore(release): 1.0.0-dev.12 [skip ci]
# [1.0.0-dev.12](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.11...v1.0.0-dev.12) (2023-12-12)

### Features

* **Tiktok:** Bump compatibility to `32.5.3` ([#536](https://github.com/ReVanced/revanced-integrations/issues/536)) ([10a1e16](10a1e168d0))
2023-12-12 00:09:33 +00:00
d4rkk3y
10a1e168d0 feat(Tiktok): Bump compatibility to 32.5.3 (#536)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-12-12 01:07:08 +01:00
semantic-release-bot
e79ebb26ec chore(release): 1.0.0-dev.11 [skip ci]
# [1.0.0-dev.11](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.10...v1.0.0-dev.11) (2023-12-11)

### Features

* **YouTube:** Add `Change start page` patch ([792dc0c](792dc0c522))
2023-12-11 23:44:14 +00:00
oSumAtrIX
792dc0c522 feat(YouTube): Add Change start page patch 2023-12-12 00:38:04 +01:00
semantic-release-bot
8870f7ab0c chore(release): 1.0.0-dev.10 [skip ci]
# [1.0.0-dev.10](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.9...v1.0.0-dev.10) (2023-12-11)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Wait until fetch is complete before allowing the first Short to start playback ([#538](https://github.com/ReVanced/revanced-integrations/issues/538)) ([1c9c51c](1c9c51ca5f))
2023-12-11 18:33:07 +00:00
LisoUseInAIKyrios
1c9c51ca5f fix(YouTube - Return YouTube Dislike): Wait until fetch is complete before allowing the first Short to start playback (#538) 2023-12-11 22:30:36 +04:00
semantic-release-bot
6f7a7b825e chore(release): 1.0.0-dev.9 [skip ci]
# [1.0.0-dev.9](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.8...v1.0.0-dev.9) (2023-12-11)

### Features

* **YouTube - Alternative Thumbnails:** Add option to use DeArrow ([#534](https://github.com/ReVanced/revanced-integrations/issues/534)) ([c4ee6ca](c4ee6ca4dd))
2023-12-11 01:09:29 +00:00
Chris
c4ee6ca4dd feat(YouTube - Alternative Thumbnails): Add option to use DeArrow (#534)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2023-12-11 05:06:55 +04:00
semantic-release-bot
46dbbf5f86 chore(release): 1.0.0-dev.8 [skip ci]
# [1.0.0-dev.8](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.7...v1.0.0-dev.8) (2023-12-10)

### Bug Fixes

* **YouTube - Announcements:** Don't show error toast if there is no internet connection ([#537](https://github.com/ReVanced/revanced-integrations/issues/537)) ([0ce92c2](0ce92c284d))
2023-12-10 22:46:37 +00:00
nullptr
0ce92c284d fix(YouTube - Announcements): Don't show error toast if there is no internet connection (#537) 2023-12-10 23:44:10 +01:00
oSumAtrIX
fb56e9a362 build: Simplify enabling local build cache 2023-12-10 22:21:40 +01:00
semantic-release-bot
fa5713b4a0 chore(release): 1.0.0-dev.7 [skip ci]
# [1.0.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.6...v1.0.0-dev.7) (2023-12-09)

### Bug Fixes

* **YouTube - Spoof signature:** Wait until storyboard fetch is done ([#535](https://github.com/ReVanced/revanced-integrations/issues/535)) ([92e8619](92e8619cd7))
2023-12-09 10:33:48 +00:00
LisoUseInAIKyrios
92e8619cd7 fix(YouTube - Spoof signature): Wait until storyboard fetch is done (#535) 2023-12-09 14:31:20 +04:00
semantic-release-bot
9c635e5f91 chore(release): 1.0.0-dev.6 [skip ci]
# [1.0.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.5...v1.0.0-dev.6) (2023-12-07)

### Bug Fixes

* **YouTube - Client spoof:** Do not break clips ([f9102fa](f9102fa83b))
2023-12-07 19:03:21 +00:00
oSumAtrIX
f9102fa83b fix(YouTube - Client spoof): Do not break clips 2023-12-07 23:00:36 +04:00
semantic-release-bot
e7758a7ac4 chore(release): 1.0.0-dev.5 [skip ci]
# [1.0.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.4...v1.0.0-dev.5) (2023-12-04)

### Bug Fixes

* **YouTube - Minimized playback:** Fix PIP incorrectly shown for some Shorts playback ([#533](https://github.com/ReVanced/revanced-integrations/issues/533)) ([fb433da](fb433da6ad))
2023-12-04 23:50:11 +00:00
LisoUseInAIKyrios
fb433da6ad fix(YouTube - Minimized playback): Fix PIP incorrectly shown for some Shorts playback (#533) 2023-12-05 03:47:31 +04:00
semantic-release-bot
aa6f591141 chore(release): 1.0.0-dev.4 [skip ci]
# [1.0.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.3...v1.0.0-dev.4) (2023-12-04)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Prevent the first Short opened from freezing the UI ([#532](https://github.com/ReVanced/revanced-integrations/issues/532)) ([0bb8669](0bb86694e2))
2023-12-04 08:49:53 +00:00
LisoUseInAIKyrios
0bb86694e2 fix(YouTube - Return YouTube Dislike): Prevent the first Short opened from freezing the UI (#532) 2023-12-04 10:47:29 +02:00
semantic-release-bot
d484f35127 chore(release): 1.0.0-dev.3 [skip ci]
# [1.0.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.2...v1.0.0-dev.3) (2023-12-03)

### Bug Fixes

* **YouTube - SponsorBlock:** Prevent autoplay from stopping to work ([f4e2d56](f4e2d56b18))
2023-12-03 18:33:33 +00:00
oSumAtrIX
f4e2d56b18 fix(YouTube - SponsorBlock): Prevent autoplay from stopping to work 2023-12-03 19:30:56 +01:00
semantic-release-bot
9394c512ba chore(release): 1.0.0-dev.2 [skip ci]
# [1.0.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v1.0.0-dev.1...v1.0.0-dev.2) (2023-12-03)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Fix dislikes sometimes not showing for non English language ([5d4c8b0](5d4c8b0a1b))
2023-12-03 18:27:44 +00:00
LisoUseInAIKyrios
5d4c8b0a1b fix(YouTube - Return YouTube Dislike): Fix dislikes sometimes not showing for non English language 2023-12-03 20:24:37 +02:00
semantic-release-bot
3a56431a28 chore(release): 1.0.0-dev.1 [skip ci]
# [1.0.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.125.1-dev.1...v1.0.0-dev.1) (2023-12-02)

### Features

* Allow choosing the vendor of GmsCore via patch options ([#529](https://github.com/ReVanced/revanced-integrations/issues/529)) ([fba7181](fba7181e70))

### BREAKING CHANGES

* The class `MicroGSupport` has been renamed to `GmsCoreSupport`
2023-12-02 21:54:20 +00:00
oSumAtrIX
fba7181e70 feat: Allow choosing the vendor of GmsCore via patch options (#529)
BREAKING CHANGE: The class `MicroGSupport` has been renamed to `GmsCoreSupport`
2023-12-02 22:51:39 +01:00
semantic-release-bot
3b4f0206ad chore(release): 0.125.1-dev.1 [skip ci]
## [0.125.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.125.0...v0.125.1-dev.1) (2023-12-02)

### Bug Fixes

* **YouTube - SponsorBlock:** Allow autoplay when skipping to the end of the video ([3d660e1](3d660e1b5e))
2023-12-02 21:31:15 +00:00
oSumAtrIX
3d660e1b5e fix(YouTube - SponsorBlock): Allow autoplay when skipping to the end of the video 2023-12-02 17:56:08 +01:00
semantic-release-bot
dd045ad985 chore(release): 0.125.0 [skip ci]
# [0.125.0](https://github.com/ReVanced/revanced-integrations/compare/v0.124.1...v0.125.0) (2023-12-02)

### Features

* Modernize restart logic ([#527](https://github.com/ReVanced/revanced-integrations/issues/527)) ([0ea4e72](0ea4e720ed))
2023-12-02 14:27:11 +00:00
oSumAtrIX
0a15245f41 chore: Merge branch dev to main (#528) 2023-12-02 15:24:33 +01:00
semantic-release-bot
a18c134aaa chore(release): 0.125.0-dev.1 [skip ci]
# [0.125.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.124.1...v0.125.0-dev.1) (2023-11-29)

### Features

* Modernize restart logic ([#527](https://github.com/ReVanced/revanced-integrations/issues/527)) ([0ea4e72](0ea4e720ed))
2023-11-29 20:51:03 +00:00
LisoUseInAIKyrios
0ea4e720ed feat: Modernize restart logic (#527) 2023-11-29 22:48:23 +02:00
semantic-release-bot
c6a1b9fc59 chore(release): 0.124.1 [skip ci]
## [0.124.1](https://github.com/ReVanced/revanced-integrations/compare/v0.124.0...v0.124.1) (2023-11-27)

### Bug Fixes

* **YouTube - Hide layout components:** Prevent app from crashing on startup ([c2112e0](c2112e066a))
* **YouTube - Return YouTube dislike:** Fix layout of right to left languages ([11abdf1](11abdf162f))
* **YouTube - Spoof signature:** fix error toast when viewing very old YouTube videos ([#525](https://github.com/ReVanced/revanced-integrations/issues/525)) ([eede028](eede028c96))
2023-11-27 19:09:52 +00:00
oSumAtrIX
d6ed0c061c chore: Merge branch dev to main (#526) 2023-11-27 20:07:15 +01:00
semantic-release-bot
3ba3d5108d chore(release): 0.124.1-dev.3 [skip ci]
## [0.124.1-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.124.1-dev.2...v0.124.1-dev.3) (2023-11-27)

### Bug Fixes

* **YouTube - Hide layout components:** Prevent app from crashing on startup ([c2112e0](c2112e066a))
2023-11-27 19:06:27 +00:00
oSumAtrIX
c2112e066a fix(YouTube - Hide layout components): Prevent app from crashing on startup
The application crashes sporadically when a field is not initialized yet in a static context.
2023-11-27 20:02:59 +01:00
oSumAtrIX
6520499a27 refactor: Use a newline consistently 2023-11-27 18:40:49 +01:00
semantic-release-bot
b2d9dcd6c0 chore(release): 0.124.1-dev.2 [skip ci]
## [0.124.1-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.124.1-dev.1...v0.124.1-dev.2) (2023-11-26)

### Bug Fixes

* **YouTube - Return YouTube dislike:** Fix layout of right to left languages ([11abdf1](11abdf162f))
2023-11-26 22:27:00 +00:00
LisoUseInAIKyrios
11abdf162f fix(YouTube - Return YouTube dislike): Fix layout of right to left languages 2023-11-27 00:24:14 +02:00
semantic-release-bot
b514bdd7d0 chore(release): 0.124.1-dev.1 [skip ci]
## [0.124.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.124.0...v0.124.1-dev.1) (2023-11-25)

### Bug Fixes

* **YouTube - Spoof signature:** fix error toast when viewing very old YouTube videos ([#525](https://github.com/ReVanced/revanced-integrations/issues/525)) ([eede028](eede028c96))
2023-11-25 21:32:51 +00:00
LisoUseInAIKyrios
eede028c96 fix(YouTube - Spoof signature): fix error toast when viewing very old YouTube videos (#525) 2023-11-25 23:30:15 +02:00
semantic-release-bot
9eb0471989 chore(release): 0.124.0 [skip ci]
# [0.124.0](https://github.com/ReVanced/revanced-integrations/compare/v0.123.0...v0.124.0) (2023-11-23)

### Bug Fixes

* **YouTube - Custom playback speed:** Fix toast error on opening playback speed menu ([#522](https://github.com/ReVanced/revanced-integrations/issues/522)) ([0124f41](0124f41cd6))
* **YouTube - Enable tablet layout:** Respect the original device layout ([bc56aa5](bc56aa5f07))
* **YouTube - SponsorBlock:** Do not disable autoplay when skipping to the end of the video ([f936f24](f936f240a7))

### Features

* **YouTube - Hide ads:** Hide shopping links in video description ([11e7c04](11e7c04b70))
* **YouTube - Hide layout components:** Hide "For You" shelf in channel page ([2613e10](2613e102cc))
2023-11-23 03:37:12 +00:00
oSumAtrIX
3dbfbf9e6f chore: Merge branch dev to main (#524) 2023-11-23 04:34:47 +01:00
semantic-release-bot
373820eb99 chore(release): 0.124.0-dev.1 [skip ci]
# [0.124.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.123.1-dev.1...v0.124.0-dev.1) (2023-11-23)

### Bug Fixes

* **YouTube - Enable tablet layout:** Respect the original device layout ([bc56aa5](bc56aa5f07))
* **YouTube - SponsorBlock:** Do not disable autoplay when skipping to the end of the video ([f936f24](f936f240a7))

### Features

* **YouTube - Hide ads:** Hide shopping links in video description ([11e7c04](11e7c04b70))
* **YouTube - Hide layout components:** Hide "For You" shelf in channel page ([2613e10](2613e102cc))
2023-11-23 03:25:50 +00:00
oSumAtrIX
f936f240a7 fix(YouTube - SponsorBlock): Do not disable autoplay when skipping to the end of the video 2023-11-23 04:23:02 +01:00
semantic-release-bot
b9d50fd3d8 chore(release): 0.124.0-dev.1 [skip ci]
# [0.124.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.123.1-dev.1...v0.124.0-dev.1) (2023-11-23)

### Bug Fixes

* **YouTube - Enable tablet layout:** Respect the original device layout ([bc56aa5](bc56aa5f07))

### Features

* **YouTube - Hide ads:** Hide shopping links in video description ([11e7c04](11e7c04b70))
* **YouTube - Hide layout components:** Hide "For You" shelf in channel page ([2613e10](2613e102cc))
2023-11-23 03:01:56 +00:00
oSumAtrIX
11e7c04b70 feat(YouTube - Hide ads): Hide shopping links in video description 2023-11-23 03:55:03 +01:00
oSumAtrIX
2613e102cc feat(YouTube - Hide layout components): Hide "For You" shelf in channel page 2023-11-23 03:55:02 +01:00
semantic-release-bot
2a582f9424 chore(release): 0.123.1-dev.2 [skip ci]
## [0.123.1-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.123.1-dev.1...v0.123.1-dev.2) (2023-11-23)

### Bug Fixes

* **YouTube - Enable tablet layout:** Respect the original device layout ([9e3e747](9e3e747ce6))
2023-11-23 03:55:02 +01:00
oSumAtrIX
bc56aa5f07 fix(YouTube - Enable tablet layout): Respect the original device layout 2023-11-23 03:54:59 +01:00
oSumAtrIX
1ebf8e04a6 chore: Add heading to issue templates 2023-11-23 00:56:50 +01:00
semantic-release-bot
c13504a0b3 chore(release): 0.123.1-dev.1 [skip ci]
## [0.123.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.123.0...v0.123.1-dev.1) (2023-11-22)

### Bug Fixes

* **YouTube - Custom playback speed:** Fix toast error on opening playback speed menu ([#522](https://github.com/ReVanced/revanced-integrations/issues/522)) ([0124f41](0124f41cd6))
2023-11-22 19:20:28 +00:00
LisoUseInAIKyrios
0124f41cd6 fix(YouTube - Custom playback speed): Fix toast error on opening playback speed menu (#522) 2023-11-22 21:18:01 +02:00
oSumAtrIX
31777e027e chore: Merge branch dev to main (#523) 2023-11-22 09:02:44 +01:00
oSumAtrIX
dedd2f658a chore: Suppress deprecated flag 2023-11-22 01:03:30 +01:00
oSumAtrIX
7f0c2c507c build: Bump dependencies 2023-11-22 01:02:28 +01:00
oSumAtrIX
17e78824b7 build: Bump Gradle wrapper 2023-11-22 01:01:56 +01:00
oSumAtrIX
36fe5718cf build: Use dedicated Gradle cache action 2023-11-22 01:01:56 +01:00
oSumAtrIX
355acc7023 build: Use Gradle build cache 2023-11-22 01:01:55 +01:00
oSumAtrIX
dcca2e5e8d ci: Simplify cache paths 2023-11-22 00:11:15 +01:00
oSumAtrIX
c4d01b2334 chore: Reword comment for first PR merge 2023-11-22 00:07:48 +01:00
oSumAtrIX
ce2ad04f60 chore: Add a newline between steps 2023-11-22 00:04:20 +01:00
oSumAtrIX
1230d77f94 chore: Simplify issue templates 2023-11-21 23:57:28 +01:00
oSumAtrIX
2735f99c0a chore: Add folder to gitignore 2023-11-21 23:42:04 +01:00
oSumAtrIX
64463dea5b ci: Update cache key 2023-11-21 23:41:41 +01:00
oSumAtrIX
4932a71088 chore: Update packages 2023-11-21 23:40:57 +01:00
semantic-release-bot
f6e936812e chore(release): 0.123.0 [skip ci]
# [0.123.0](https://github.com/ReVanced/revanced-integrations/compare/v0.122.1...v0.123.0) (2023-11-20)

### Bug Fixes

* **YouTube - Hide layout components:** Check if `bytes` parameter of `LayoutComponentsFilter#filterMixPlaylists` for null ([abfbb0d](abfbb0d7d5))
* **YouTube - Spoof client:** Fix frozen video on playback start ([#520](https://github.com/ReVanced/revanced-integrations/issues/520)) ([ffcee71](ffcee71f79))

### Features

* **YouTube:** Add `Disable rolling number animations` patch ([#519](https://github.com/ReVanced/revanced-integrations/issues/519)) ([ce0bedc](ce0bedc5d3))
2023-11-20 17:58:54 +00:00
oSumAtrIX
c8510dbb4d chore: Merge branch dev to main (#521) 2023-11-20 18:56:05 +01:00
semantic-release-bot
d3021bcf0b chore(release): 0.122.2-dev.2 [skip ci]
## [0.122.2-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.122.2-dev.1...v0.122.2-dev.2) (2023-11-20)

### Bug Fixes

* **YouTube - Spoof client:** Fix frozen video on playback start ([#520](https://github.com/ReVanced/revanced-integrations/issues/520)) ([9139954](91399540ba))
2023-11-20 18:55:55 +01:00
LisoUseInAIKyrios
ffcee71f79 fix(YouTube - Spoof client): Fix frozen video on playback start (#520) 2023-11-20 18:55:55 +01:00
semantic-release-bot
8cbe2b5a92 chore(release): 0.122.2-dev.1 [skip ci]
## [0.122.2-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.122.1...v0.122.2-dev.1) (2023-11-19)

### Bug Fixes

* **YouTube - Hide layout components:** Check if `bytes` parameter of `LayoutComponentsFilter#filterMixPlaylists` for null ([3eb07c1](3eb07c12dd))
2023-11-20 18:55:55 +01:00
oSumAtrIX
abfbb0d7d5 fix(YouTube - Hide layout components): Check if bytes parameter of LayoutComponentsFilter#filterMixPlaylists for null 2023-11-20 18:55:55 +01:00
LisoUseInAIKyrios
ce0bedc5d3 feat(YouTube): Add Disable rolling number animations patch (#519) 2023-11-20 18:55:53 +01:00
semantic-release-bot
5cf5d86913 chore(release): 0.122.1 [skip ci]
## [0.122.1](https://github.com/ReVanced/revanced-integrations/compare/v0.122.0...v0.122.1) (2023-11-19)

### Bug Fixes

* **YouTube - ReturnYouTubeDislike:** Improve layout padding ([#517](https://github.com/ReVanced/revanced-integrations/issues/517)) ([ba15aba](ba15aba063))
* **YouTube:** Check if `protobufBuffer` can be filtered ([b078f0c](b078f0ca37))
2023-11-19 04:02:38 +00:00
oSumAtrIX
7dd826ad71 chore: Merge branch dev to main (#518) 2023-11-19 04:59:59 +01:00
oSumAtrIX
58afcec641 chore(YouTube): Only debug log if protobufBuffer has no array 2023-11-18 22:14:08 +01:00
58 changed files with 12807 additions and 4099 deletions

View File

@@ -1,59 +0,0 @@
name: 🐞 Bug report
description: Report a very clearly broken issue.
title: 'bug: <title>'
labels: [bug]
body:
- type: markdown
attributes:
value: |
# ReVanced bug report
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-integrations/labels/bug).
- type: dropdown
attributes:
label: Type
options:
- Cosmetic
- Other
validations:
required: true
- type: textarea
attributes:
label: Bug description
description: How did you find the bug? Any additional details that might help?
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Add the steps to reproduce this bug including your environment.
placeholder: Step 1. Download some files. Step 2. ...
validations:
required: true
- type: textarea
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
validations:
required: true
- type: textarea
attributes:
label: Screenshots or videos
description: Add screenshots or videos that show the bug here.
placeholder: Drag and drop the screenshots/videos into this box.
validations:
required: false
- type: textarea
attributes:
label: Solution
description: If applicable, add a possible solution.
validations:
required: false
- type: textarea
attributes:
label: Additional context
description: Add additional context here.
validations:
required: false

109
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@@ -0,0 +1,109 @@
name: 🐞 Bug report
description: Report a bug or an issue.
title: 'bug: '
labels: ['Bug report']
body:
- type: markdown
attributes:
value: |
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# ReVanced Integrations bug report
Before creating a new bug report, please keep the following in mind:
- **Do not submit a duplicate bug report**: You can review existing bug reports [here](https://github.com/ReVanced/revanced-integrations/labels/Bug%20report).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea
attributes:
label: Bug description
description: |
- Describe your bug in detail
- Add steps to reproduce the bug if possible (Step 1. ... Step 2. ...)
- Add images and videos if possible
- List used patches if applicable
validations:
required: true
- type: textarea
attributes:
label: Error logs
description: Exceptions can be captured by running `logcat | grep AndroidRuntime` in a shell.
render: shell
- type: textarea
attributes:
label: Solution
description: If applicable, add a possible solution to the bug.
- type: textarea
attributes:
label: Additional context
description: Add additional context here.
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Your bug report will be closed if you don't follow the checklist below.
options:
- label: This issue is not a duplicate of an existing bug report.
required: true
- label: I have chosen an appropriate title.
required: true
- label: All requested information has been provided properly.
required: true

View File

@@ -1,46 +0,0 @@
name: ⭐ Feature request
description: Create a detailed feature request.
title: 'feat: <title>'
labels: [feature-request]
body:
- type: markdown
attributes:
value: |
# ReVanced feature request
Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-integrations/labels/feature-request).
- type: dropdown
attributes:
label: Type
options:
- Functionality
- Cosmetic
- Other
validations:
required: true
- type: textarea
attributes:
label: Issue
description: What is the current problem. Why does it require a feature request?
validations:
required: true
- type: textarea
attributes:
label: Feature
description: Describe your feature in detail. How does it solve the issue?
validations:
required: true
- type: textarea
attributes:
label: Motivation
description: Why should your feature should be considered?
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add additional context here.
validations:
required: false

View File

@@ -0,0 +1,105 @@
name: ⭐ Feature request
description: Create a detailed request for a new feature.
title: 'feat: '
labels: ['Feature request']
body:
- type: markdown
attributes:
value: |
<p align="center">
<picture>
<source
width="256px"
media="(prefers-color-scheme: dark)"
srcset="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
>
<img
width="256px"
src="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
>
</picture>
<br>
<a href="https://revanced.app/">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-logo/revanced-logo.svg" />
<img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-integrations/main/assets/revanced-logo/revanced-logo.svg" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://github.com/ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
<img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="http://revanced.app/discord">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://reddit.com/r/revancedapp">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://t.me/app_revanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://x.com/revancedapp">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
<img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
</picture>
</a>&nbsp;&nbsp;&nbsp;
<a href="https://www.youtube.com/@ReVanced">
<picture>
<source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
<img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
</picture>
</a>
<br>
<br>
Continuing the legacy of Vanced
</p>
# ReVanced Integrations feature request
Before creating a new feature request, please keep the following in mind:
- **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-integrations/labels/Feature%20request).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea
attributes:
label: Feature description
description: |
- Describe your feature in detail
- Add images, videos, links, examples, references, etc. if possible
- Add the target application name in case you request a new patch
- type: textarea
attributes:
label: Motivation
description: |
A strong motivation is necessary for a feature request to be considered.
- Why should this feature be implemented?
- What is the explicit use case?
- What are the benefits?
- What makes this feature important?
validations:
required: true
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Your feature request will be closed if you don't follow the checklist below.
options:
- label: This issue is not a duplicate of an existing feature request.
required: true
- label: I have chosen an appropriate title.
required: true
- label: All requested information has been provided properly.
required: true

2
.github/config.yml vendored
View File

@@ -1,2 +1,2 @@
firstPRMergeComment: >
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) to receive a role for your contribution.

View File

@@ -16,6 +16,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Open pull request
uses: repo-sync/pull-request@v2
with:

View File

@@ -23,23 +23,28 @@ jobs:
# https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false
fetch-depth: 0
- name: Cache
- name: Cache Node modules
uses: actions/cache@v3
with:
path: |
${{ runner.home }}/.gradle/caches
${{ runner.home }}/.gradle/wrapper
.gradle
node_modules
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
key: npm-${{ hashFiles('package-lock.json') }}
- name: Cache Gradle
uses: burrunan/gradle-cache-action@v1
- name: Setup Java
run: echo "JAVA_HOME=$JAVA_HOME_17_X64" >> $GITHUB_ENV
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew build clean
- name: Setup semantic-release
run: npm install
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@
/.idea
/.vscode
/*.log
node_modules

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@ android {
dependencies {
compileOnly(project(mapOf("path" to ":dummy")))
compileOnly("androidx.annotation:annotation:1.6.0")
compileOnly("androidx.annotation:annotation:1.7.0")
compileOnly("androidx.appcompat:appcompat:1.7.0-alpha03")
compileOnly("com.squareup.okhttp3:okhttp:5.0.0-alpha.11")
compileOnly("com.squareup.retrofit2:retrofit:2.9.0")

View File

@@ -0,0 +1,16 @@
package app.revanced.integrations.patches;
import android.content.Intent;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
@SuppressWarnings("unused")
public final class ChangeStartPagePatch {
public static void changeIntent(Intent intent) {
final var startPage = SettingsEnum.START_PAGE.getString();
if (startPage.isEmpty()) return;
LogHelper.printDebug(() -> "Changing start page to " + startPage);
intent.setAction("com.google.android.youtube.action." + startPage);
}
}

View File

@@ -0,0 +1,12 @@
package app.revanced.integrations.patches;
import app.revanced.integrations.settings.SettingsEnum;
public class DisableRollingNumberAnimationsPatch {
/**
* Injection point.
*/
public static boolean disableRollingNumberAnimations() {
return SettingsEnum.DISABLE_ROLLING_NUMBER_ANIMATIONS.getBoolean();
}
}

View File

@@ -0,0 +1,74 @@
package app.revanced.integrations.patches;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import androidx.annotation.RequiresApi;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
import java.util.Objects;
import static app.revanced.integrations.utils.StringRef.str;
/**
* @noinspection unused
*/
public class GmsCoreSupport {
private static final String GMS_CORE_PACKAGE_NAME
= getGmsCoreVendor() + ".android.gms";
private static final String DONT_KILL_MY_APP_LINK
= "https://dontkillmyapp.com";
private static final Uri GMS_CORE_PROVIDER
= Uri.parse("content://" + getGmsCoreVendor() + ".android.gsf.gservices/prefix");
private static void search(Context context, String uriString, String message) {
ReVancedUtils.showToastLong(message);
var intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(SearchManager.QUERY, uriString);
context.startActivity(intent);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public static void checkAvailability() {
var context = Objects.requireNonNull(ReVancedUtils.getContext());
try {
context.getPackageManager().getPackageInfo(GMS_CORE_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException exception) {
LogHelper.printInfo(() -> "GmsCore was not found", exception);
search(context, getGmsCoreDownloadLink(), str("gms_core_not_installed_warning"));
// Gracefully exit the app, so it does not crash.
System.exit(0);
}
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
if (client != null) return;
LogHelper.printInfo(() -> "GmsCore is not running in the background");
search(context, DONT_KILL_MY_APP_LINK, str("gms_core_not_running_warning"));
}
}
private static String getGmsCoreDownloadLink() {
final var vendor = getGmsCoreVendor();
switch (vendor) {
case "com.mgoogle":
return "https://github.com/TeamVanced/VancedMicroG/releases/latest";
case "app.revanced":
return "https://github.com/revanced/gmscore/releases/latest";
default:
return vendor + ".android.gms";
}
}
// Modified by a patch. Do not touch.
private static String getGmsCoreVendor() {
return "app.revanced";
}
}

View File

@@ -1,53 +0,0 @@
package app.revanced.integrations.patches;
import static app.revanced.integrations.utils.StringRef.str;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import java.util.Objects;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
public class MicroGSupport {
private static final String MICROG_VENDOR = "com.mgoogle";
private static final String MICROG_PACKAGE_NAME = MICROG_VENDOR + ".android.gms";
private static final String VANCED_MICROG_DOWNLOAD_LINK = "https://github.com/TeamVanced/VancedMicroG/releases/latest";
private static final String DONT_KILL_MY_APP_LINK = "https://dontkillmyapp.com";
private static final Uri VANCED_MICROG_PROVIDER = Uri.parse("content://" + MICROG_VENDOR + ".android.gsf.gservices/prefix");
private static void startIntent(Context context, String uriString, String message) {
ReVancedUtils.showToastLong(message);
var intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(uriString));
context.startActivity(intent);
}
@TargetApi(26)
public static void checkAvailability() {
var context = Objects.requireNonNull(ReVancedUtils.getContext());
try {
context.getPackageManager().getPackageInfo(MICROG_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException exception) {
LogHelper.printInfo(() -> "Vanced MicroG was not found", exception);
startIntent(context, VANCED_MICROG_DOWNLOAD_LINK, str("microg_not_installed_warning"));
// Gracefully exit the app, so it does not crash.
System.exit(0);
}
try (var client = context.getContentResolver().acquireContentProviderClient(VANCED_MICROG_PROVIDER)) {
if (client != null) return;
LogHelper.printInfo(() -> "Vanced MicroG is not running in the background");
startIntent(context, DONT_KILL_MY_APP_LINK, str("microg_not_running_warning"));
}
}
}

View File

@@ -2,12 +2,39 @@ package app.revanced.integrations.patches;
import app.revanced.integrations.shared.PlayerType;
@SuppressWarnings("unused")
public class MinimizedPlaybackPatch {
public static boolean isPlaybackNotShort() {
return !PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized();
/**
* Injection point.
*/
public static boolean playbackIsNotShort() {
// Steps to verify most edge cases:
// 1. Open a regular video
// 2. Minimize app (PIP should appear)
// 3. Reopen app
// 4. Open a Short (without closing the regular video)
// (try opening both Shorts in the video player suggestions AND Shorts from the home feed)
// 5. Minimize the app (PIP should not appear)
// 6. Reopen app
// 7. Close the Short
// 8. Resume playing the regular video
// 9. Minimize the app (PIP should appear)
if (!VideoInformation.lastVideoIdIsShort()) {
return true; // Definitely is not a Short.
}
// Might be a Short, or might be a prior regular video on screen again after a Short was closed.
// This incorrectly prevents PIP if player is in WATCH_WHILE_MINIMIZED after closing a Short,
// But there's no way around this unless an additional hook is added to definitively detect
// the Shorts player is on screen. This use case is unusual anyways so it's not a huge concern.
return !PlayerType.getCurrent().isNoneHiddenOrMinimized();
}
/**
* Injection point.
*/
public static boolean overrideMinimizedPlaybackAvailable() {
// This could be done entirely in the patch,
// but having a unique method to search for makes manually inspecting the patched apk much easier.

View File

@@ -9,6 +9,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.integrations.patches.components.ReturnYouTubeDislikeFilterPatch;
import app.revanced.integrations.patches.spoof.SpoofAppVersionPatch;
import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.shared.PlayerType;
@@ -27,19 +28,25 @@ import static app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislik
* Handles all interaction of UI patch components.
*
* Known limitation:
* Litho based Shorts player can experience temporarily frozen video playback if the RYD fetch takes too long.
* The implementation of Shorts litho requires blocking the loading the first Short until RYD has completed.
* This is because it modifies the dislikes text synchronously, and if the RYD fetch has
* not completed yet then the UI will be temporarily frozen.
*
* Temporary work around:
* Enable app spoofing to version 18.33.40 or older, as that uses a non litho Shorts player.
*
* Permanent fix (yet to be implemented), either of:
* - Modify patch to hook onto the Shorts Litho TextView, and update the dislikes asynchronously.
* - Find a way to force Litho to rebuild it's component tree
* (and use that hook to force the shorts dislikes to update after the fetch is completed).
* A (yet to be implemented) solution that fixes this problem. Any one of:
* - Modify patch to hook onto the Shorts Litho TextView, and update the dislikes text asynchronously.
* - Find a way to force Litho to rebuild it's component tree,
* and use that hook to force the shorts dislikes to update after the fetch is completed.
* - Hook into the dislikes button image view, and replace the dislikes thumb down image with a
* generated image of the number of dislikes, then update the image asynchronously. This Could
* also be used for the regular video player to give a better UI layout and completely remove
* the need for the Rolling Number patches.
*/
@SuppressWarnings("unused")
public class ReturnYouTubeDislikePatch {
public static final boolean IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER =
SpoofAppVersionPatch.isSpoofingToEqualOrLessThan("18.33.40");
/**
* RYD data for the current video on screen.
*/
@@ -318,8 +325,12 @@ public class ReturnYouTubeDislikePatch {
try {
if (SettingsEnum.RYD_ENABLED.getBoolean() && !SettingsEnum.RYD_COMPACT_LAYOUT.getBoolean()) {
if (ReturnYouTubeDislike.isPreviouslyCreatedSegmentedSpan(text)) {
// +1 pixel is needed for some foreign languages that measure
// the text different from what is used for layout (Greek in particular).
// Probably a bug in Android, but who knows.
// Single line mode is also used as an additional fix for this issue.
return measuredTextWidth + ReturnYouTubeDislike.leftSeparatorBounds.right
+ ReturnYouTubeDislike.leftSeparatorShapePaddingPixels;
+ ReturnYouTubeDislike.leftSeparatorShapePaddingPixels + 1;
}
}
} catch (Exception ex) {
@@ -335,9 +346,17 @@ public class ReturnYouTubeDislikePatch {
// YouTube Rolling Numbers do not use compound drawables or drawable padding.
if (view.getCompoundDrawablePadding() == 0) {
LogHelper.printDebug(() -> "Adding rolling number TextView changes");
ShapeDrawable leftSeparator = ReturnYouTubeDislike.getLeftSeparatorDrawable();
view.setCompoundDrawables(leftSeparator, null, null, null);
view.setCompoundDrawablePadding(ReturnYouTubeDislike.leftSeparatorShapePaddingPixels);
ShapeDrawable separator = ReturnYouTubeDislike.getLeftSeparatorDrawable();
if (ReVancedUtils.isRightToLeftTextLayout()) {
view.setCompoundDrawables(null, null, separator, null);
} else {
view.setCompoundDrawables(separator, null, null, null);
}
// Single line mode does not clip words if the span is larger than the view bounds.
// The styled span applied to the view should always have the same bounds,
// but use this feature just in case the measurements are somehow off by a few pixels.
view.setSingleLine(true);
}
}
@@ -350,6 +369,7 @@ public class ReturnYouTubeDislikePatch {
LogHelper.printDebug(() -> "Removing rolling number TextView changes");
view.setCompoundDrawablePadding(0);
view.setCompoundDrawables(null, null, null, null);
view.setSingleLine(false);
}
}
@@ -536,26 +556,47 @@ public class ReturnYouTubeDislikePatch {
// Video Id and voting hooks (all players).
//
private static volatile boolean lastPlayerResponseWasShort;
/**
* Injection point. Uses 'playback response' video id hook to preload RYD.
*/
public static void preloadVideoId(@NonNull String videoId, boolean videoIsOpeningOrPlaying) {
public static void preloadVideoId(@NonNull String videoId, boolean isShortAndOpeningOrPlaying) {
try {
// Shorts shelf in home and subscription feed causes player response hook to be called,
// and the 'is opening/playing' parameter will be false.
// This hook will be called again when the Short is actually opened.
if (!videoIsOpeningOrPlaying || !SettingsEnum.RYD_ENABLED.getBoolean()) {
return;
}
if (!SettingsEnum.RYD_SHORTS.getBoolean() && PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized()) {
if (!SettingsEnum.RYD_ENABLED.getBoolean()) {
return;
}
if (videoId.equals(lastPrefetchedVideoId)) {
return;
}
lastPrefetchedVideoId = videoId;
final boolean videoIdIsShort = VideoInformation.lastVideoIdIsShort();
// Shorts shelf in home and subscription feed causes player response hook to be called,
// and the 'is opening/playing' parameter will be false.
// This hook will be called again when the Short is actually opened.
if (videoIdIsShort && (!isShortAndOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean())) {
return;
}
final boolean waitForFetchToComplete = !IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
&& videoIdIsShort && !lastPlayerResponseWasShort;
LogHelper.printDebug(() -> "Prefetching RYD for video: " + videoId);
ReturnYouTubeDislike.getFetchForVideoId(videoId);
ReturnYouTubeDislike fetch = ReturnYouTubeDislike.getFetchForVideoId(videoId);
if (waitForFetchToComplete && !fetch.fetchCompleted()) {
// This call is off the main thread, so wait until the RYD fetch completely finishes,
// otherwise if this returns before the fetch completes then the UI can
// become frozen when the main thread tries to modify the litho Shorts dislikes and
// it must wait for the fetch.
// Only need to do this for the first Short opened, as the next Short to swipe to
// are preloaded in the background.
//
// If an asynchronous litho Shorts solution is found, then this blocking call should be removed.
LogHelper.printDebug(() -> "Waiting for prefetch to complete: " + videoId);
fetch.getFetchData(20000); // Any arbitrarily large max wait time.
}
// Set the fields after the fetch completes, so any concurrent calls will also wait.
lastPlayerResponseWasShort = videoIdIsShort;
lastPrefetchedVideoId = videoId;
} catch (Exception ex) {
LogHelper.printException(() -> "preloadVideoId failure", ex);
}

View File

@@ -12,10 +12,15 @@ import java.util.Objects;
/**
* Hooking class for the current playing video.
* @noinspection unused
*/
public final class VideoInformation {
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
private static final String SEEK_METHOD_NAME = "seekTo";
/**
* Prefix present in all Short player parameters signature.
*/
private static final String SHORTS_PLAYER_PARAMETERS = "8AEB";
private static WeakReference<Object> playerControllerRef;
private static Method seekMethod;
@@ -27,6 +32,7 @@ public final class VideoInformation {
@NonNull
private static volatile String playerResponseVideoId = "";
private static volatile boolean videoIdIsShort;
/**
* The current playback speed
@@ -64,12 +70,33 @@ public final class VideoInformation {
}
}
/**
* @return If the player parameters are for a Short.
*/
public static boolean playerParametersAreShort(@NonNull String parameters) {
return parameters.startsWith(SHORTS_PLAYER_PARAMETERS);
}
/**
* Injection point.
*/
public static String newPlayerResponseSignature(@NonNull String signature, boolean isShortAndOpeningOrPlaying) {
final boolean isShort = playerParametersAreShort(signature);
if (!isShort || isShortAndOpeningOrPlaying) {
if (videoIdIsShort != isShort) {
videoIdIsShort = isShort;
LogHelper.printDebug(() -> "videoIdIsShort: " + isShort);
}
}
return signature; // Return the original value since we are observing and not modifying.
}
/**
* Injection point. Called off the main thread.
*
* @param videoId The id of the last video loaded.
*/
public static void setPlayerResponseVideoId(@NonNull String videoId, boolean videoIsOpeningOrPlaying) {
public static void setPlayerResponseVideoId(@NonNull String videoId, boolean isShortAndOpeningOrPlaying) {
if (!playerResponseVideoId.equals(videoId)) {
LogHelper.printDebug(() -> "New player response video id: " + videoId);
playerResponseVideoId = videoId;
@@ -89,7 +116,7 @@ public final class VideoInformation {
/**
* Overrides the current playback speed.
*
* <p>
* <b> Used exclusively by {@link RememberPlaybackSpeedPatch} </b>
*/
public static void overridePlaybackSpeed(float speedOverride) {
@@ -124,32 +151,39 @@ public final class VideoInformation {
/**
* Seek on the current video.
* Does not function for playback of Shorts.
*
* <p>
* Caution: If called from a videoTimeHook() callback,
* this will cause a recursive call into the same videoTimeHook() callback.
*
* @param millisecond The millisecond to seek the video to.
* @return if the seek was successful
* @return true if the seek was successful.
*/
public static boolean seekTo(final long millisecond) {
final long videoLength = getVideoLength();
// Prevent issues such as play/ pause button or autoplay not working.
final long seekToMilliseconds = Math.min(millisecond, VideoInformation.getVideoLength() - 250);
ReVancedUtils.verifyOnMainThread();
try {
LogHelper.printDebug(() -> "Seeking to " + millisecond);
return (Boolean) seekMethod.invoke(playerControllerRef.get(), millisecond);
LogHelper.printDebug(() -> "Seeking to " + seekToMilliseconds);
//noinspection DataFlowIssue
return (Boolean) seekMethod.invoke(playerControllerRef.get(), seekToMilliseconds);
} catch (Exception ex) {
LogHelper.printException(() -> "Failed to seek", ex);
return false;
}
}
/** @noinspection UnusedReturnValue*/
public static boolean seekToRelative(long millisecondsRelative) {
return seekTo(videoTime + millisecondsRelative);
}
/**
* Id of the current video playing. Includes Shorts.
* Id of the last video opened. Includes Shorts.
*
* @return The id of the video. Empty string if not set yet.
* @return The id of the video, or an empty string if no videos have been opened yet.
*/
@NonNull
public static String getVideoId() {
@@ -158,20 +192,30 @@ public final class VideoInformation {
/**
* Differs from {@link #videoId} as this is the video id for the
* last player response received, which may not be the current video playing.
*
* last player response received, which may not be the last video opened.
* <p>
* If Shorts are loading the background, this commonly will be
* different from the Short that is currently on screen.
*
* <p>
* For most use cases, you should instead use {@link #getVideoId()}.
*
* @return The id of the last video loaded. Empty string if not set yet.
* @return The id of the last video loaded, or an empty string if no videos have been loaded yet.
*/
@NonNull
public static String getPlayerResponseVideoId() {
return playerResponseVideoId;
}
/**
* @return If the last player response video id _that was opened_ was a Short.
* <p>
* Note: This value returned may not match the status of {@link #getPlayerResponseVideoId()}
* since that includes player responses for videos not opened.
*/
public static boolean lastVideoIdIsShort() {
return videoIdIsShort;
}
/**
* @return The current playback speed.
*/
@@ -192,9 +236,9 @@ public final class VideoInformation {
/**
* Playback time of the current video playing. Includes Shorts.
*
* <p>
* Value will lag behind the actual playback time by a variable amount based on the playback speed.
*
* <p>
* If playback speed is 2.0x, this value may be up to 2000ms behind the actual playback time.
* If playback speed is 1.0x, this value may be up to 1000ms behind the actual playback time.
* If playback speed is 0.5x, this value may be up to 500ms behind the actual playback time.
@@ -208,12 +252,12 @@ public final class VideoInformation {
/**
* @return If the playback is at the end of the video.
*
* <p>
* If video is playing in the background with no video visible,
* this always returns false (even if the video is actually at the end).
*
* <p>
* This is equivalent to checking for {@link VideoState#ENDED},
* but can give a more up to date result for code calling from some hooks.
* but can give a more up-to-date result for code calling from some hooks.
*
* @see VideoState
*/

View File

@@ -32,6 +32,9 @@ public final class AnnouncementsPatch {
public static void showAnnouncement(final Activity context) {
if (!SettingsEnum.ANNOUNCEMENTS.getBoolean()) return;
// Check if there is internet connection
if (!ReVancedUtils.isNetworkConnected()) return;
ReVancedUtils.runOnBackgroundThread(() -> {
try {
HttpURLConnection connection = AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT, CONSUMER);
@@ -43,7 +46,7 @@ public final class AnnouncementsPatch {
if (connection.getResponseCode() != 200) {
if (SettingsEnum.ANNOUNCEMENT_LAST_HASH.getString().isEmpty()) return;
SettingsEnum.ANNOUNCEMENT_LAST_HASH.saveValue("");
SettingsEnum.ANNOUNCEMENT_LAST_HASH.resetToDefault();
ReVancedUtils.showToastLong("Failed to get announcement");
return;
@@ -118,7 +121,7 @@ public final class AnnouncementsPatch {
*/
private static boolean emptyLastAnnouncementHash() {
if (SettingsEnum.ANNOUNCEMENT_LAST_HASH.getString().isEmpty()) return true;
SettingsEnum.ANNOUNCEMENT_LAST_HASH.saveValue("");
SettingsEnum.ANNOUNCEMENT_LAST_HASH.resetToDefault();
return false;
}

View File

@@ -12,6 +12,7 @@ import app.revanced.integrations.utils.StringTrieSearch;
public final class AdsFilter extends Filter {
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup shoppingLinks;
public AdsFilter() {
exceptions.addPatterns(
@@ -71,6 +72,11 @@ public final class AdsFilter extends Filter {
"products_in_video"
);
shoppingLinks = new StringFilterGroup(
SettingsEnum.HIDE_SHOPPING_LINKS,
"expandable_list"
);
final var webLinkPanel = new StringFilterGroup(
SettingsEnum.HIDE_WEB_SEARCH_RESULTS,
"web_link_panel"
@@ -93,6 +99,7 @@ public final class AdsFilter extends Filter {
viewProducts,
selfSponsor,
webLinkPanel,
shoppingLinks,
movieAds
);
this.identifierFilterGroupList.addAll(carouselAd);
@@ -104,6 +111,10 @@ public final class AdsFilter extends Filter {
if (exceptions.matches(path))
return false;
// Check for the index because of likelihood of false positives.
if (matchedGroup == shoppingLinks && matchedIndex != 0)
return false;
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
}

View File

@@ -12,8 +12,10 @@ import app.revanced.integrations.utils.StringTrieSearch;
public final class LayoutComponentsFilter extends Filter {
private final StringTrieSearch exceptions = new StringTrieSearch();
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch();
private static ByteArrayAsStringFilterGroup mixPlaylistsExceptions2;
private static final ByteArrayAsStringFilterGroup mixPlaylistsExceptions2 = new ByteArrayAsStringFilterGroup(
null,
"cell_description_body"
);
private final CustomFilterGroup custom;
private static final ByteArrayAsStringFilterGroup mixPlaylists = new ByteArrayAsStringFilterGroup(
@@ -25,6 +27,13 @@ public final class LayoutComponentsFilter extends Filter {
private final StringFilterGroup notifyMe;
private final StringFilterGroup expandableMetadata;
static {
mixPlaylistsExceptions.addPatterns(
"V.ED", // Playlist browse id.
"java.lang.ref.WeakReference"
);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public LayoutComponentsFilter() {
exceptions.addPatterns(
@@ -35,16 +44,6 @@ public final class LayoutComponentsFilter extends Filter {
"library_recent_shelf"
);
mixPlaylistsExceptions.addPatterns(
"V.ED", // Playlist browse id.
"java.lang.ref.WeakReference"
);
mixPlaylistsExceptions2 = new ByteArrayAsStringFilterGroup(
null,
"cell_description_body"
);
custom = new CustomFilterGroup(
SettingsEnum.CUSTOM_FILTER,
SettingsEnum.CUSTOM_FILTER_STRINGS
@@ -192,6 +191,11 @@ public final class LayoutComponentsFilter extends Filter {
"featured_channel_watermark_overlay"
);
final var forYouShelf = new StringFilterGroup(
SettingsEnum.HIDE_FOR_YOU_SHELF,
"mixed_content_shelf"
);
this.pathFilterGroupList.addAll(
channelBar,
communityPosts,
@@ -216,6 +220,7 @@ public final class LayoutComponentsFilter extends Filter {
timedReactions,
imageShelf,
channelMemberShelf,
forYouShelf,
custom
);
@@ -247,19 +252,25 @@ public final class LayoutComponentsFilter extends Filter {
* Injection point.
* Called from a different place then the other filters.
*/
public static boolean filterMixPlaylists(final Object conversionContext, final byte[] bytes) {
public static boolean filterMixPlaylists(final Object conversionContext, @Nullable final byte[] bytes) {
if (bytes == null) {
LogHelper.printDebug(() -> "bytes is null");
return false;
}
// Prevent playlist items being hidden, if a mix playlist is present in it.
if (mixPlaylistsExceptions.matches(conversionContext.toString()))
return false;
if (!mixPlaylists.check(bytes).isFiltered()) return false;
if (!mixPlaylists.check(bytes).isFiltered())
return false;
// Prevent hiding the description of some videos accidentally.
if (mixPlaylistsExceptions2.check(bytes).isFiltered()) return false;
if (mixPlaylistsExceptions2.check(bytes).isFiltered())
return false;
LogHelper.printDebug(() -> "Filtered mix playlist");
return true;
}
public static boolean showWatermark() {

View File

@@ -133,7 +133,7 @@ final class CustomFilterGroup extends StringFilterGroup {
for (String pattern : patterns) {
if (!StringTrieSearch.isValidPattern(pattern)) {
ReVancedUtils.showToastLong("Invalid custom filter, resetting to default");
setting.saveValue(setting.defaultValue);
setting.resetToDefault();
return getFilterPatterns(setting);
}
}
@@ -489,8 +489,13 @@ public final class LithoFilterPatch {
return false;
ByteBuffer protobufBuffer = bufferThreadLocal.get();
if (protobufBuffer == null || !protobufBuffer.hasArray()) {
LogHelper.printException(() -> "Proto buffer is null or has no array"); // Should never happen.
if (protobufBuffer == null) {
LogHelper.printException(() -> "Proto buffer is null"); // Should never happen.
return false;
}
if (!protobufBuffer.hasArray()) {
LogHelper.printDebug(() -> "Proto buffer does not have an array");
return false;
}

View File

@@ -53,14 +53,14 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
/**
* Injection point.
*/
public static void newPlayerResponseVideoId(String videoId, boolean videoIsOpeningOrPlaying) {
public static void newPlayerResponseVideoId(String videoId, boolean isShortAndOpeningOrPlaying) {
try {
if (!videoIsOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean()) {
if (!isShortAndOpeningOrPlaying || !SettingsEnum.RYD_SHORTS.getBoolean()) {
return;
}
synchronized (lastVideoIds) {
if (lastVideoIds.put(videoId, Boolean.TRUE) == null) {
LogHelper.printDebug(() -> "New video id: " + videoId);
LogHelper.printDebug(() -> "New Short video id: " + videoId);
}
}
} catch (Exception ex) {

View File

@@ -15,15 +15,23 @@ import java.util.Arrays;
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.
*
* Going over 8x does not increase the actual playback speed any higher,
* and the UI selector starts flickering and acting weird.
* Over 10x and the speeds show up out of order in the UI selector.
*/
public static final float MAXIMUM_PLAYBACK_SPEED = 10;
public static final float MAXIMUM_PLAYBACK_SPEED = 8;
/**
* Custom playback speeds.
*/
public static float[] customPlaybackSpeeds;
/**
* The last time the old playback menu was forcefully called.
*/
private static long lastTimeOldPlaybackMenuInvoked;
/**
* PreferenceList entries and values, of all available playback speeds.
*/
@@ -35,7 +43,7 @@ public class CustomPlaybackSpeedPatch {
private static void resetCustomSpeeds(@NonNull String toastMessage) {
ReVancedUtils.showToastLong(toastMessage);
SettingsEnum.CUSTOM_PLAYBACK_SPEEDS.saveValue(SettingsEnum.CUSTOM_PLAYBACK_SPEEDS.defaultValue);
SettingsEnum.CUSTOM_PLAYBACK_SPEEDS.resetToDefault();
}
private static void loadCustomSpeeds() {
@@ -101,36 +109,48 @@ public class CustomPlaybackSpeedPatch {
// 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();
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
// This only shows in phone layout.
final var touchInsidedView = parentView4th.getChildAt(0);
touchInsidedView.setSoundEffectsEnabled(false);
touchInsidedView.performClick();
// 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();
if (!PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible || recyclerView.getChildCount() == 0) {
return;
}
ViewGroup PlaybackSpeedParentView = (ViewGroup) recyclerView.getChildAt(0);
if (PlaybackSpeedParentView == null || PlaybackSpeedParentView.getChildCount() != 8) {
return;
}
PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
ViewGroup parentView3rd = (ViewGroup) recyclerView.getParent().getParent().getParent();
ViewGroup parentView4th = (ViewGroup) parentView3rd.getParent();
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
// This only shows in phone layout.
final var touchInsidedView = parentView4th.getChildAt(0);
touchInsidedView.setSoundEffectsEnabled(false);
touchInsidedView.performClick();
// 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();
} catch (Exception ex) {
LogHelper.printException(() -> "onFlyoutMenuCreate failure", ex);
}
});
}
public static void showOldPlaybackSpeedMenu() {
private static void showOldPlaybackSpeedMenu() {
// This method is sometimes used multiple times.
// To prevent this, ignore method reuse within 1 second.
final long now = System.currentTimeMillis();
if (now - lastTimeOldPlaybackMenuInvoked < 1000) {
LogHelper.printDebug(() -> "Ignoring call to showOldPlaybackSpeedMenu");
return;
}
lastTimeOldPlaybackMenuInvoked = now;
LogHelper.printDebug(() -> "Old video quality menu shown");
// Rest of the implementation added by patch.

View File

@@ -29,6 +29,11 @@ public class SpoofSignaturePatch {
*/
private static final String INCOGNITO_PARAMETERS = "CgIQBg==";
/**
* Parameters used when playing clips.
*/
private static final String CLIPS_PARAMETERS = "kAIB";
/**
* Parameters causing playback issues.
*/
@@ -43,16 +48,13 @@ public class SpoofSignaturePatch {
*/
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.
*/
@Nullable
private static volatile String lastPlayerResponseVideoId;
@Nullable
private static volatile Future<StoryboardRenderer> rendererFuture;
private static volatile boolean useOriginalStoryboardRenderer;
@@ -60,10 +62,13 @@ public class SpoofSignaturePatch {
private static volatile boolean isPlayingShorts;
@Nullable
private static StoryboardRenderer getRenderer() {
if (rendererFuture != null) {
private static StoryboardRenderer getRenderer(boolean waitForCompletion) {
Future<StoryboardRenderer> future = rendererFuture;
if (future != null) {
try {
return rendererFuture.get(2000, TimeUnit.MILLISECONDS);
if (waitForCompletion || future.isDone()) {
return future.get(20000, TimeUnit.MILLISECONDS); // Any arbitrarily large timeout.
} // else, return null.
} catch (TimeoutException ex) {
LogHelper.printDebug(() -> "Could not get renderer (get timed out)");
} catch (ExecutionException | InterruptedException ex) {
@@ -81,37 +86,48 @@ public class SpoofSignaturePatch {
*
* @param parameters Original protobuf parameter value.
*/
public static String spoofParameter(String parameters) {
LogHelper.printDebug(() -> "Original protobuf parameter value: " + parameters);
public static String spoofParameter(String parameters, boolean isShortAndOpeningOrPlaying) {
try {
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.
if (useOriginalStoryboardRenderer = parameters.length() > 150) return parameters;
// Shorts do not need to be spoofed.
if (useOriginalStoryboardRenderer = parameters.startsWith(SHORTS_PLAYER_PARAMETERS)) {
isPlayingShorts = true;
return parameters;
}
isPlayingShorts = false;
boolean isPlayingFeed = PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL
&& containsAny(parameters, AUTOPLAY_PARAMETERS);
if (isPlayingFeed) {
if (useOriginalStoryboardRenderer = !SettingsEnum.SPOOF_SIGNATURE_IN_FEED.getBoolean()) {
// Don't spoof the feed video playback. This will cause video playback issues,
// but only if user continues watching for more than 1 minute.
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()) {
return parameters;
}
// Spoof the feed video. Video will show up in watch history and video subtitles are missing.
fetchStoryboardRenderer();
return SCRIM_PARAMETER + INCOGNITO_PARAMETERS;
}
fetchStoryboardRenderer();
// 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.
//noinspection AssignmentUsedAsCondition
if (useOriginalStoryboardRenderer = parameters.length() > 150 || containsAny(parameters, CLIPS_PARAMETERS)) {
return parameters;
}
// Shorts do not need to be spoofed.
//noinspection AssignmentUsedAsCondition
if (useOriginalStoryboardRenderer = VideoInformation.playerParametersAreShort(parameters)) {
isPlayingShorts = true;
return parameters;
}
isPlayingShorts = false;
boolean isPlayingFeed = PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL
&& containsAny(parameters, AUTOPLAY_PARAMETERS);
if (isPlayingFeed) {
//noinspection AssignmentUsedAsCondition
if (useOriginalStoryboardRenderer = !SettingsEnum.SPOOF_SIGNATURE_IN_FEED.getBoolean()) {
// Don't spoof the feed video playback. This will cause video playback issues,
// but only if user continues watching for more than 1 minute.
return parameters;
}
// Spoof the feed video. Video will show up in watch history and video subtitles are missing.
fetchStoryboardRenderer();
return SCRIM_PARAMETER + INCOGNITO_PARAMETERS;
}
fetchStoryboardRenderer();
} catch (Exception ex) {
LogHelper.printException(() -> "spoofParameter failure", ex);
}
return INCOGNITO_PARAMETERS;
}
@@ -126,21 +142,24 @@ public class SpoofSignaturePatch {
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();
// Block until the renderer fetch completes.
// This is desired because if this returns without finishing the fetch
// then video will start playback but the storyboard is not ready yet.
getRenderer(true);
}
private static String getStoryboardRendererSpec(String originalStoryboardRendererSpec,
boolean returnNullIfLiveStream) {
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean() && !useOriginalStoryboardRenderer) {
StoryboardRenderer renderer = getRenderer();
StoryboardRenderer renderer = getRenderer(false);
if (renderer != null) {
if (returnNullIfLiveStream && renderer.isLiveStream()) return null;
return renderer.getSpec();
if (returnNullIfLiveStream && renderer.isLiveStream()) {
return null;
}
String spec = renderer.getSpec();
if (spec != null) {
return spec;
}
}
}
@@ -171,7 +190,7 @@ public class SpoofSignaturePatch {
*/
public static int getRecommendedLevel(int originalLevel) {
if (SettingsEnum.SPOOF_SIGNATURE.getBoolean() && !useOriginalStoryboardRenderer) {
StoryboardRenderer renderer = getRenderer();
StoryboardRenderer renderer = getRenderer(false);
if (renderer != null) {
Integer recommendedLevel = renderer.getRecommendedLevel();
if (recommendedLevel != null) return recommendedLevel;
@@ -186,7 +205,17 @@ public class SpoofSignaturePatch {
* if {@link SettingsEnum#SPOOF_STORYBOARD_RENDERER} is not enabled.
*/
public static boolean getSeekbarThumbnailOverrideValue() {
return SettingsEnum.SPOOF_SIGNATURE.getBoolean();
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()) {
return false;
}
StoryboardRenderer renderer = getRenderer(false);
if (renderer == null) {
// Spoof storyboard renderer is turned off,
// video is paid, or the storyboard fetch timed out.
// Show empty thumbnails so the seek time and chapters still show up.
return true;
}
return renderer.getSpec() != null;
}
/**
@@ -195,15 +224,19 @@ public class SpoofSignaturePatch {
* @param view seekbar thumbnail view. Includes both shorts and regular videos.
*/
public static void seekbarImageViewCreated(ImageView view) {
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()
|| SettingsEnum.SPOOF_STORYBOARD_RENDERER.getBoolean()) {
return;
}
if (isPlayingShorts) return;
try {
if (!SettingsEnum.SPOOF_SIGNATURE.getBoolean()
|| SettingsEnum.SPOOF_STORYBOARD_RENDERER.getBoolean()) {
return;
}
if (isPlayingShorts) return;
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.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);
} catch (Exception ex) {
LogHelper.printException(() -> "seekbarImageViewCreated failure", ex);
}
}
}

View File

@@ -1,23 +1,23 @@
package app.revanced.integrations.patches.spoof;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
public final class StoryboardRenderer {
@Nullable
private final String spec;
private final boolean isLiveStream;
@Nullable
private final Integer recommendedLevel;
public StoryboardRenderer(String spec, boolean isLiveStream, @Nullable Integer recommendedLevel) {
public StoryboardRenderer(@Nullable String spec, boolean isLiveStream, @Nullable Integer recommendedLevel) {
this.spec = spec;
this.isLiveStream = isLiveStream;
this.recommendedLevel = recommendedLevel;
}
@NonNull
@Nullable
public String getSpec() {
return spec;
}

View File

@@ -23,6 +23,11 @@ final class PlayerRoutes {
static final String ANDROID_INNER_TUBE_BODY;
static final String TV_EMBED_INNER_TUBE_BODY;
/**
* TCP connection and HTTP read timeout
*/
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 4 * 1000; // 4 Seconds.
static {
JSONObject innerTubeBody = new JSONObject();
@@ -88,8 +93,8 @@ final class PlayerRoutes {
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.setConnectTimeout(CONNECTION_TIMEOUT_MILLISECONDS);
connection.setReadTimeout(CONNECTION_TIMEOUT_MILLISECONDS);
return connection;
}
}

View File

@@ -1,27 +1,56 @@
package app.revanced.integrations.patches.spoof.requests;
import static app.revanced.integrations.patches.spoof.requests.PlayerRoutes.ANDROID_INNER_TUBE_BODY;
import static app.revanced.integrations.patches.spoof.requests.PlayerRoutes.GET_STORYBOARD_SPEC_RENDERER;
import static app.revanced.integrations.patches.spoof.requests.PlayerRoutes.TV_EMBED_INNER_TUBE_BODY;
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.io.IOException;
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.*;
import app.revanced.integrations.patches.spoof.StoryboardRenderer;
import app.revanced.integrations.requests.Requester;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
public class StoryboardRendererRequester {
/**
* For videos that have no storyboard.
* Usually for low resolution videos as old as YouTube itself.
* Does not include paid videos where the renderer fetch fails.
*/
private static final StoryboardRenderer emptyStoryboard
= new StoryboardRenderer(null, false, null);
private StoryboardRendererRequester() {
}
private static void randomlyWaitIfLocallyDebugging() {
final boolean randomlyWait = false; // Enable to simulate slow connection responses.
if (randomlyWait) {
final long maximumTimeToRandomlyWait = 10000;
ReVancedUtils.doNothingForDuration(maximumTimeToRandomlyWait);
}
}
private static void handleConnectionError(@NonNull String toastMessage, @Nullable Exception ex,
boolean showToastOnIOException) {
if (showToastOnIOException) ReVancedUtils.showToastShort(toastMessage);
LogHelper.printInfo(() -> toastMessage, ex);
}
@Nullable
private static JSONObject fetchPlayerResponse(@NonNull String requestBody) {
private static JSONObject fetchPlayerResponse(@NonNull String requestBody, boolean showToastOnIOException) {
final long startTime = System.currentTimeMillis();
try {
ReVancedUtils.verifyOffMainThread();
@@ -33,14 +62,21 @@ public class StoryboardRendererRequester {
connection.getOutputStream().write(innerTubeBody, 0, innerTubeBody.length);
final int responseCode = connection.getResponseCode();
randomlyWaitIfLocallyDebugging();
if (responseCode == 200) return Requester.parseJSONObject(connection);
LogHelper.printException(() -> "API not available: " + responseCode);
// Always show a toast for this, as a non 200 response means something is broken.
handleConnectionError("Spoof storyboard not available: " + responseCode,
null, showToastOnIOException || SettingsEnum.DEBUG_TOAST_ON_ERROR.getBoolean());
connection.disconnect();
} catch (SocketTimeoutException ex) {
LogHelper.printException(() -> "API timed out", ex);
handleConnectionError("Spoof storyboard temporarily not available (API timed out)",
ex, showToastOnIOException);
} catch (IOException ex) {
handleConnectionError("Spoof storyboard temporarily not available: " + ex.getMessage(),
ex, showToastOnIOException);
} catch (Exception ex) {
LogHelper.printException(() -> "Failed to fetch storyboard URL", ex);
LogHelper.printException(() -> "Spoof storyboard fetch failed", ex); // Should never happen.
} finally {
LogHelper.printDebug(() -> "Request took: " + (System.currentTimeMillis() - startTime) + "ms");
}
@@ -64,8 +100,9 @@ public class StoryboardRendererRequester {
* @return StoryboardRenderer or null if playabilityStatus is not OK.
*/
@Nullable
private static StoryboardRenderer getStoryboardRendererUsingBody(@NonNull String innerTubeBody) {
final JSONObject playerResponse = fetchPlayerResponse(innerTubeBody);
private static StoryboardRenderer getStoryboardRendererUsingBody(@NonNull String innerTubeBody,
boolean showToastOnIOException) {
final JSONObject playerResponse = fetchPlayerResponse(innerTubeBody, showToastOnIOException);
if (playerResponse != null && isPlayabilityStatusOk(playerResponse))
return getStoryboardRendererUsingResponse(playerResponse);
@@ -76,6 +113,10 @@ public class StoryboardRendererRequester {
private static StoryboardRenderer getStoryboardRendererUsingResponse(@NonNull JSONObject playerResponse) {
try {
LogHelper.printDebug(() -> "Parsing response: " + playerResponse);
if (!playerResponse.has("storyboards")) {
LogHelper.printDebug(() -> "Using empty storyboard");
return emptyStoryboard;
}
final JSONObject storyboards = playerResponse.getJSONObject("storyboards");
final boolean isLiveStream = storyboards.has("playerLiveStoryboardSpecRenderer");
final String storyboardsRendererTag = isLiveStream
@@ -103,23 +144,19 @@ public class StoryboardRendererRequester {
@Nullable
public static StoryboardRenderer getStoryboardRenderer(@NonNull String videoId) {
try {
Objects.requireNonNull(videoId);
Objects.requireNonNull(videoId);
var renderer = getStoryboardRendererUsingBody(String.format(ANDROID_INNER_TUBE_BODY, videoId));
var renderer = getStoryboardRendererUsingBody(
String.format(ANDROID_INNER_TUBE_BODY, videoId), false);
if (renderer == null) {
LogHelper.printDebug(() -> videoId + " not available using Android client");
renderer = getStoryboardRendererUsingBody(
String.format(TV_EMBED_INNER_TUBE_BODY, videoId, videoId), true);
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");
}
LogHelper.printDebug(() -> videoId + " not available using TV embedded client");
}
return renderer;
} catch (Exception ex) {
LogHelper.printException(() -> "Failed to fetch storyboard URL", ex);
}
return null;
return renderer;
}
}

View File

@@ -48,7 +48,7 @@ public final class SeekbarColorPatch {
Color.colorToHSV(seekbarColor, customSeekbarColorHSV);
} catch (Exception ex) {
ReVancedUtils.showToastShort("Invalid seekbar color value. Using default value.");
SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.saveValue(SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.defaultValue);
SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.resetToDefault();
loadCustomSeekbarColor();
}
}

View File

@@ -62,12 +62,12 @@ public class ReturnYouTubeDislikeApi {
* How long to wait until API calls are resumed, if the API requested a back off.
* No clear guideline of how long to wait until resuming.
*/
private static final int BACKOFF_RATE_LIMIT_MILLISECONDS = 4 * 60 * 1000; // 4 Minutes.
private static final int BACKOFF_RATE_LIMIT_MILLISECONDS = 5 * 60 * 1000; // 5 Minutes.
/**
* How long to wait until API calls are resumed, if any connection error occurs.
*/
private static final int BACKOFF_CONNECTION_ERROR_MILLISECONDS = 60 * 1000; // 60 Seconds.
private static final int BACKOFF_CONNECTION_ERROR_MILLISECONDS = 2 * 60 * 1000; // 2 Minutes.
/**
* If non zero, then the system time of when API calls can resume.
@@ -139,25 +139,13 @@ public class ReturnYouTubeDislikeApi {
* Simulates a slow response by doing meaningless calculations.
* Used to debug the app UI and verify UI timeout logic works
*/
@SuppressWarnings("UnusedReturnValue")
private static long randomlyWaitIfLocallyDebugging() {
private static void randomlyWaitIfLocallyDebugging() {
final boolean DEBUG_RANDOMLY_DELAY_NETWORK_CALLS = false; // set true to debug UI
if (DEBUG_RANDOMLY_DELAY_NETWORK_CALLS) {
final long amountOfTimeToWaste = (long) (Math.random()
* (API_GET_VOTES_TCP_TIMEOUT_MILLISECONDS + API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS));
final long timeCalculationStarted = System.currentTimeMillis();
LogHelper.printDebug(() -> "Artificially creating network delay of: " + amountOfTimeToWaste + "ms");
long meaninglessValue = 0;
while (System.currentTimeMillis() - timeCalculationStarted < amountOfTimeToWaste) {
// could do a thread sleep, but that will trigger an exception if the thread is interrupted
meaninglessValue += Long.numberOfLeadingZeros((long)Math.exp(Math.random()));
}
// return the value, otherwise the compiler or VM might optimize and remove the meaningless time wasting work,
// leaving an empty loop that hammers on the System.currentTimeMillis native call
return meaninglessValue;
ReVancedUtils.doNothingForDuration(amountOfTimeToWaste);
}
return 0;
}
/**

View File

@@ -50,18 +50,24 @@ public enum SettingsEnum {
HIDE_MERCHANDISE_BANNERS("revanced_hide_merchandise_banners", BOOLEAN, TRUE),
HIDE_PAID_CONTENT("revanced_hide_paid_content_ads", BOOLEAN, TRUE),
HIDE_PRODUCTS_BANNER("revanced_hide_products_banner", BOOLEAN, TRUE),
HIDE_SHOPPING_LINKS("revanced_hide_shopping_links", BOOLEAN, TRUE),
HIDE_SELF_SPONSOR("revanced_hide_self_sponsor_ads", BOOLEAN, TRUE),
HIDE_VIDEO_ADS("revanced_hide_video_ads", BOOLEAN, TRUE, true),
HIDE_WEB_SEARCH_RESULTS("revanced_hide_web_search_results", BOOLEAN, TRUE),
// Layout
ALT_THUMBNAIL("revanced_alt_thumbnail", BOOLEAN, FALSE),
ALT_THUMBNAIL_TYPE("revanced_alt_thumbnail_type", INTEGER, 2, parents(ALT_THUMBNAIL)),
ALT_THUMBNAIL_FAST_QUALITY("revanced_alt_thumbnail_fast_quality", BOOLEAN, FALSE, parents(ALT_THUMBNAIL)),
ALT_THUMBNAIL_STILLS("revanced_alt_thumbnail_stills", BOOLEAN, FALSE),
ALT_THUMBNAIL_STILLS_TIME("revanced_alt_thumbnail_stills_time", INTEGER, 2, parents(ALT_THUMBNAIL_STILLS)),
ALT_THUMBNAIL_STILLS_FAST("revanced_alt_thumbnail_stills_fast", BOOLEAN, FALSE, parents(ALT_THUMBNAIL_STILLS)),
ALT_THUMBNAIL_DEARROW("revanced_alt_thumbnail_dearrow", BOOLEAN, false),
ALT_THUMBNAIL_DEARROW_API_URL("revanced_alt_thumbnail_dearrow_api_url", STRING,
"https://dearrow-thumb.ajay.app/api/v1/getThumbnail", true, parents(ALT_THUMBNAIL_DEARROW)),
ALT_THUMBNAIL_DEARROW_CONNECTION_TOAST("revanced_alt_thumbnail_dearrow_connection_toast", BOOLEAN, TRUE, parents(ALT_THUMBNAIL_DEARROW)),
CUSTOM_FILTER("revanced_custom_filter", BOOLEAN, FALSE),
CUSTOM_FILTER_STRINGS("revanced_custom_filter_strings", STRING, "", true, parents(CUSTOM_FILTER)),
DISABLE_FULLSCREEN_AMBIENT_MODE("revanced_disable_fullscreen_ambient_mode", BOOLEAN, TRUE, true),
DISABLE_RESUMING_SHORTS_PLAYER("revanced_disable_resuming_shorts_player", BOOLEAN, FALSE),
DISABLE_ROLLING_NUMBER_ANIMATIONS("revanced_disable_rolling_number_animations", BOOLEAN, FALSE),
DISABLE_SUGGESTED_VIDEO_END_SCREEN("revanced_disable_suggested_video_end_screen", BOOLEAN, TRUE),
GRADIENT_LOADING_SCREEN("revanced_gradient_loading_screen", BOOLEAN, FALSE),
HIDE_ALBUM_CARDS("revanced_hide_album_cards", BOOLEAN, FALSE, true),
@@ -113,6 +119,7 @@ public enum SettingsEnum {
HIDE_TIMESTAMP("revanced_hide_timestamp", BOOLEAN, FALSE),
@Deprecated HIDE_VIDEO_WATERMARK("revanced_hide_video_watermark", BOOLEAN, TRUE),
HIDE_VIDEO_CHANNEL_WATERMARK("revanced_hide_channel_watermark", BOOLEAN, TRUE),
HIDE_FOR_YOU_SHELF("revanced_hide_for_you_shelf", BOOLEAN, TRUE),
HIDE_VIDEO_QUALITY_MENU_FOOTER("revanced_hide_video_quality_menu_footer", BOOLEAN, TRUE),
PLAYER_OVERLAY_OPACITY("revanced_player_overlay_opacity", INTEGER, 100, true),
PLAYER_POPUP_PANELS("revanced_hide_player_popup_panels", BOOLEAN, FALSE),
@@ -122,6 +129,8 @@ public enum SettingsEnum {
TABLET_LAYOUT("revanced_tablet_layout", BOOLEAN, FALSE, true, "revanced_tablet_layout_user_dialog_message"),
USE_TABLET_MINIPLAYER("revanced_tablet_miniplayer", BOOLEAN, FALSE, true),
WIDE_SEARCHBAR("revanced_wide_searchbar", BOOLEAN, FALSE, true),
START_PAGE("revanced_start_page", STRING, ""),
// Description
HIDE_CHAPTERS("revanced_hide_chapters", BOOLEAN, TRUE),
HIDE_INFO_CARDS_SECTION("revanced_hide_info_cards_section", BOOLEAN, TRUE),
@@ -427,7 +436,7 @@ public enum SettingsEnum {
LogHelper.printInfo(() -> "Migrating old setting of '" + oldSetting.value
+ "' from: " + oldSetting + " into replacement setting: " + newSetting);
newSetting.saveValue(oldSetting.value);
oldSetting.saveValue(oldSetting.defaultValue); // reset old value
oldSetting.resetToDefault();
}
}
@@ -519,6 +528,13 @@ public enum SettingsEnum {
}
}
/**
* Identical to calling {@link #saveValue(Object)} using {@link #defaultValue}.
*/
public void resetToDefault() {
saveValue(defaultValue);
}
/**
* @return if this setting can be configured and used.
* <p>
@@ -691,7 +707,7 @@ public enum SettingsEnum {
} else if (setting.includeWithImportExport() && !setting.isSetToDefault()) {
LogHelper.printDebug(() -> "Resetting to default: " + setting);
rebootSettingChanged |= setting.rebootApp;
setting.saveValue(setting.defaultValue);
setting.resetToDefault();
}
}
numberOfSettingsImported += SponsorBlockSettings.importCategoriesFromFlatJson(json);

View File

@@ -0,0 +1,35 @@
package app.revanced.integrations.settingsmenu;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.preference.Preference;
import android.util.AttributeSet;
/**
* Allows tapping the DeArrow about preference to open the DeArrow website.
*/
@SuppressWarnings("unused")
public class AlternativeThumbnailsAboutDeArrowPreference extends Preference {
{
setOnPreferenceClickListener(pref -> {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("https://dearrow.ajay.app"));
pref.getContext().startActivity(i);
return false;
});
}
public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public AlternativeThumbnailsAboutDeArrowPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AlternativeThumbnailsAboutDeArrowPreference(Context context) {
super(context);
}
}

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