Compare commits

...

42 Commits

Author SHA1 Message Date
semantic-release-bot
0338d0acd3 chore: Release v5.14.0-dev.6 [skip ci]
# [5.14.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.5...v5.14.0-dev.6) (2025-03-07)

### Bug Fixes

* **YouTube - Hide layout components:** Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled ([99879f6](99879f6e0a))
2025-03-07 17:40:21 +00:00
LisoUseInAIKyrios
99879f6e0a fix(YouTube - Hide layout components): Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled 2025-03-07 19:37:23 +02:00
github-actions[bot]
f0c70de602 chore: Sync translations (#4562) 2025-03-07 19:35:55 +02:00
LisoUseInAIKyrios
737ae07a06 refactor(YouTube): Sort no title preference group by first sub preference title 2025-03-06 21:15:29 +02:00
semantic-release-bot
2c51de59de chore: Release v5.14.0-dev.5 [skip ci]
# [5.14.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.4...v5.14.0-dev.5) (2025-03-06)

### Features

* **Infinity for Reddit:** Add support for package name on IzzyOnDroid ([#4554](https://github.com/ReVanced/revanced-patches/issues/4554)) ([df3dc1c](df3dc1c0b2))
2025-03-06 18:27:44 +00:00
ILoveOpenSourceApplications
df3dc1c0b2 feat(Infinity for Reddit): Add support for package name on IzzyOnDroid (#4554) 2025-03-06 20:24:54 +02:00
github-actions[bot]
074c948581 chore: Sync translations (#4556) 2025-03-06 20:24:22 +02:00
semantic-release-bot
2a88b1f895 chore: Release v5.14.0-dev.4 [skip ci]
# [5.14.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.3...v5.14.0-dev.4) (2025-03-06)

### Bug Fixes

* **YouTube:** Combine `Restore old video quality menu` and `Remember video quality` into `Video quality` patch ([#4552](https://github.com/ReVanced/revanced-patches/issues/4552)) ([ee5c830](ee5c830df8))
2025-03-06 12:59:39 +00:00
LisoUseInAIKyrios
ee5c830df8 fix(YouTube): Combine Restore old video quality menu and Remember video quality into Video quality patch (#4552) 2025-03-06 14:56:32 +02:00
semantic-release-bot
e63a4b31f3 chore: Release v5.14.0-dev.3 [skip ci]
# [5.14.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.2...v5.14.0-dev.3) (2025-03-06)

### Bug Fixes

* **Boost for reddit - Client spoof:** Use a different user agent to combat Reddit's API issues ([8d0bca3](8d0bca3b03))
2025-03-06 10:36:52 +00:00
oSumAtrIX
8d0bca3b03 fix(Boost for reddit - Client spoof): Use a different user agent to combat Reddit's API issues 2025-03-06 11:33:06 +01:00
semantic-release-bot
c162d65d5b chore: Release v5.14.0-dev.2 [skip ci]
# [5.14.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.1...v5.14.0-dev.2) (2025-03-06)

### Bug Fixes

* **YouTube - Hide ads:** Hide new type of buttoned ad ([#4528](https://github.com/ReVanced/revanced-patches/issues/4528)) ([67dcd09](67dcd091c4))
2025-03-06 09:30:51 +00:00
ILoveOpenSourceApplications
67dcd091c4 fix(YouTube - Hide ads): Hide new type of buttoned ad (#4528) 2025-03-06 11:27:37 +02:00
github-actions[bot]
ac5ce2d67f chore: Sync translations (#4553) 2025-03-06 11:26:20 +02:00
LisoUseInAIKyrios
4b78d056fd ci: Pull Crowdin strings less often
Crowdin is starting to give errors and pulling less often may help.
2025-03-06 11:25:22 +02:00
semantic-release-bot
f8c901b2c1 chore: Release v5.14.0-dev.1 [skip ci]
# [5.14.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.1-dev.1...v5.14.0-dev.1) (2025-03-06)

### Features

* **YouTube - Remember video quality:** Add separate Shorts default quality settings ([#4543](https://github.com/ReVanced/revanced-patches/issues/4543)) ([2a67c31](2a67c312e1))
2025-03-06 06:49:39 +00:00
alieRN
2a67c312e1 feat(YouTube - Remember video quality): Add separate Shorts default quality settings (#4543) 2025-03-06 08:46:33 +02:00
semantic-release-bot
a7eed30f46 chore: Release v5.13.1-dev.1 [skip ci]
## [5.13.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.0...v5.13.1-dev.1) (2025-03-06)

### Bug Fixes

* **YouTube - Change form factor:** Restore Automotive form factor watch history menu, channel pages, and community posts ([#4541](https://github.com/ReVanced/revanced-patches/issues/4541)) ([e2de2d8](e2de2d8d44))
2025-03-06 06:28:57 +00:00
LisoUseInAIKyrios
e2de2d8d44 fix(YouTube - Change form factor): Restore Automotive form factor watch history menu, channel pages, and community posts (#4541) 2025-03-06 08:26:09 +02:00
github-actions[bot]
7ebbf356c0 chore: Sync translations (#4550) 2025-03-06 08:25:49 +02:00
ILoveOpenSourceApplications
2ced5c6e2a refactor(YouTube): Use more consistent strings (#4526) 2025-03-05 08:55:44 +02:00
semantic-release-bot
4a090ba659 chore: Release v5.13.0 [skip ci]
# [5.13.0](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0) (2025-03-03)

### Bug Fixes

* **TikTok:** Resolve startup app crash ([3c52ab8](3c52ab8017))
* **TikTok:** Resolve startup app crash ([c817977](c8179776ed))
* **TikTok:** Resolve startup app crash ([d5aab3d](d5aab3d464))
* **TikTok:** Resolve startup app crash ([348f7e1](348f7e12cb))
* **YouTube - Copy video URL:** Use correct button ordering ([d77d5bf](d77d5bfbdd))
* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([1b60a72](1b60a72ede))
* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([94fb367](94fb367618))
* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([7cc939a](7cc939ab03))
* **YouTube - Hide video action buttons:** Move 'Disable Like and Subscribe glow' to action buttons settings menu ([7991c80](7991c80129))
* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([4ae1155](4ae1155e51))
* **YouTube - Spoof app version:** Force old settings menus if spoofing to older app targets ([#4490](https://github.com/ReVanced/revanced-patches/issues/4490)) ([0c0bbb8](0c0bbb8713))
* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([cbbf474](cbbf474c50))
* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([329f993](329f993024))
* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([e664a24](e664a24f73))
* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([a2c79f1](a2c79f1349))
* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([f5dd902](f5dd902915))

### Features

* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([fb8dbb4](fb8dbb4723))
* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([93ea250](93ea250bf3))
* **YouTube - Navigation buttons:** Add 'Hide notifications' setting ([#4485](https://github.com/ReVanced/revanced-patches/issues/4485)) ([d6eae01](d6eae01e12))
* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([3548359](354835966d))
2025-03-03 07:01:16 +00:00
LisoUseInAIKyrios
cb609a6d9d chore: Merge branch dev to main (#4470) 2025-03-03 08:57:51 +02:00
github-actions[bot]
42e6de9e8f chore: Sync translations (#4525) 2025-03-03 08:55:55 +02:00
semantic-release-bot
c4a5b9a28c chore: Release v5.13.0-dev.19 [skip ci]
# [5.13.0-dev.19](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.18...v5.13.0-dev.19) (2025-03-02)

### Bug Fixes

* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([cbbf474](cbbf474c50))
2025-03-02 15:44:18 +00:00
github-actions[bot]
c86c85947f chore: Sync translations (#4523) 2025-03-02 17:40:33 +02:00
LisoUseInAIKyrios
cbbf474c50 fix(YouTube - Spoof video streams): Resolve playback issues with dynamic player config (#4521) 2025-03-02 17:38:43 +02:00
semantic-release-bot
f147b7b73d chore: Release v5.13.0-dev.18 [skip ci]
# [5.13.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.17...v5.13.0-dev.18) (2025-02-28)

### Features

* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([fb8dbb4](fb8dbb4723))
2025-02-28 08:33:53 +00:00
tillcash
fb8dbb4723 feat(Infinity for Reddit): Add support for Infinity for Reddit Plus (#4511) 2025-02-28 10:31:02 +02:00
github-actions[bot]
1e0d27e689 chore: Sync translations (#4517) 2025-02-28 10:30:30 +02:00
semantic-release-bot
a2185bce09 chore: Release v5.13.0-dev.17 [skip ci]
# [5.13.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.16...v5.13.0-dev.17) (2025-02-27)

### Bug Fixes

* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([1b60a72](1b60a72ede))
2025-02-27 13:23:12 +00:00
ILoveOpenSourceApplications
1b60a72ede fix(YouTube - Hide filter bar): Fix Hide in feed not working in subscriptions feed (#4512) 2025-02-27 15:20:30 +02:00
semantic-release-bot
12b4ee04ad chore: Release v5.13.0-dev.16 [skip ci]
# [5.13.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.15...v5.13.0-dev.16) (2025-02-27)

### Features

* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([93ea250](93ea250bf3))
2025-02-27 06:12:28 +00:00
github-actions[bot]
f9a6cc96de chore: Sync translations (#4510) 2025-02-27 08:09:23 +02:00
Jasper Abbink
93ea250bf3 feat(NU.nl): Add Hide ads and Spoof Certificate patch (#4368)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-02-27 08:07:54 +02:00
semantic-release-bot
fdb946a2cc chore: Release v5.13.0-dev.15 [skip ci]
# [5.13.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.14...v5.13.0-dev.15) (2025-02-25)

### Bug Fixes

* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([7cc939a](7cc939ab03))
2025-02-25 15:42:36 +00:00
ILoveOpenSourceApplications
7cc939ab03 fix(YouTube - Hide player components): Show correct end video thumbnail if Hide end screen suggested video is enabled (#4502)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-02-25 17:38:16 +02:00
github-actions[bot]
228d72428d chore: Sync translations (#4505) 2025-02-25 16:55:10 +02:00
semantic-release-bot
4db7ab4207 chore: Release v5.13.0-dev.14 [skip ci]
# [5.13.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.13...v5.13.0-dev.14) (2025-02-25)

### Bug Fixes

* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([329f993](329f993024))
2025-02-25 13:37:45 +00:00
MarcaD
329f993024 fix(YouTube - Swipe controls): Adjust the overlay text size (#4503) 2025-02-25 15:34:37 +02:00
github-actions[bot]
7cd1fb22d8 chore: Sync translations (#4504) 2025-02-25 15:34:19 +02:00
LisoUseInAIKyrios
ae111bc0b9 refactor(YouTube - Force original audio): Adjust settings text 2025-02-25 11:35:44 +02:00
173 changed files with 4017 additions and 2338 deletions

View File

@@ -2,7 +2,7 @@ name: Pull strings
on: on:
schedule: schedule:
- cron: "0 */6 * * *" - cron: "0 */8 * * *"
workflow_dispatch: workflow_dispatch:
jobs: jobs:

View File

@@ -1,3 +1,124 @@
# [5.14.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.5...v5.14.0-dev.6) (2025-03-07)
### Bug Fixes
* **YouTube - Hide layout components:** Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled ([62a6164](https://github.com/ReVanced/revanced-patches/commit/62a6164b88b64200b517a5ba6b800d8214dbbad8))
# [5.14.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.4...v5.14.0-dev.5) (2025-03-06)
### Features
* **Infinity for Reddit:** Add support for package name on IzzyOnDroid ([#4554](https://github.com/ReVanced/revanced-patches/issues/4554)) ([cf9f959](https://github.com/ReVanced/revanced-patches/commit/cf9f959923076c10a7f0a29f6ba277f5a055ec07))
# [5.14.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.3...v5.14.0-dev.4) (2025-03-06)
### Bug Fixes
* **YouTube:** Combine `Restore old video quality menu` and `Remember video quality` into `Video quality` patch ([#4552](https://github.com/ReVanced/revanced-patches/issues/4552)) ([ee67b76](https://github.com/ReVanced/revanced-patches/commit/ee67b763d5c5947a5b1ef4420b1efa820ed6af83))
# [5.14.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.2...v5.14.0-dev.3) (2025-03-06)
### Bug Fixes
* **Boost for reddit - Client spoof:** Use a different user agent to combat Reddit's API issues ([5d3c817](https://github.com/ReVanced/revanced-patches/commit/5d3c8175b34a3f6ae2732b25db0851773a8c000d))
# [5.14.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.1...v5.14.0-dev.2) (2025-03-06)
### Bug Fixes
* **YouTube - Hide ads:** Hide new type of buttoned ad ([#4528](https://github.com/ReVanced/revanced-patches/issues/4528)) ([4387a7b](https://github.com/ReVanced/revanced-patches/commit/4387a7b131f49729e902e008bb4cec073635c040))
# [5.14.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.1-dev.1...v5.14.0-dev.1) (2025-03-06)
### Features
* **YouTube - Remember video quality:** Add separate Shorts default quality settings ([#4543](https://github.com/ReVanced/revanced-patches/issues/4543)) ([88142ab](https://github.com/ReVanced/revanced-patches/commit/88142ab464192b564b1b8d56a6b45663f77f5e00))
## [5.13.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.0...v5.13.1-dev.1) (2025-03-06)
### Bug Fixes
* **YouTube - Change form factor:** Restore Automotive form factor watch history menu, channel pages, and community posts ([#4541](https://github.com/ReVanced/revanced-patches/issues/4541)) ([aa5c001](https://github.com/ReVanced/revanced-patches/commit/aa5c001968446e5270c756256724e917009612cd))
# [5.13.0](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0) (2025-03-03)
### Bug Fixes
* **TikTok:** Resolve startup app crash ([18c0fc2](https://github.com/ReVanced/revanced-patches/commit/18c0fc2a7f186f50a904fd25dbaa739abdd24993))
* **TikTok:** Resolve startup app crash ([6466398](https://github.com/ReVanced/revanced-patches/commit/64663983b84de1f28636205f61bf0a24c83968d1))
* **TikTok:** Resolve startup app crash ([c14bc24](https://github.com/ReVanced/revanced-patches/commit/c14bc244550de30eca975ca7c09e8eb0c47534b5))
* **TikTok:** Resolve startup app crash ([d700076](https://github.com/ReVanced/revanced-patches/commit/d7000768a5e5a688c9f4e48858ac34e352222c1e))
* **YouTube - Copy video URL:** Use correct button ordering ([5e622cc](https://github.com/ReVanced/revanced-patches/commit/5e622ccf66d34af31c6026fa7f4d332460c6ecb0))
* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([634d0ee](https://github.com/ReVanced/revanced-patches/commit/634d0ee12e31491c7ee1d4ceb002daf8366a3c15))
* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([4ac8854](https://github.com/ReVanced/revanced-patches/commit/4ac8854b99808a8957f3b0b7438e1e0cdedffbaf))
* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([6c4885a](https://github.com/ReVanced/revanced-patches/commit/6c4885a1d5dfff50100b01840b5552d92e83ee4a))
* **YouTube - Hide video action buttons:** Move 'Disable Like and Subscribe glow' to action buttons settings menu ([29b265d](https://github.com/ReVanced/revanced-patches/commit/29b265d8fdaa48502650be9623bfc518a57a0bb1))
* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([edf66f4](https://github.com/ReVanced/revanced-patches/commit/edf66f4e16d46156cb8b8e31d18cb8dbcb87737e))
* **YouTube - Spoof app version:** Force old settings menus if spoofing to older app targets ([#4490](https://github.com/ReVanced/revanced-patches/issues/4490)) ([45e7c46](https://github.com/ReVanced/revanced-patches/commit/45e7c46dd9c70c926b8b1a97ada668f90f5f6f8c))
* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([647e764](https://github.com/ReVanced/revanced-patches/commit/647e7642efc0c00db17ccb6a620d1c96ccf4afed))
* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([6dc4bf7](https://github.com/ReVanced/revanced-patches/commit/6dc4bf75e09ed6f05534919d7b769b720043abce))
* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([63fe870](https://github.com/ReVanced/revanced-patches/commit/63fe870d48ca2217327b952bde241b7f16ced850))
* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([bf8e775](https://github.com/ReVanced/revanced-patches/commit/bf8e7759f9bdbdfef419a879fb3dd7cf0dff0098))
* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([1f08047](https://github.com/ReVanced/revanced-patches/commit/1f08047b48cc9555a4887d16ec7219a55a77251f))
### Features
* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([d74732b](https://github.com/ReVanced/revanced-patches/commit/d74732b7596104321bde263201d95649e4bd0eee))
* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([f3268fb](https://github.com/ReVanced/revanced-patches/commit/f3268fb03ca25fb5465e36015b6c9dec2c84a655))
* **YouTube - Navigation buttons:** Add 'Hide notifications' setting ([#4485](https://github.com/ReVanced/revanced-patches/issues/4485)) ([506d241](https://github.com/ReVanced/revanced-patches/commit/506d2414bbc760e764e5a514b32926083d6ecb6b))
* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([198e4d2](https://github.com/ReVanced/revanced-patches/commit/198e4d2a2315c24a09eb9ecfefbd131a75384d2c))
# [5.13.0-dev.19](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.18...v5.13.0-dev.19) (2025-03-02)
### Bug Fixes
* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([647e764](https://github.com/ReVanced/revanced-patches/commit/647e7642efc0c00db17ccb6a620d1c96ccf4afed))
# [5.13.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.17...v5.13.0-dev.18) (2025-02-28)
### Features
* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([d74732b](https://github.com/ReVanced/revanced-patches/commit/d74732b7596104321bde263201d95649e4bd0eee))
# [5.13.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.16...v5.13.0-dev.17) (2025-02-27)
### Bug Fixes
* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([634d0ee](https://github.com/ReVanced/revanced-patches/commit/634d0ee12e31491c7ee1d4ceb002daf8366a3c15))
# [5.13.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.15...v5.13.0-dev.16) (2025-02-27)
### Features
* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([f3268fb](https://github.com/ReVanced/revanced-patches/commit/f3268fb03ca25fb5465e36015b6c9dec2c84a655))
# [5.13.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.14...v5.13.0-dev.15) (2025-02-25)
### Bug Fixes
* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([6c4885a](https://github.com/ReVanced/revanced-patches/commit/6c4885a1d5dfff50100b01840b5552d92e83ee4a))
# [5.13.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.13...v5.13.0-dev.14) (2025-02-25)
### Bug Fixes
* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([6dc4bf7](https://github.com/ReVanced/revanced-patches/commit/6dc4bf75e09ed6f05534919d7b769b720043abce))
# [5.13.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.12...v5.13.0-dev.13) (2025-02-24) # [5.13.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.12...v5.13.0-dev.13) (2025-02-24)

View File

@@ -0,0 +1,4 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:nunl:stub"))
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,114 @@
package app.revanced.extension.nunl.ads;
import nl.nu.performance.api.client.interfaces.Block;
import nl.nu.performance.api.client.unions.SmallArticleLinkFlavor;
import nl.nu.performance.api.client.objects.*;
import java.util.ArrayList;
import java.util.List;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class HideAdsPatch {
private static final String[] blockedHeaderBlocks = {
"Aanbiedingen (Adverteerders)",
"Aangeboden door NUshop"
};
// "Rubrieken" menu links to ads.
private static final String[] blockedLinkBlocks = {
"Van onze adverteerders"
};
public static void filterAds(List<Block> blocks) {
try {
ArrayList<Block> cleanedList = new ArrayList<>();
boolean skipFullHeader = false;
boolean skipUntilDivider = false;
int index = 0;
while (index < blocks.size()) {
Block currentBlock = blocks.get(index);
// Because of pagination, we might not see the Divider in front of it.
// Just remove it as is and leave potential extra spacing visible on the screen.
if (currentBlock instanceof DpgBannerBlock) {
index++;
continue;
}
if (index + 1 < blocks.size()) {
// Filter Divider -> DpgMediaBanner -> Divider.
if (currentBlock instanceof DividerBlock
&& blocks.get(index + 1) instanceof DpgBannerBlock) {
index += 2;
continue;
}
// Filter Divider -> LinkBlock (... -> LinkBlock -> LinkBlock-> LinkBlock -> Divider).
if (currentBlock instanceof DividerBlock
&& blocks.get(index + 1) instanceof LinkBlock linkBlock) {
Link link = linkBlock.getLink();
if (link != null && link.getTitle() != null) {
for (String blockedLinkBlock : blockedLinkBlocks) {
if (blockedLinkBlock.equals(link.getTitle().getText())) {
skipUntilDivider = true;
break;
}
}
if (skipUntilDivider) {
index++;
continue;
}
}
}
}
// Skip LinkBlocks with a "flavor" claiming to be "isPartner" (sponsored inline ads).
if (currentBlock instanceof LinkBlock linkBlock
&& linkBlock.getLink() != null
&& linkBlock.getLink().getLinkFlavor() instanceof SmallArticleLinkFlavor smallArticleLinkFlavor
&& smallArticleLinkFlavor.isPartner() != null
&& smallArticleLinkFlavor.isPartner()) {
index++;
continue;
}
if (currentBlock instanceof DividerBlock) {
skipUntilDivider = false;
}
// Filter HeaderBlock with known ads until next HeaderBlock.
if (currentBlock instanceof HeaderBlock headerBlock) {
StyledText headerText = headerBlock.component20();
if (headerText != null) {
skipFullHeader = false;
for (String blockedHeaderBlock : blockedHeaderBlocks) {
if (blockedHeaderBlock.equals(headerText.getText())) {
skipFullHeader = true;
break;
}
}
if (skipFullHeader) {
index++;
continue;
}
}
}
if (!skipFullHeader && !skipUntilDivider) {
cleanedList.add(currentBlock);
}
index++;
}
// Replace list in-place to not deal with moving the result to the correct register in smali.
blocks.clear();
blocks.addAll(cleanedList);
} catch (Exception ex) {
Logger.printException(() -> "filterAds failure", ex);
}
}
}

View File

@@ -0,0 +1,17 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
}
android {
namespace = "app.revanced.extension"
compileSdk = 34
defaultConfig {
minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,5 @@
package nl.nu.performance.api.client.interfaces;
public class Block {
}

View File

@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;
import nl.nu.performance.api.client.interfaces.Block;
public class DividerBlock extends Block {
}

View File

@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;
import nl.nu.performance.api.client.interfaces.Block;
public class DpgBannerBlock extends Block {
}

View File

@@ -0,0 +1,10 @@
package nl.nu.performance.api.client.objects;
import nl.nu.performance.api.client.interfaces.Block;
public class HeaderBlock extends Block {
// returns title
public final StyledText component20() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,13 @@
package nl.nu.performance.api.client.objects;
import nl.nu.performance.api.client.unions.LinkFlavor;
public class Link {
public final StyledText getTitle() {
throw new UnsupportedOperationException("Stub");
}
public final LinkFlavor getLinkFlavor() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,10 @@
package nl.nu.performance.api.client.objects;
import android.os.Parcelable;
import nl.nu.performance.api.client.interfaces.Block;
public abstract class LinkBlock extends Block implements Parcelable {
public final Link getLink() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;
public class StyledText {
public final String getText() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,4 @@
package nl.nu.performance.api.client.unions;
public interface LinkFlavor {
}

View File

@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.unions;
public class SmallArticleLinkFlavor implements LinkFlavor {
public final Boolean isPartner() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -362,27 +362,18 @@ public class Utils {
} }
public static void setContext(Context appContext) { public static void setContext(Context appContext) {
// Must initially set context as the language settings needs it. // Must initially set context to check the app language.
context = appContext; context = appContext;
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get(); AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language != AppLanguage.DEFAULT) { if (language != AppLanguage.DEFAULT) {
// Create a new context with the desired language. // Create a new context with the desired language.
Logger.printDebug(() -> "Using app language: " + language);
Configuration config = appContext.getResources().getConfiguration(); Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale()); config.setLocale(language.getLocale());
context = appContext.createConfigurationContext(config); context = appContext.createConfigurationContext(config);
} }
// In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies.
// Calling the regular printDebug method here can cause a Settings context null pointer exception,
// even though the context is already set before the call.
//
// The initialization logger methods do not directly or indirectly
// reference the Context or any Settings and are unaffected by this problem.
//
// Info level also helps debug if a patch hook is called before
// the context is set since debug logging is off by default.
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
} }
public static void setClipboard(@NonNull String text) { public static void setClipboard(@NonNull String text) {

View File

@@ -158,16 +158,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
/** /**
* Syncs all UI Preferences to any {@link Setting} they represent. * Syncs all UI Preferences to any {@link Setting} they represent.
*/ */
private void updatePreferenceScreen(@NonNull PreferenceScreen screen, private void updatePreferenceScreen(@NonNull PreferenceGroup group,
boolean syncSettingValue, boolean syncSettingValue,
boolean applySettingToPreference) { boolean applySettingToPreference) {
// Alternatively this could iterate thru all Settings and check for any matching Preferences, // Alternatively this could iterate thru all Settings and check for any matching Preferences,
// but there are many more Settings than UI preferences so it's more efficient to only check // but there are many more Settings than UI preferences so it's more efficient to only check
// the Preferences. // the Preferences.
for (int i = 0, prefCount = screen.getPreferenceCount(); i < prefCount; i++) { for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
Preference pref = screen.getPreference(i); Preference pref = group.getPreference(i);
if (pref instanceof PreferenceScreen) { if (pref instanceof PreferenceGroup subGroup) {
updatePreferenceScreen((PreferenceScreen) pref, syncSettingValue, applySettingToPreference); updatePreferenceScreen(subGroup, syncSettingValue, applySettingToPreference);
} else if (pref.hasKey()) { } else if (pref.hasKey()) {
String key = pref.getKey(); String key = pref.getKey();
Setting<?> setting = Setting.getSettingFromPath(key); Setting<?> setting = Setting.getSettingFromPath(key);

View File

@@ -0,0 +1,54 @@
package app.revanced.extension.shared.settings.preference;
import android.annotation.SuppressLint;
import android.content.Context;
import android.preference.PreferenceCategory;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* Empty preference category with no title, used to organize and group related preferences together.
*/
@SuppressWarnings({"unused", "deprecation"})
public class NoTitlePreferenceCategory extends PreferenceCategory {
public NoTitlePreferenceCategory(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoTitlePreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public NoTitlePreferenceCategory(Context context) {
super(context);
}
@Override
@SuppressLint("MissingSuperCall")
protected View onCreateView(ViewGroup parent) {
// Return an zero-height view to eliminate empty title space.
return new View(getContext());
}
@Override
public CharSequence getTitle() {
// Title can be used for sorting. Return the first sub preference title.
if (getPreferenceCount() > 0) {
return getPreference(0).getTitle();
}
return super.getTitle();
}
@Override
public int getTitleRes() {
if (getPreferenceCount() > 0) {
return getPreference(0).getTitleRes();
}
return super.getTitleRes();
}
}

View File

@@ -107,6 +107,21 @@ public class SpoofVideoStreamsPatch {
return false; return false;
} }
/**
* Injection point.
* Turns off a feature flag that interferes with spoofing.
*/
public static boolean useMediaFetchHotConfigReplacement(boolean original) {
if (original) {
Logger.printDebug(() -> "useMediaFetchHotConfigReplacement is set on");
}
if (!SPOOF_STREAMING_DATA) {
return original;
}
return false;
}
/** /**
* Injection point. * Injection point.
*/ */

View File

@@ -1,7 +1,7 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class BackgroundPlaybackPatch { public class BackgroundPlaybackPatch {
@@ -23,16 +23,7 @@ public class BackgroundPlaybackPatch {
// 7. Close the Short // 7. Close the Short
// 8. Resume playing the regular video // 8. Resume playing the regular video
// 9. Minimize the app (PIP should appear) // 9. Minimize the app (PIP should appear)
if (!VideoInformation.lastVideoIdIsShort()) { return !ShortsPlayerState.isOpen();
return true; // Definitely is not a Short.
}
// TODO: Add better hook.
// Might be a Shorts, or might be a prior regular video on screen again after a Shorts was closed.
// This incorrectly prevents PIP if player is in WATCH_WHILE_MINIMIZED after closing a Shorts,
// 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();
} }
/** /**

View File

@@ -1,9 +1,17 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.shared.Utils; import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.NavigationBar;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ChangeFormFactorPatch { public class ChangeFormFactorPatch {
@@ -41,14 +49,57 @@ public class ChangeFormFactorPatch {
@Nullable @Nullable
private static final Integer FORM_FACTOR_TYPE = Settings.CHANGE_FORM_FACTOR.get().formFactorType; private static final Integer FORM_FACTOR_TYPE = Settings.CHANGE_FORM_FACTOR.get().formFactorType;
private static final boolean USING_AUTOMOTIVE_TYPE = Objects.requireNonNull(
FormFactor.AUTOMOTIVE.formFactorType).equals(FORM_FACTOR_TYPE);
/** /**
* Injection point. * Injection point.
*/ */
public static int getFormFactor(int original) { public static int getFormFactor(int original) {
return FORM_FACTOR_TYPE == null if (FORM_FACTOR_TYPE == null) return original;
? original
: FORM_FACTOR_TYPE; if (USING_AUTOMOTIVE_TYPE) {
// Do not change if the player is opening or is opened,
// otherwise the video description cannot be opened.
PlayerType current = PlayerType.getCurrent();
if (current.isMaximizedOrFullscreen() || current == PlayerType.WATCH_WHILE_SLIDING_MINIMIZED_MAXIMIZED) {
Logger.printDebug(() -> "Using original form factor for player");
return original;
}
if (!NavigationBar.isSearchBarActive()) {
// Automotive type shows error 400 when opening a channel page and using some explore tab.
// This is a bug in unpatched YouTube that occurs on actual Android Automotive devices.
// Work around the issue by using the original form factor if not in search and the
// navigation back button is present.
if (NavigationBar.isBackButtonVisible()) {
Logger.printDebug(() -> "Using original form factor, as back button is visible without search present");
return original;
}
// Do not change library tab otherwise watch history is hidden.
// Do this check last since the current navigation button is required.
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.LIBRARY) {
return original;
}
}
}
return FORM_FACTOR_TYPE;
} }
/**
* Injection point.
*/
public static void navigationTabCreated(NavigationButton button, View tabView) {
// On first startup of the app the navigation buttons are fetched and updated.
// If the user immediately opens the 'You' or opens a video, then the call to
// update the navigtation buttons will use the non automotive form factor
// and the explore tab is missing.
// Fixing this is not so simple because of the concurrent calls for the player and You tab.
// For now, always hide the explore tab.
if (USING_AUTOMOTIVE_TYPE && button == NavigationButton.EXPLORE) {
tabView.setVisibility(View.GONE);
}
}
} }

View File

@@ -1,7 +1,7 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DisableAutoCaptionsPatch { public class DisableAutoCaptionsPatch {
@@ -14,7 +14,7 @@ public class DisableAutoCaptionsPatch {
public static boolean autoCaptionsEnabled() { public static boolean autoCaptionsEnabled() {
return Settings.AUTO_CAPTIONS.get() return Settings.AUTO_CAPTIONS.get()
// Do not use auto captions for Shorts. // Do not use auto captions for Shorts.
&& !PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized(); && ShortsPlayerState.isOpen();
} }
} }

View File

@@ -1,24 +0,0 @@
package app.revanced.extension.youtube.patches;
import android.annotation.SuppressLint;
import android.widget.ImageView;
import app.revanced.extension.youtube.settings.Settings;
/** @noinspection unused*/
public final class DisableSuggestedVideoEndScreenPatch {
@SuppressLint("StaticFieldLeak")
private static ImageView lastView;
public static void closeEndScreen(final ImageView imageView) {
if (!Settings.DISABLE_SUGGESTED_VIDEO_END_SCREEN.get()) return;
// Prevent adding the listener multiple times.
if (lastView == imageView) return;
lastView = imageView;
imageView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
if (imageView.isShown()) imageView.callOnClick();
});
}
}

View File

@@ -24,7 +24,7 @@ public final class EnableDebuggingPatch {
/** /**
* Injection point. * Injection point.
*/ */
public static boolean isBooleanFeatureFlagEnabled(boolean value, long flag) { public static boolean isBooleanFeatureFlagEnabled(boolean value, Long flag) {
if (LOG_FEATURE_FLAGS && value) { if (LOG_FEATURE_FLAGS && value) {
if (featureFlags.putIfAbsent(flag, true) == null) { if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> "boolean feature is enabled: " + flag); Logger.printDebug(() -> "boolean feature is enabled: " + flag);

View File

@@ -0,0 +1,13 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class HideEndScreenSuggestedVideoPatch {
/**
* Injection point.
*/
public static boolean hideEndScreenSuggestedVideo() {
return Settings.HIDE_END_SCREEN_SUGGESTED_VIDEO.get();
}
}

View File

@@ -1,8 +1,11 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
import app.revanced.extension.youtube.shared.VideoState; import app.revanced.extension.youtube.shared.VideoState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -24,4 +27,26 @@ public class PlayerTypeHookPatch {
VideoState.setFromString(youTubeVideoState.name()); VideoState.setFromString(youTubeVideoState.name());
} }
/**
* Injection point.
*
* Add a listener to the shorts player overlay View.
* Triggered when a shorts player is attached or detached to Windows.
*
* @param view shorts player overlay (R.id.reel_watch_player).
*/
public static void onShortsCreate(View view) {
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(@Nullable View v) {
ShortsPlayerState.setOpen(true);
}
@Override
public void onViewDetachedFromWindow(@Nullable View v) {
ShortsPlayerState.setOpen(false);
}
});
}
} }

View File

@@ -74,6 +74,7 @@ public final class AdsFilter extends Filter {
"video_display_button_group_layout", "video_display_button_group_layout",
"landscape_image_wide_button_layout", "landscape_image_wide_button_layout",
"video_display_carousel_button_group_layout", "video_display_carousel_button_group_layout",
"video_display_full_buttoned_short_dr_layout",
"compact_landscape_image_layout", // Tablet layout search results. "compact_landscape_image_layout", // Tablet layout search results.
"text_image_no_button_layout" // Tablet layout search results. "text_image_no_button_layout" // Tablet layout search results.
); );

View File

@@ -2,20 +2,20 @@ package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.youtube.patches.playback.quality.RestoreOldVideoQualityMenuPatch; import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
/** /**
* Abuse LithoFilter for {@link RestoreOldVideoQualityMenuPatch}. * Abuse LithoFilter for {@link AdvancedVideoQualityMenuPatch}.
*/ */
public final class VideoQualityMenuFilterPatch extends Filter { public final class AdvancedVideoQualityMenuFilter extends Filter {
// Must be volatile or synchronized, as litho filtering runs off main thread // Must be volatile or synchronized, as litho filtering runs off main thread
// and this field is then access from the main thread. // and this field is then access from the main thread.
public static volatile boolean isVideoQualityMenuVisible; public static volatile boolean isVideoQualityMenuVisible;
public VideoQualityMenuFilterPatch() { public AdvancedVideoQualityMenuFilter() {
addPathCallbacks(new StringFilterGroup( addPathCallbacks(new StringFilterGroup(
Settings.RESTORE_OLD_VIDEO_QUALITY_MENU, Settings.ADVANCED_VIDEO_QUALITY_MENU,
"quick_quality_sheet_content.eml-js" "quick_quality_sheet_content.eml-js"
)); ));
} }

View File

@@ -98,6 +98,11 @@ public final class LayoutComponentsFilter extends Filter {
"compact_banner" "compact_banner"
); );
final var subscriptionsChipBar = new StringFilterGroup(
Settings.HIDE_FILTER_BAR_FEED_IN_FEED,
"subscriptions_chip_bar"
);
inFeedSurvey = new StringFilterGroup( inFeedSurvey = new StringFilterGroup(
Settings.HIDE_FEED_SURVEY, Settings.HIDE_FEED_SURVEY,
"in_feed_survey", "in_feed_survey",
@@ -264,6 +269,7 @@ public final class LayoutComponentsFilter extends Filter {
singleItemInformationPanel, singleItemInformationPanel,
emergencyBox, emergencyBox,
subscribersCommunityGuidelines, subscribersCommunityGuidelines,
subscriptionsChipBar,
channelGuidelines, channelGuidelines,
audioTrackButton, audioTrackButton,
artistCard, artistCard,
@@ -456,6 +462,12 @@ public final class LayoutComponentsFilter extends Filter {
return true; return true;
} }
// Do not hide if the navigation back button is visible,
// otherwise the content shelves in the YouTube Movie/Courses pages is hidden.
if (NavigationBar.isBackButtonVisible()) {
return false;
}
// Check navigation button last. // Check navigation button last.
// Only filter if the library tab is not selected. // Only filter if the library tab is not selected.
// This check is important as the shelf layout is used for the library tab playlists. // This check is important as the shelf layout is used for the library tab playlists.

View File

@@ -8,30 +8,30 @@ import android.widget.ListView;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.components.VideoQualityMenuFilterPatch; import app.revanced.extension.youtube.patches.components.AdvancedVideoQualityMenuFilter;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
/** /**
* This patch contains the logic to show the old video quality menu. * This patch contains the logic to always open the advanced video quality menu.
* Two methods are required, because the quality menu is a RecyclerView in the new YouTube version * Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
* and a ListView in the old one. * and a ListView in the old one.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class RestoreOldVideoQualityMenuPatch { public final class AdvancedVideoQualityMenuPatch {
/** /**
* Injection point. * Injection point.
*/ */
public static void onFlyoutMenuCreate(RecyclerView recyclerView) { public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return; if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
recyclerView.getViewTreeObserver().addOnDrawListener(() -> { recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
try { try {
// Check if the current view is the quality menu. // Check if the current view is the quality menu.
if (!VideoQualityMenuFilterPatch.isVideoQualityMenuVisible || recyclerView.getChildCount() == 0) { if (!AdvancedVideoQualityMenuFilter.isVideoQualityMenuVisible || recyclerView.getChildCount() == 0) {
return; return;
} }
VideoQualityMenuFilterPatch.isVideoQualityMenuVisible = false; AdvancedVideoQualityMenuFilter.isVideoQualityMenuVisible = false;
ViewParent quickQualityViewParent = Utils.getParentView(recyclerView, 3); ViewParent quickQualityViewParent = Utils.getParentView(recyclerView, 3);
if (!(quickQualityViewParent instanceof ViewGroup)) { if (!(quickQualityViewParent instanceof ViewGroup)) {
@@ -39,16 +39,15 @@ public final class RestoreOldVideoQualityMenuPatch {
} }
View firstChild = recyclerView.getChildAt(0); View firstChild = recyclerView.getChildAt(0);
if (!(firstChild instanceof ViewGroup)) { if (!(firstChild instanceof ViewGroup firstChildGroup)) {
return; return;
} }
ViewGroup advancedQualityParentView = (ViewGroup) firstChild; if (firstChildGroup.getChildCount() < 4) {
if (advancedQualityParentView.getChildCount() < 4) {
return; return;
} }
View advancedQualityView = advancedQualityParentView.getChildAt(3); View advancedQualityView = firstChildGroup.getChildAt(3);
if (advancedQualityView == null) { if (advancedQualityView == null) {
return; return;
} }
@@ -71,7 +70,7 @@ public final class RestoreOldVideoQualityMenuPatch {
* Used to force the creation of the advanced menu item for the Shorts quality flyout. * Used to force the creation of the advanced menu item for the Shorts quality flyout.
*/ */
public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) { public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) {
return Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get() || original; return Settings.ADVANCED_VIDEO_QUALITY_MENU.get() || original;
} }
/** /**
@@ -79,8 +78,8 @@ public final class RestoreOldVideoQualityMenuPatch {
* *
* Used if spoofing to an old app version, and also used for the Shorts video quality flyout. * Used if spoofing to an old app version, and also used for the Shorts video quality flyout.
*/ */
public static void showOldVideoQualityMenu(final ListView listView) { public static void showAdvancedVideoQualityMenu(ListView listView) {
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return; if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override @Override

View File

@@ -12,15 +12,19 @@ import java.util.List;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.IntegerSetting; import app.revanced.extension.shared.settings.IntegerSetting;
import app.revanced.extension.youtube.patches.VideoInformation; import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class RememberVideoQualityPatch { public class RememberVideoQualityPatch {
private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2; private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
private static final IntegerSetting wifiQualitySetting = Settings.VIDEO_QUALITY_DEFAULT_WIFI; private static final IntegerSetting videoQualityWifi = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting mobileQualitySetting = Settings.VIDEO_QUALITY_DEFAULT_MOBILE; private static final IntegerSetting videoQualityMobile = Settings.VIDEO_QUALITY_DEFAULT_MOBILE;
private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE;
private static boolean qualityNeedsUpdating; private static boolean qualityNeedsUpdating;
@@ -41,17 +45,29 @@ public class RememberVideoQualityPatch {
@Nullable @Nullable
private static List<Integer> videoQualities; private static List<Integer> videoQualities;
private static boolean shouldRememberVideoQuality() {
BooleanSetting preference = ShortsPlayerState.isOpen() ?
Settings.REMEMBER_SHORTS_QUALITY_LAST_SELECTED
: Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED;
return preference.get();
}
private static void changeDefaultQuality(int defaultQuality) { private static void changeDefaultQuality(int defaultQuality) {
String networkTypeMessage; String networkTypeMessage;
boolean useShortsPreference = ShortsPlayerState.isOpen();
if (Utils.getNetworkType() == NetworkType.MOBILE) { if (Utils.getNetworkType() == NetworkType.MOBILE) {
mobileQualitySetting.save(defaultQuality); if (useShortsPreference) shortsQualityMobile.save(defaultQuality);
else videoQualityMobile.save(defaultQuality);
networkTypeMessage = str("revanced_remember_video_quality_mobile"); networkTypeMessage = str("revanced_remember_video_quality_mobile");
} else { } else {
wifiQualitySetting.save(defaultQuality); if (useShortsPreference) shortsQualityWifi.save(defaultQuality);
else videoQualityWifi.save(defaultQuality);
networkTypeMessage = str("revanced_remember_video_quality_wifi"); networkTypeMessage = str("revanced_remember_video_quality_wifi");
} }
Utils.showToastShort( Utils.showToastShort(str(
str("revanced_remember_video_quality_toast", networkTypeMessage, (defaultQuality + "p"))); useShortsPreference ? "revanced_remember_video_quality_toast_shorts" : "revanced_remember_video_quality_toast",
networkTypeMessage, (defaultQuality + "p")
));
} }
/** /**
@@ -62,9 +78,10 @@ public class RememberVideoQualityPatch {
*/ */
public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) { public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) {
try { try {
boolean useShortsPreference = ShortsPlayerState.isOpen();
final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE
? mobileQualitySetting.get() ? (useShortsPreference ? shortsQualityMobile : videoQualityMobile).get()
: wifiQualitySetting.get(); : (useShortsPreference ? shortsQualityWifi : videoQualityWifi).get();
if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) { if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
return originalQualityIndex; // Nothing to do. return originalQualityIndex; // Nothing to do.
@@ -141,17 +158,17 @@ public class RememberVideoQualityPatch {
* Injection point. Old quality menu. * Injection point. Old quality menu.
*/ */
public static void userChangedQuality(int selectedQualityIndex) { public static void userChangedQuality(int selectedQualityIndex) {
if (!Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED.get()) return; if (shouldRememberVideoQuality()) {
userSelectedQualityIndex = selectedQualityIndex;
userSelectedQualityIndex = selectedQualityIndex; userChangedDefaultQuality = true;
userChangedDefaultQuality = true; }
} }
/** /**
* Injection point. New quality menu. * Injection point. New quality menu.
*/ */
public static void userChangedQualityInNewFlyout(int selectedQuality) { public static void userChangedQualityInNewFlyout(int selectedQuality) {
if (!Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED.get()) return; if (!shouldRememberVideoQuality()) return;
changeDefaultQuality(selectedQuality); // Quality is human readable resolution (ie: 1080). changeDefaultQuality(selectedQuality); // Quality is human readable resolution (ie: 1080).
} }

View File

@@ -47,11 +47,14 @@ import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
public class Settings extends BaseSettings { public class Settings extends BaseSettings {
// Video // Video
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
public static final BooleanSetting RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2); public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2); public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
public static final IntegerSetting SHORTS_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_shorts_quality_default_wifi", -2, true);
public static final IntegerSetting SHORTS_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_shorts_quality_default_mobile", -2, true);
public static final BooleanSetting REMEMBER_SHORTS_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_shorts_quality_last_selected", FALSE);
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
// Speed // Speed
public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true); public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true);
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE); public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
@@ -125,7 +128,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE); public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true); public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE); public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED); public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true); public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE); public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
@@ -135,6 +137,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE); public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE);
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE); public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE); public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
public static final BooleanSetting HIDE_END_SCREEN_SUGGESTED_VIDEO = new BooleanSetting("revanced_end_screen_suggested_video", FALSE, true);
public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE); public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE);
public static final BooleanSetting HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE); public static final BooleanSetting HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE); public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
@@ -171,10 +174,10 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_summary", FALSE); public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_summary", FALSE);
public static final BooleanSetting HIDE_COMMENTS_BY_MEMBERS_HEADER = new BooleanSetting("revanced_hide_comments_by_members_header", FALSE); public static final BooleanSetting HIDE_COMMENTS_BY_MEMBERS_HEADER = new BooleanSetting("revanced_hide_comments_by_members_header", FALSE);
public static final BooleanSetting HIDE_COMMENTS_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE); public static final BooleanSetting HIDE_COMMENTS_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE); public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE); public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE);
public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE); public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
// Description // Description
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE); public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE);
public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE); public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE);
@@ -284,7 +287,6 @@ public class Settings extends BaseSettings {
"revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability()); "revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability());
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true); public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true); public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
public static final StringSetting SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_primary", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR)); public static final StringSetting SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_primary", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
public static final StringSetting SEEKBAR_CUSTOM_COLOR_ACCENT = new StringSetting("revanced_seekbar_custom_color_accent", "#FF2791", true, parent(SEEKBAR_CUSTOM_COLOR)); public static final StringSetting SEEKBAR_CUSTOM_COLOR_ACCENT = new StringSetting("revanced_seekbar_custom_color_accent", "#FF2791", true, parent(SEEKBAR_CUSTOM_COLOR));
@@ -322,7 +324,6 @@ public class Settings extends BaseSettings {
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true, public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true, public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS)); public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
@@ -384,9 +385,13 @@ public class Settings extends BaseSettings {
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF"); public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
// Deprecated migrations // Deprecated migrations
public static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024 private static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true); private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true);
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", FALSE); private static final BooleanSetting DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", FALSE);
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
static { static {
// region Migration // region Migration
@@ -405,6 +410,10 @@ public class Settings extends BaseSettings {
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER); migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
migrateOldSettingToNew(DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN, HIDE_END_SCREEN_SUGGESTED_VIDEO);
migrateOldSettingToNew(DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU, ADVANCED_VIDEO_QUALITY_MENU);
// Migrate renamed enum. // Migrate renamed enum.
//noinspection deprecation //noinspection deprecation
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) { if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {

View File

@@ -3,7 +3,9 @@ package app.revanced.extension.youtube.shared;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
import android.app.Activity; import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.view.View; import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -24,12 +26,22 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class NavigationBar { public final class NavigationBar {
/**
* Interface to call obfuscated methods in AppCompat Toolbar class.
*/
public interface AppCompatToolbarPatchInterface {
Drawable patch_getNavigationIcon();
}
// //
// Search bar // Search and toolbar.
// //
private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null); private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null);
private static volatile WeakReference<AppCompatToolbarPatchInterface> toolbarResultsRef
= new WeakReference<>(null);
/** /**
* Injection point. * Injection point.
*/ */
@@ -37,6 +49,22 @@ public final class NavigationBar {
searchBarResultsRef = new WeakReference<>(searchbarResults); searchBarResultsRef = new WeakReference<>(searchbarResults);
} }
/**
* Injection point.
*/
public static void setToolbar(FrameLayout layout) {
AppCompatToolbarPatchInterface toolbar = Utils.getChildView(layout, false, (view) ->
view instanceof AppCompatToolbarPatchInterface
);
if (toolbar == null) {
Logger.printException(() -> "Could not find navigation toolbar");
return;
}
toolbarResultsRef = new WeakReference<>(toolbar);
}
/** /**
* @return If the search bar is on screen. This includes if the player * @return If the search bar is on screen. This includes if the player
* is on screen and the search results are behind the player (and not visible). * is on screen and the search results are behind the player (and not visible).
@@ -47,8 +75,13 @@ public final class NavigationBar {
return searchbarResults != null && searchbarResults.getParent() != null; return searchbarResults != null && searchbarResults.getParent() != null;
} }
public static boolean isBackButtonVisible() {
AppCompatToolbarPatchInterface toolbar = toolbarResultsRef.get();
return toolbar != null && toolbar.patch_getNavigationIcon() != null;
}
// //
// Navigation bar buttons // Navigation bar buttons.
// //
/** /**

View File

@@ -5,7 +5,7 @@ import app.revanced.extension.youtube.Event
import app.revanced.extension.youtube.patches.VideoInformation import app.revanced.extension.youtube.patches.VideoInformation
/** /**
* Main player type. * Regular player type.
*/ */
enum class PlayerType { enum class PlayerType {
/** /**
@@ -90,8 +90,6 @@ enum class PlayerType {
* Does not include the first moment after a short is opened when a regular video is minimized on screen, * Does not include the first moment after a short is opened when a regular video is minimized on screen,
* or while watching a short with a regular video present on a spoofed 16.x version of YouTube. * or while watching a short with a regular video present on a spoofed 16.x version of YouTube.
* To include those situations instead use [isNoneHiddenOrMinimized]. * To include those situations instead use [isNoneHiddenOrMinimized].
*
* @see VideoInformation
*/ */
fun isNoneOrHidden(): Boolean { fun isNoneOrHidden(): Boolean {
return this == NONE || this == HIDDEN return this == NONE || this == HIDDEN
@@ -107,8 +105,11 @@ enum class PlayerType {
* when spoofing to an old version this will return false even * when spoofing to an old version this will return false even
* though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]). * though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]).
* *
* Instead of this method, consider using {@link ShortsPlayerState}
* which may work better for some situations.
*
* @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state. * @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state.
* @see VideoInformation * @see ShortsPlayerState
*/ */
fun isNoneHiddenOrSlidingMinimized(): Boolean { fun isNoneHiddenOrSlidingMinimized(): Boolean {
return isNoneOrHidden() || this == WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED return isNoneOrHidden() || this == WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED
@@ -125,9 +126,12 @@ enum class PlayerType {
* Typically used to detect if a Short is playing when the player cannot be in a minimized state, * Typically used to detect if a Short is playing when the player cannot be in a minimized state,
* such as the user interacting with a button or element of the player. * such as the user interacting with a button or element of the player.
* *
* Instead of this method, consider using {@link ShortsPlayerState}
* which may work better for some situations.
*
* @return If nothing, a Short, a regular video is sliding off screen to a dismissed or hidden state, * @return If nothing, a Short, a regular video is sliding off screen to a dismissed or hidden state,
* a regular video is minimized (and a new video is not being opened). * a regular video is minimized (and a new video is not being opened).
* @see VideoInformation * @see ShortsPlayerState
*/ */
fun isNoneHiddenOrMinimized(): Boolean { fun isNoneHiddenOrMinimized(): Boolean {
return isNoneHiddenOrSlidingMinimized() || this == WATCH_WHILE_MINIMIZED return isNoneHiddenOrSlidingMinimized() || this == WATCH_WHILE_MINIMIZED

View File

@@ -0,0 +1,38 @@
package app.revanced.extension.youtube.shared
import app.revanced.extension.shared.Logger
import app.revanced.extension.youtube.Event
/**
* Shorts player state.
*/
class ShortsPlayerState {
companion object {
@JvmStatic
fun setOpen(open: Boolean) {
if (isOpen != open) {
Logger.printDebug { "ShortsPlayerState open changed to: $isOpen" }
isOpen = open
onChange(open)
}
}
@Volatile
private var isOpen = false
/**
* Shorts player state change listener.
*/
@JvmStatic
val onChange = Event<Boolean>()
/**
* If the Shorts player is currently open.
*/
@JvmStatic
fun isOpen(): Boolean {
return isOpen
}
}
}

View File

@@ -18,7 +18,7 @@ import kotlin.math.min
import kotlin.math.round import kotlin.math.round
/** /**
* Main overlay layout for displaying volume and brightness level with both circular and rectangular progress bars. * Main overlay layout for displaying volume and brightness level with both circular and horizontal progress bars.
*/ */
class SwipeControlsOverlayLayout( class SwipeControlsOverlayLayout(
context: Context, context: Context,
@@ -69,7 +69,7 @@ class SwipeControlsOverlayLayout(
} }
addView(circularProgressView) addView(circularProgressView)
// Initialize rectangular progress bar // Initialize horizontal progress bar
val screenWidth = resources.displayMetrics.widthPixels val screenWidth = resources.displayMetrics.widthPixels
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
horizontalProgressView = HorizontalProgressView( horizontalProgressView = HorizontalProgressView(
@@ -152,10 +152,7 @@ class SwipeControlsOverlayLayout(
} }
/** /**
* Abstract base class for progress views to reduce code duplication. * Abstract base class for progress views.
*/
/**
* Abstract base class for progress views to reduce code duplication.
*/ */
abstract class AbstractProgressView( abstract class AbstractProgressView(
context: Context, context: Context,
@@ -183,10 +180,9 @@ abstract class AbstractProgressView(
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor color = overlayTextColor
textAlign = Paint.Align.CENTER textAlign = Paint.Align.CENTER
textSize = 30f // Can adjust based on need textSize = 40f // Can adjust based on need
} }
protected var progress = 0 protected var progress = 0
protected var maxProgress = 100 protected var maxProgress = 100
protected var displayText: String = "0" protected var displayText: String = "0"
@@ -211,7 +207,7 @@ abstract class AbstractProgressView(
} }
/** /**
* Custom view for rendering a circular progress indicator with text and icon. * Custom view for rendering a circular progress indicator with icons and text.
*/ */
class CircularProgressView( class CircularProgressView(
context: Context, context: Context,
@@ -235,7 +231,7 @@ class CircularProgressView(
private val rectF = RectF() private val rectF = RectF()
init { init {
textPaint.textSize = 40f // Override default text size for horizontal view textPaint.textSize = 40f // Override default text size for circular view
progressPaint.strokeWidth = 20f progressPaint.strokeWidth = 20f
fillBackgroundPaint.strokeWidth = 20f fillBackgroundPaint.strokeWidth = 20f
progressPaint.strokeCap = Paint.Cap.ROUND progressPaint.strokeCap = Paint.Cap.ROUND
@@ -266,7 +262,7 @@ class CircularProgressView(
it.draw(canvas) it.draw(canvas)
} }
// If not in icon-only mode, draw the text inside the ring. // If not a minimal style mode, draw the text inside the ring.
if (!overlayShowOverlayMinimalStyle) { if (!overlayShowOverlayMinimalStyle) {
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint) canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
} }
@@ -300,7 +296,7 @@ class HorizontalProgressView(
private val padding = 40f private val padding = 40f
init { init {
textPaint.textSize = 30f // Override default text size for horizontal view textPaint.textSize = 36f // Override default text size for horizontal view
progressPaint.strokeWidth = 0f progressPaint.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL progressPaint.style = Paint.Style.FILL

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true org.gradle.parallel = true
android.useAndroidX = true android.useAndroidX = true
kotlin.code.style = official kotlin.code.style = official
version = 5.13.0-dev.13 version = 5.14.0-dev.6

View File

@@ -348,6 +348,14 @@ public final class app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatchKt {
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/nunl/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/nunl/firebase/SpoofCertificatePatchKt {
public static final fun getSpoofCertificatePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/nyx/misc/pro/UnlockProPatchKt { public final class app/revanced/patches/nyx/misc/pro/UnlockProPatchKt {
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -768,8 +776,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/TextPref
} }
public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt { public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt {
public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt { public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
@@ -1116,6 +1124,10 @@ public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideE
public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/youtube/layout/hide/endscreensuggestion/HideEndScreenSuggestedVideoPatchKt {
public static final fun getHideEndScreenSuggestedVideoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatchKt { public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatchKt {
public static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -1451,6 +1463,10 @@ public final class app/revanced/patches/youtube/video/quality/RememberVideoQuali
public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/youtube/video/quality/VideoQualityPatchKt {
public static final fun getVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt { public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt {
public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }

View File

@@ -5,10 +5,10 @@ import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
internal const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch" "Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch"
internal const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused") @Suppress("unused")
val spoofWifiPatch = bytecodePatch( val spoofWifiPatch = bytecodePatch(

View File

@@ -8,9 +8,8 @@ import org.w3c.dom.Element
@Suppress("unused") @Suppress("unused")
val changeVersionCodePatch = resourcePatch( val changeVersionCodePatch = resourcePatch(
name = "Change version code", name = "Change version code",
description = "Changes the version code of the app. By default the highest version code is set. " + description = "Changes the version code of the app. This will turn off app store updates " +
"This allows older versions of an app to be installed " + "and allows downgrading an existing app install to an older app version.",
"if their version code is set to the same or a higher value and can stop app stores to update the app.",
use = false, use = false,
) { ) {
val versionCode by intOption( val versionCode by intOption(
@@ -21,7 +20,8 @@ val changeVersionCodePatch = resourcePatch(
"Highest" to Int.MAX_VALUE, "Highest" to Int.MAX_VALUE,
), ),
title = "Version code", title = "Version code",
description = "The version code to use", description = "The version code to use. Using the highest value turns off app store " +
"updates and allows downgrading an existing app install to an older app version.",
required = true, required = true,
) { versionCode -> versionCode!! >= 1 } ) { versionCode -> versionCode!! >= 1 }

View File

@@ -17,7 +17,7 @@ import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
internal const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;" "Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
// TODO: Replace this patch with spoofVideoStreamsPatch once possible. // TODO: Replace this patch with spoofVideoStreamsPatch once possible.

View File

@@ -0,0 +1,44 @@
package app.revanced.patches.nunl.ads
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val jwUtilCreateAdvertisementFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
custom { methodDef, classDef ->
classDef.type == "Lnl/sanomamedia/android/nu/video/util/JWUtil;" && methodDef.name == "createAdvertising"
}
}
internal val screenMapperFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Lnl/nu/android/bff/domain/models/screen/ScreenEntity;")
parameters("Lnl/nu/performance/api/client/objects/Screen;")
opcodes(
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_EQZ,
Opcode.CHECK_CAST
)
custom { methodDef, classDef ->
classDef.type == "Lnl/nu/android/bff/data/mappers/ScreenMapper;" && methodDef.name == "map"
}
}
internal val nextPageRepositoryImplFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("Lnl/nu/android/bff/domain/models/Page;")
parameters("Lnl/nu/performance/api/client/PacResponse;", "Ljava/lang/String;")
opcodes(
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_EQZ,
Opcode.CHECK_CAST
)
custom { methodDef, classDef ->
classDef.type == "Lnl/nu/android/bff/data/repositories/NextPageRepositoryImpl;" && methodDef.name == "mapToPage"
}
}

View File

@@ -0,0 +1,51 @@
package app.revanced.patches.nunl.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val hideAdsPatch = bytecodePatch(
name = "Hide ads",
description = "Hide ads and sponsored articles in list pages and remove pre-roll ads on videos.",
) {
compatibleWith("nl.sanomamedia.android.nu"("11.0.0", "11.0.1", "11.1.0"))
dependsOn(sharedExtensionPatch("nunl", mainActivityOnCreateHook))
execute {
// Disable video pre-roll ads.
// Whenever the app tries to create an ad via JWUtils.createAdvertising, don't actually tell the underlying JWPlayer library to do so => JWPlayer will not display ads.
jwUtilCreateAdvertisementFingerprint.method.addInstructions(
0,
"""
new-instance v0, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;
invoke-direct { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;-><init>()V
invoke-virtual { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;->build()Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig;
move-result-object v0
return-object v0
""",
)
// Filter injected content from API calls out of lists.
arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach {
// Index of instruction moving result of BlockPage;->getBlocks(...).
val moveGetBlocksResultObjectIndex = it.patternMatch!!.startIndex
it.method.apply {
val moveInstruction = getInstruction<OneRegisterInstruction>(moveGetBlocksResultObjectIndex)
val listRegister = moveInstruction.registerA
// Add instruction after moving List<Block> to register and then filter this List<Block> in place.
addInstructions(
moveGetBlocksResultObjectIndex + 1,
"""
invoke-static { v$listRegister }, Lapp/revanced/extension/nunl/ads/HideAdsPatch;->filterAds(Ljava/util/List;)V
""",
)
}
}
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.nunl.ads
import app.revanced.patches.shared.misc.extension.extensionHook
internal val mainActivityOnCreateHook = extensionHook {
custom { method, classDef ->
classDef.type == "Lnl/sanomamedia/android/nu/main/NUMainActivity;" && method.name == "onCreate"
}
}

View File

@@ -0,0 +1,20 @@
package app.revanced.patches.nunl.firebase
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val getFingerprintHashForPackageFingerprints = arrayOf(
"Lcom/google/firebase/installations/remote/FirebaseInstallationServiceClient;",
"Lcom/google/firebase/remoteconfig/internal/ConfigFetchHttpClient;",
"Lcom/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient;"
).map { className ->
fingerprint {
accessFlags(AccessFlags.PRIVATE)
parameters()
returns("Ljava/lang/String;")
custom { methodDef, classDef ->
classDef.type == className && methodDef.name == "getFingerprintHashForPackage"
}
}
}

View File

@@ -0,0 +1,24 @@
package app.revanced.patches.nunl.firebase
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
val spoofCertificatePatch = bytecodePatch(
name = "Spoof certificate",
description = "Spoofs the X-Android-Cert header to allow push messages.",
) {
compatibleWith("nl.sanomamedia.android.nu")
execute {
getFingerprintHashForPackageFingerprints.forEach { fingerprint ->
fingerprint.method.addInstructions(
0,
"""
const-string v0, "eae41fc018df2731a9b6ae1ac327da44a288667b"
return-object v0
""",
)
}
}
}

View File

@@ -1,7 +1,10 @@
package app.revanced.patches.reddit.customclients.boostforreddit.api package app.revanced.patches.reddit.customclients.boostforreddit.api
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.reddit.customclients.spoofClientPatch import app.revanced.patches.reddit.customclients.spoofClientPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption -> val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption ->
compatibleWith("com.rubenmayayo.reddit") compatibleWith("com.rubenmayayo.reddit")
@@ -23,14 +26,15 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com")
// region Patch user agent. // region Patch user agent.
// Use a random number as the platform in the user agent string. // Use a random user agent.
val platformName = (0..100000).random() val randomName = (0..100000).random()
val platformParameter = 0 val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
buildUserAgentFingerprint.let {
buildUserAgentFingerprint.method.addInstructions( val userAgentTemplateIndex = it.stringMatches!!.first().index
0, val register = it.method.getInstruction<OneRegisterInstruction>(userAgentTemplateIndex).registerA
"const-string p$platformParameter, \"$platformName\"",
) it.method.replaceInstruction(userAgentTemplateIndex, "const-string v$register, \"$userAgent\"")
}
// endregion // endregion
} }

View File

@@ -8,7 +8,11 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
val spoofClientPatch = spoofClientPatch(redirectUri = "infinity://localhost") { clientIdOption -> val spoofClientPatch = spoofClientPatch(redirectUri = "infinity://localhost") { clientIdOption ->
compatibleWith("ml.docilealligator.infinityforreddit") compatibleWith(
"ml.docilealligator.infinityforreddit",
"ml.docilealligator.infinityforreddit.plus",
"ml.docilealligator.infinityforreddit.patreon"
)
val clientId by clientIdOption val clientId by clientIdOption

View File

@@ -11,7 +11,11 @@ val unlockSubscriptionPatch = bytecodePatch(
) { ) {
dependsOn(spoofClientPatch) dependsOn(spoofClientPatch)
compatibleWith("ml.docilealligator.infinityforreddit") compatibleWith(
"ml.docilealligator.infinityforreddit",
"ml.docilealligator.infinityforreddit.plus",
"ml.docilealligator.infinityforreddit.patreon"
)
execute { execute {
setOf( setOf(

View File

@@ -17,7 +17,7 @@ import org.w3c.dom.Element
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
abstract class BasePreference( abstract class BasePreference(
val key: String? = null, val key: String? = null,
val titleKey: String = "${key}_title", val titleKey: String? = "${key}_title",
val summaryKey: String? = "${key}_summary", val summaryKey: String? = "${key}_summary",
val icon: String? = null, val icon: String? = null,
val layout: String? = null, val layout: String? = null,
@@ -35,7 +35,7 @@ abstract class BasePreference(
open fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit): Element = open fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit): Element =
ownerDocument.createElement(tag).apply { ownerDocument.createElement(tag).apply {
key?.let { setAttribute("android:key", it) } key?.let { setAttribute("android:key", it) }
setAttribute("android:title", "@string/${titleKey}") titleKey?.let { setAttribute("android:title", "@string/${titleKey}") }
summaryKey?.let { addSummary(it) } summaryKey?.let { addSummary(it) }
icon?.let { icon?.let {
setAttribute("android:icon", it) setAttribute("android:icon", it)

View File

@@ -17,7 +17,7 @@ import org.w3c.dom.Document
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
open class PreferenceCategory( open class PreferenceCategory(
key: String? = null, key: String? = null,
titleKey: String = "${key}_title", titleKey: String? = "${key}_title",
icon: String? = null, icon: String? = null,
layout: String? = null, layout: String? = null,
sorting: Sorting = Sorting.BY_TITLE, sorting: Sorting = Sorting.BY_TITLE,

View File

@@ -137,3 +137,15 @@ internal val patchIncludedExtensionMethodFingerprint = fingerprint {
classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded" classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded"
} }
} }
// Feature flag that turns on Platypus programming language code compiled to native C++.
// This code appears to replace the player config after the streams are loaded.
// Flag is present in YouTube 19.34, but is missing Platypus stream replacement code until 19.43.
// Flag and Platypus code is also present in newer versions of YouTube Music.
internal const val MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG = 45645570L
internal val mediaFetchHotConfigFingerprint = fingerprint {
literal {
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG
}
}

View File

@@ -31,10 +31,11 @@ internal const val EXTENSION_CLASS_DESCRIPTOR =
fun spoofVideoStreamsPatch( fun spoofVideoStreamsPatch(
block: BytecodePatchBuilder.() -> Unit = {}, block: BytecodePatchBuilder.() -> Unit = {},
applyMediaFetchHotConfigChanges: BytecodePatchBuilder.() -> Boolean = { false },
executeBlock: BytecodePatchContext.() -> Unit = {}, executeBlock: BytecodePatchContext.() -> Unit = {},
) = bytecodePatch( ) = bytecodePatch(
name = "Spoof video streams", name = "Spoof video streams",
description = "Spoofs the client video streams to fix playback.", description = "Adds options to spoof the client video streams to fix playback.",
) { ) {
block() block()
@@ -238,6 +239,17 @@ fun spoofVideoStreamsPatch(
// endregion // endregion
// region turn off stream config replacement feature flag.
if (applyMediaFetchHotConfigChanges()) {
mediaFetchHotConfigFingerprint.method.insertFeatureFlagBooleanOverride(
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
)
}
// endregion
executeBlock() executeBlock()
} }
} }

View File

@@ -11,7 +11,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tiktok/settings/AdPersonalizationActivityHook;" "Lapp/revanced/extension/tiktok/settings/AdPersonalizationActivityHook;"
val settingsPatch = bytecodePatch( val settingsPatch = bytecodePatch(

View File

@@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tudortmund/lockscreen/ShowOnLockscreenPatch;" "Lapp/revanced/extension/tudortmund/lockscreen/ShowOnLockscreenPatch;"
@Suppress("unused") @Suppress("unused")

View File

@@ -11,7 +11,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
internal const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/HideGetPremiumPatch;" "Lapp/revanced/extension/youtube/patches/HideGetPremiumPatch;"
val hideGetPremiumPatch = bytecodePatch( val hideGetPremiumPatch = bytecodePatch(

View File

@@ -50,7 +50,7 @@ private val downloadsResourcePatch = resourcePatch {
} }
} }
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DownloadsPatch;" private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DownloadsPatch;"
internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;" internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;"

View File

@@ -15,8 +15,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
val enableSeekbarTappingPatch = bytecodePatch( val enableSeekbarTappingPatch = bytecodePatch(
name = "Seekbar tapping", name = "Enable tap to seek",
description = "Adds an option to enable tap-to-seek on the seekbar of the video player.", description = "Adds an option to enable tap to seek on the seekbar of the video player.",
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,

View File

@@ -23,7 +23,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;" "Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;"
val navigationButtonsPatch = bytecodePatch( val navigationButtonsPatch = bytecodePatch(

View File

@@ -43,7 +43,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
val hidePlayerOverlayButtonsPatch = bytecodePatch( val hidePlayerOverlayButtonsPatch = bytecodePatch(
name = "Hide player overlay buttons", name = "Hide player overlay buttons",
description = "Adds options to hide the player cast, autoplay, caption button and next/ previous buttons.", description = "Adds options to hide the player Cast, Autoplay, Captions, and Previous & Next buttons.",
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,

View File

@@ -6,7 +6,9 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.youtube.layout.buttons.navigation.navigationButtonsPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference import app.revanced.util.getReference
@@ -15,7 +17,7 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;" private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;"
@Suppress("unused") @Suppress("unused")
val changeFormFactorPatch = bytecodePatch( val changeFormFactorPatch = bytecodePatch(
@@ -26,6 +28,7 @@ val changeFormFactorPatch = bytecodePatch(
sharedExtensionPatch, sharedExtensionPatch,
settingsPatch, settingsPatch,
addResourcesPatch, addResourcesPatch,
navigationButtonsPatch
) )
compatibleWith( compatibleWith(
@@ -50,6 +53,8 @@ val changeFormFactorPatch = bytecodePatch(
) )
) )
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
createPlayerRequestBodyWithModelFingerprint.method.apply { createPlayerRequestBodyWithModelFingerprint.method.apply {
val formFactorEnumClass = formFactorEnumConstructorFingerprint.originalClassDef.type val formFactorEnumClass = formFactorEnumConstructorFingerprint.originalClassDef.type

View File

@@ -44,9 +44,12 @@ private val hideEndscreenCardsResourcePatch = resourcePatch {
} }
} }
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/HideEndscreenCardsPatch;"
@Suppress("unused") @Suppress("unused")
val hideEndscreenCardsPatch = bytecodePatch( val hideEndscreenCardsPatch = bytecodePatch(
name = "Hide endscreen cards", name = "Hide end screen cards",
description = "Adds an option to hide suggested video cards at the end of videos.", description = "Adds an option to hide suggested video cards at the end of videos.",
) { ) {
dependsOn( dependsOn(
@@ -78,9 +81,7 @@ val hideEndscreenCardsPatch = bytecodePatch(
addInstruction( addInstruction(
insertIndex, insertIndex,
"invoke-static { v$viewRegister }, " + "invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideEndscreen(Landroid/view/View;)V",
"Lapp/revanced/extension/youtube/patches/HideEndscreenCardsPatch;->" +
"hideEndscreen(Landroid/view/View;)V",
) )
} }
} }

View File

@@ -0,0 +1,38 @@
package app.revanced.patches.youtube.layout.hide.endscreensuggestion
import app.revanced.patcher.fingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val autoNavConstructorFingerprint = fingerprint {
returns("V")
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
strings("main_app_autonav")
}
internal val autoNavStatusFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
parameters()
}
internal val removeOnLayoutChangeListenerFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters()
opcodes(
Opcode.IPUT,
Opcode.INVOKE_VIRTUAL
)
// This is the only reference present in the entire smali.
custom { method, _ ->
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.name == "removeOnLayoutChangeListener" &&
reference.definingClass.endsWith("/YouTubePlayerOverlaysLayout;")
} >= 0
}
}

View File

@@ -0,0 +1,92 @@
package app.revanced.patches.youtube.layout.hide.endscreensuggestion
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/HideEndScreenSuggestedVideoPatch;"
@Suppress("unused")
val hideEndScreenSuggestedVideoPatch = bytecodePatch(
name = "Hide end screen suggested video",
description = "Adds an option to hide the suggested video at the end of videos.",
) {
dependsOn(
sharedExtensionPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
addResources("youtube", "layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch")
PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_end_screen_suggested_video"),
)
removeOnLayoutChangeListenerFingerprint.let {
val endScreenMethod = navigate(it.originalMethod).to(it.patternMatch!!.endIndex).stop()
endScreenMethod.apply {
val autoNavStatusMethodName = autoNavStatusFingerprint.match(
autoNavConstructorFingerprint.classDef
).originalMethod.name
val invokeIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.name == autoNavStatusMethodName &&
reference.returnType == "Z" &&
reference.parameterTypes.isEmpty()
}
val iGetObjectIndex = indexOfFirstInstructionReversedOrThrow(invokeIndex, Opcode.IGET_OBJECT)
val invokeReference = getInstruction<ReferenceInstruction>(invokeIndex).reference
val iGetObjectReference = getInstruction<ReferenceInstruction>(iGetObjectIndex).reference
val opcodeName = getInstruction(invokeIndex).opcode.name
addInstructionsWithLabels(
0,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideEndScreenSuggestedVideo()Z
move-result v0
if-eqz v0, :show_end_screen_recommendation
iget-object v0, p0, $iGetObjectReference
# This reference checks whether autoplay is turned on.
$opcodeName { v0 }, $invokeReference
move-result v0
# Hide suggested video end screen only when autoplay is turned off.
if-nez v0, :show_end_screen_recommendation
return-void
""",
ExternalLabel("show_end_screen_recommendation", getInstruction(0))
)
}
}
}
}

View File

@@ -14,7 +14,7 @@ import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableFullscreenAmbientModePatch;" "Lapp/revanced/extension/youtube/patches/DisableFullscreenAmbientModePatch;"
val disableFullscreenAmbientModePatch = bytecodePatch( val disableFullscreenAmbientModePatch = bytecodePatch(

View File

@@ -158,9 +158,9 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_comments_by_members_header"), SwitchPreference("revanced_hide_comments_by_members_header"),
SwitchPreference("revanced_hide_comments_section"), SwitchPreference("revanced_hide_comments_section"),
SwitchPreference("revanced_hide_comments_create_a_short_button"), SwitchPreference("revanced_hide_comments_create_a_short_button"),
SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons"),
SwitchPreference("revanced_hide_comments_preview_comment"), SwitchPreference("revanced_hide_comments_preview_comment"),
SwitchPreference("revanced_hide_comments_thanks_button"), SwitchPreference("revanced_hide_comments_thanks_button"),
SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons"),
), ),
sorting = PreferenceScreenPreference.Sorting.UNSORTED, sorting = PreferenceScreenPreference.Sorting.UNSORTED,
), ),

View File

@@ -159,7 +159,7 @@ private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/pat
@Suppress("unused") @Suppress("unused")
val hideShortsComponentsPatch = bytecodePatch( val hideShortsComponentsPatch = bytecodePatch(
name = "Hide Shorts components", name = "Hide Shorts components",
description = "Adds options to hide components related to YouTube Shorts.", description = "Adds options to hide components related to Shorts.",
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,

View File

@@ -1,79 +1,9 @@
package app.revanced.patches.youtube.layout.hide.suggestedvideoendscreen package app.revanced.patches.youtube.layout.hide.suggestedvideoendscreen
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.youtube.layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
internal var sizeAdjustableLiteAutoNavOverlay = -1L @Deprecated("Use 'Hide suggested video end screen' instead.")
private set val disableSuggestedVideoEndScreenPatch = bytecodePatch {
dependsOn(hideEndScreenSuggestedVideoPatch)
internal val disableSuggestedVideoEndScreenResourcePatch = resourcePatch { }
dependsOn(
settingsPatch,
resourceMappingPatch,
addResourcesPatch,
)
execute {
addResources("youtube", "layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch")
PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_disable_suggested_video_end_screen"),
)
sizeAdjustableLiteAutoNavOverlay = resourceMappings[
"layout",
"size_adjustable_lite_autonav_overlay",
]
}
}
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableSuggestedVideoEndScreenPatch;"
@Suppress("unused")
val disableSuggestedVideoEndScreenPatch = bytecodePatch(
name = "Disable suggested video end screen",
description = "Adds an option to disable the suggested video end screen at the end of videos.",
) {
dependsOn(
sharedExtensionPatch,
disableSuggestedVideoEndScreenResourcePatch,
)
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
createEndScreenViewFingerprint.method.apply {
val addOnClickEventListenerIndex = createEndScreenViewFingerprint.patternMatch!!.endIndex - 1
val viewRegister = getInstruction<FiveRegisterInstruction>(addOnClickEventListenerIndex).registerC
addInstruction(
addOnClickEventListenerIndex + 1,
"invoke-static {v$viewRegister}, " +
"$EXTENSION_CLASS_DESCRIPTOR->closeEndScreen(Landroid/widget/ImageView;)V",
)
}
}
}

View File

@@ -1,18 +0,0 @@
package app.revanced.patches.youtube.layout.hide.suggestedvideoendscreen
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val createEndScreenViewFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")
parameters("Landroid/content/Context;")
opcodes(
Opcode.INVOKE_DIRECT,
Opcode.INVOKE_VIRTUAL,
Opcode.CONST,
)
literal { sizeAdjustableLiteAutoNavOverlay }
}

View File

@@ -134,7 +134,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/
@Suppress("unused") @Suppress("unused")
val miniplayerPatch = bytecodePatch( val miniplayerPatch = bytecodePatch(
name = "Miniplayer", name = "Miniplayer",
description = "Adds options to change the in app minimized player." description = "Adds options to change the in-app minimized player."
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,

View File

@@ -12,6 +12,8 @@ import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater
@@ -71,20 +73,29 @@ val spoofAppVersionPatch = bytecodePatch(
addResources("youtube", "layout.spoofappversion.spoofAppVersionPatch") addResources("youtube", "layout.spoofappversion.spoofAppVersionPatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences( PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_spoof_app_version"), // Group the switch and list preference together, since General menu is sorted by name
if (is_19_17_or_greater) { // and the preferences can be scattered apart with non English langauges.
ListPreference( PreferenceCategory(
key = "revanced_spoof_app_version_target", titleKey = null,
summaryKey = null, sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = setOf(
SwitchPreference("revanced_spoof_app_version"),
if (is_19_17_or_greater) {
ListPreference(
key = "revanced_spoof_app_version_target",
summaryKey = null,
)
} else {
ListPreference(
key = "revanced_spoof_app_version_target",
summaryKey = null,
entriesKey = "revanced_spoof_app_version_target_legacy_entries",
entryValuesKey = "revanced_spoof_app_version_target_legacy_entry_values"
)
}
) )
} else { )
ListPreference(
key = "revanced_spoof_app_version_target",
summaryKey = null,
entriesKey = "revanced_spoof_app_version_target_legacy_entries",
entryValuesKey = "revanced_spoof_app_version_target_legacy_entry_values"
)
}
) )
/** /**

View File

@@ -8,7 +8,10 @@ import app.revanced.patcher.patch.stringOption
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.InputType import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch
@@ -71,6 +74,9 @@ val themePatch = bytecodePatch(
) )
dependsOn( dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
lithoColorHookPatch, lithoColorHookPatch,
seekbarColorPatch, seekbarColorPatch,
versionCheckPatch, versionCheckPatch,
@@ -78,23 +84,30 @@ val themePatch = bytecodePatch(
dependsOn( dependsOn(
settingsPatch, settingsPatch,
resourceMappingPatch, resourceMappingPatch,
addResourcesPatch,
) )
execute { execute {
addResources("youtube", "layout.theme.themeResourcePatch") val preferences = mutableSetOf<BasePreference>(
PreferenceScreen.SEEKBAR.addPreferences(
SwitchPreference("revanced_seekbar_custom_color"), SwitchPreference("revanced_seekbar_custom_color"),
TextPreference("revanced_seekbar_custom_color_primary", inputType = InputType.TEXT_CAP_CHARACTERS), TextPreference("revanced_seekbar_custom_color_primary", inputType = InputType.TEXT_CAP_CHARACTERS),
) )
if (is_19_25_or_greater) { if (is_19_25_or_greater) {
PreferenceScreen.SEEKBAR.addPreferences( preferences += TextPreference(
TextPreference("revanced_seekbar_custom_color_accent", inputType = InputType.TEXT_CAP_CHARACTERS), "revanced_seekbar_custom_color_accent",
inputType = InputType.TEXT_CAP_CHARACTERS
) )
} }
PreferenceScreen.SEEKBAR.addPreferences(
PreferenceCategory(
titleKey = null,
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = preferences
)
)
// Edit theme colors via resources. // Edit theme colors via resources.
document("res/values/colors.xml").use { document -> document("res/values/colors.xml").use { document ->
@@ -125,7 +138,6 @@ val themePatch = bytecodePatch(
colorValue: String, colorValue: String,
) { ) {
document(resourceFile).use { document -> document(resourceFile).use { document ->
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
resourcesNode.appendChild( resourcesNode.appendChild(
@@ -133,7 +145,7 @@ val themePatch = bytecodePatch(
setAttribute("name", colorName) setAttribute("name", colorName)
setAttribute("category", "color") setAttribute("category", "color")
textContent = colorValue textContent = colorValue
}, }
) )
} }
} }
@@ -152,11 +164,10 @@ val themePatch = bytecodePatch(
// Edit splash screen files and change the background color, // Edit splash screen files and change the background color,
// if the background colors are set. // if the background colors are set.
if (darkThemeBackgroundColor != null && lightThemeBackgroundColor != null) { if (darkThemeBackgroundColor != null && lightThemeBackgroundColor != null) {
val splashScreenResourceFiles = val splashScreenResourceFiles = listOf(
listOf( "res/drawable/quantum_launchscreen_youtube.xml",
"res/drawable/quantum_launchscreen_youtube.xml", "res/drawable-sw600dp/quantum_launchscreen_youtube.xml",
"res/drawable-sw600dp/quantum_launchscreen_youtube.xml", )
)
splashScreenResourceFiles.forEach editSplashScreen@{ resourceFile -> splashScreenResourceFiles.forEach editSplashScreen@{ resourceFile ->
document(resourceFile).use { document -> document(resourceFile).use { document ->
@@ -200,10 +211,7 @@ val themePatch = bytecodePatch(
} }
} }
} }
}, }
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
) )
compatibleWith( compatibleWith(

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.misc.debugging package app.revanced.patches.youtube.misc.debugging
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -11,9 +12,11 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/EnableDebuggingPatch;" "Lapp/revanced/extension/youtube/patches/EnableDebuggingPatch;"
@@ -61,19 +64,17 @@ val enableDebuggingPatch = bytecodePatch(
experimentalBooleanFeatureFlagFingerprint.match( experimentalBooleanFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply { ).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT) findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
// It appears that all usage of this method has a default of 'false', addInstructions(
// so there's no need to pass in the default. index,
addInstructions( """
insertIndex, invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
""" move-result v$register
move-result v0 """
invoke-static { v0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZJ)Z )
move-result v0 }
return v0
"""
)
} }
experimentalDoubleFeatureFlagFingerprint.match( experimentalDoubleFeatureFlagFingerprint.match(
@@ -92,7 +93,6 @@ val enableDebuggingPatch = bytecodePatch(
) )
} }
experimentalLongFeatureFlagFingerprint.match( experimentalLongFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply { ).method.apply {
@@ -108,21 +108,22 @@ val enableDebuggingPatch = bytecodePatch(
""" """
) )
experimentalStringFeatureFlagFingerprint.match( }
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
addInstructions( experimentalStringFeatureFlagFingerprint.match(
insertIndex, experimentalFeatureFlagParentFingerprint.originalClassDef
""" ).method.apply {
move-result-object v0 val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0 addInstructions(
return-object v0 insertIndex,
""" """
) move-result-object v0
} invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
} }
// There exists other experimental accessor methods for byte[] // There exists other experimental accessor methods for byte[]

View File

@@ -11,9 +11,9 @@ internal val experimentalFeatureFlagParentFingerprint = fingerprint {
} }
internal val experimentalBooleanFeatureFlagFingerprint = fingerprint { internal val experimentalBooleanFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Z") returns("Z")
parameters("J", "Z") parameters("L", "J", "Z")
} }
internal val experimentalDoubleFeatureFlagFingerprint = fingerprint { internal val experimentalDoubleFeatureFlagFingerprint = fingerprint {
@@ -33,4 +33,3 @@ internal val experimentalStringFeatureFlagFingerprint = fingerprint {
returns("Ljava/lang/String;") returns("Ljava/lang/String;")
parameters("J", "Ljava/lang/String;") parameters("J", "Ljava/lang/String;")
} }

View File

@@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
val openLinksExternallyPatch = bytecodePatch( val openLinksExternallyPatch = bytecodePatch(
name = "Open links externally", name = "Open links externally",
description = "Adds an option to always open links in your browser instead of in the in-app-browser.", description = "Adds an option to always open links in your browser instead of the in-app browser.",
) { ) {
dependsOn( dependsOn(
transformInstructionsPatch( transformInstructionsPatch(

View File

@@ -27,7 +27,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
lateinit var addLithoFilter: (String) -> Unit lateinit var addLithoFilter: (String) -> Unit
private set private set
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/LithoFilterPatch;" private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/LithoFilterPatch;"
val lithoFilterPatch = bytecodePatch( val lithoFilterPatch = bytecodePatch(
description = "Hooks the method which parses the bytes into a ComponentContext to filter components.", description = "Hooks the method which parses the bytes into a ComponentContext to filter components.",

View File

@@ -16,6 +16,23 @@ internal val actionBarSearchResultsFingerprint = fingerprint {
literal { actionBarSearchResultsViewMicId } literal { actionBarSearchResultsViewMicId }
} }
internal val toolbarLayoutFingerprint = fingerprint {
accessFlags(AccessFlags.PROTECTED, AccessFlags.CONSTRUCTOR)
literal { toolbarContainerId }
}
/**
* Matches to https://android.googlesource.com/platform/frameworks/support/+/9eee6ba/v7/appcompat/src/android/support/v7/widget/Toolbar.java#963
*/
internal val appCompatToolbarBackButtonFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/graphics/drawable/Drawable;")
parameters()
custom { methodDef, classDef ->
classDef.type == "Landroid/support/v7/widget/Toolbar;"
}
}
/** /**
* Matches to the class found in [pivotBarConstructorFingerprint]. * Matches to the class found in [pivotBarConstructorFingerprint].
*/ */

View File

@@ -8,6 +8,7 @@ import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.misc.mapping.get import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.mapping.resourceMappings
@@ -18,12 +19,16 @@ import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.util.MethodUtil
internal var imageOnlyTabResourceId = -1L internal var imageOnlyTabResourceId = -1L
@@ -32,6 +37,8 @@ internal var actionBarSearchResultsViewMicId = -1L
private set private set
internal var ytFillBellId = -1L internal var ytFillBellId = -1L
private set private set
internal var toolbarContainerId = -1L
private set
private val navigationBarHookResourcePatch = resourcePatch { private val navigationBarHookResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch) dependsOn(resourceMappingPatch)
@@ -40,6 +47,7 @@ private val navigationBarHookResourcePatch = resourcePatch {
imageOnlyTabResourceId = resourceMappings["layout", "image_only_tab"] imageOnlyTabResourceId = resourceMappings["layout", "image_only_tab"]
actionBarSearchResultsViewMicId = resourceMappings["layout", "action_bar_search_results_view_mic"] actionBarSearchResultsViewMicId = resourceMappings["layout", "action_bar_search_results_view_mic"]
ytFillBellId = resourceMappings["drawable", "yt_fill_bell_black_24"] ytFillBellId = resourceMappings["drawable", "yt_fill_bell_black_24"]
toolbarContainerId = resourceMappings["id", "toolbar_container"]
} }
} }
@@ -47,6 +55,8 @@ internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/shared/NavigationBar;" "Lapp/revanced/extension/youtube/shared/NavigationBar;"
internal const val EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR = internal const val EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR =
"Lapp/revanced/extension/youtube/shared/NavigationBar\$NavigationButton;" "Lapp/revanced/extension/youtube/shared/NavigationBar\$NavigationButton;"
private const val EXTENSION_TOOLBAR_INTERFACE =
"Lapp/revanced/extension/youtube/shared/NavigationBar${'$'}AppCompatToolbarPatchInterface;"
lateinit var hookNavigationButtonCreated: (String) -> Unit lateinit var hookNavigationButtonCreated: (String) -> Unit
@@ -143,11 +153,58 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
) )
} }
// Hook the back button visibility.
toolbarLayoutFingerprint.method.apply {
val index = indexOfFirstInstructionOrThrow {
opcode == Opcode.CHECK_CAST && getReference<TypeReference>()?.type ==
"Lcom/google/android/apps/youtube/app/ui/actionbar/MainCollapsingToolbarLayout;"
}
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstruction(
index + 1,
"invoke-static { v$register }, ${EXTENSION_CLASS_DESCRIPTOR}->setToolbar(Landroid/widget/FrameLayout;)V"
)
}
// Add interface for extensions code to call obfuscated methods.
appCompatToolbarBackButtonFingerprint.let {
it.classDef.apply {
interfaces.add(EXTENSION_TOOLBAR_INTERFACE)
val definingClass = type
val obfuscatedMethodName = it.originalMethod.name
val returnType = "Landroid/graphics/drawable/Drawable;"
methods.add(
ImmutableMethod(
definingClass,
"patch_getNavigationIcon",
listOf(),
returnType,
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
addInstructions(
0,
"""
invoke-virtual { p0 }, $definingClass->$obfuscatedMethodName()$returnType
move-result-object v0
return-object v0
"""
)
}
)
}
}
hookNavigationButtonCreated = { extensionClassDescriptor -> hookNavigationButtonCreated = { extensionClassDescriptor ->
navigationBarHookCallbackFingerprint.method.addInstruction( navigationBarHookCallbackFingerprint.method.addInstruction(
0, 0,
"invoke-static { p0, p1 }, " + "invoke-static { p0, p1 }, $extensionClassDescriptor->navigationTabCreated" +
"$extensionClassDescriptor->navigationTabCreated" +
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V", "(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
) )
} }

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.misc.playertype package app.revanced.patches.youtube.misc.playertype
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@@ -15,6 +16,12 @@ internal val playerTypeFingerprint = fingerprint {
custom { _, classDef -> classDef.endsWith("/YouTubePlayerOverlaysLayout;") } custom { _, classDef -> classDef.endsWith("/YouTubePlayerOverlaysLayout;") }
} }
internal val reelWatchPagerFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")
literal { reelWatchPlayerId }
}
internal val videoStateFingerprint = fingerprint { internal val videoStateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V") returns("V")

View File

@@ -4,15 +4,34 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PlayerTypeHookPatch;" private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PlayerTypeHookPatch;"
internal var reelWatchPlayerId = -1L
private set
private val playerTypeHookResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
execute {
reelWatchPlayerId = resourceMappings["id", "reel_watch_player"]
}
}
val playerTypeHookPatch = bytecodePatch( val playerTypeHookPatch = bytecodePatch(
description = "Hook to get the current player type and video playback state.", description = "Hook to get the current player type and video playback state.",
) { ) {
dependsOn(sharedExtensionPatch) dependsOn(sharedExtensionPatch, playerTypeHookResourcePatch)
execute { execute {
playerTypeFingerprint.method.addInstruction( playerTypeFingerprint.method.addInstruction(
@@ -20,6 +39,17 @@ val playerTypeHookPatch = bytecodePatch(
"invoke-static {p1}, $EXTENSION_CLASS_DESCRIPTOR->setPlayerType(Ljava/lang/Enum;)V", "invoke-static {p1}, $EXTENSION_CLASS_DESCRIPTOR->setPlayerType(Ljava/lang/Enum;)V",
) )
reelWatchPagerFingerprint.method.apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(reelWatchPlayerId)
val registerIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT_OBJECT)
val viewRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
addInstruction(
registerIndex + 1,
"invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR->onShortsCreate(Landroid/view/View;)V"
)
}
videoStateFingerprint.method.apply { videoStateFingerprint.method.apply {
val endIndex = videoStateFingerprint.patternMatch!!.endIndex val endIndex = videoStateFingerprint.patternMatch!!.endIndex
val videoStateFieldName = getInstruction<ReferenceInstruction>(endIndex).reference val videoStateFieldName = getInstruction<ReferenceInstruction>(endIndex).reference
@@ -27,9 +57,9 @@ val playerTypeHookPatch = bytecodePatch(
addInstructions( addInstructions(
0, 0,
""" """
iget-object v0, p1, $videoStateFieldName # copy VideoState parameter field iget-object v0, p1, $videoStateFieldName # copy VideoState parameter field
invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->setVideoState(Ljava/lang/Enum;)V invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->setVideoState(Ljava/lang/Enum;)V
""", """
) )
} }
} }

View File

@@ -20,7 +20,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
val removeTrackingQueryParameterPatch = bytecodePatch( val removeTrackingQueryParameterPatch = bytecodePatch(
name = "Remove tracking query parameter", name = "Remove tracking query parameter",
description = "Adds an option to remove the tracking info from links you share.", description = "Adds an option to remove the tracking parameter from links you share.",
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,

View File

@@ -301,11 +301,9 @@ object PreferenceScreen : BasePreferenceScreen() {
summaryKey = null, summaryKey = null,
) )
// Don't sort, because title sorting scatters the custom color preferences.
val SEEKBAR = Screen( val SEEKBAR = Screen(
key = "revanced_settings_screen_07_seekbar", key = "revanced_settings_screen_07_seekbar",
summaryKey = null, summaryKey = null,
sorting = Sorting.UNSORTED,
) )
val SWIPE_CONTROLS = Screen( val SWIPE_CONTROLS = Screen(
key = "revanced_settings_screen_08_swipe_controls", key = "revanced_settings_screen_08_swipe_controls",
@@ -323,6 +321,7 @@ object PreferenceScreen : BasePreferenceScreen() {
val VIDEO = Screen( val VIDEO = Screen(
key = "revanced_settings_screen_12_video", key = "revanced_settings_screen_12_video",
summaryKey = null, summaryKey = null,
sorting = Sorting.BY_KEY,
) )
override fun commit(screen: PreferenceScreenPreference) { override fun commit(screen: PreferenceScreenPreference) {

View File

@@ -6,6 +6,8 @@ import app.revanced.patches.shared.misc.settings.preference.NonInteractivePrefer
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
@@ -25,7 +27,10 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
dependsOn( dependsOn(
userAgentClientSpoofPatch, userAgentClientSpoofPatch,
settingsPatch, settingsPatch,
versionCheckPatch
) )
}, {
is_19_34_or_greater
}, { }, {
addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch") addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch")

View File

@@ -0,0 +1,118 @@
package app.revanced.patches.youtube.video.quality
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal var videoQualityBottomSheetListFragmentTitle = -1L
private set
internal var videoQualityQuickMenuAdvancedMenuDescription = -1L
private set
private val advancedVideoQualityMenuResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
execute {
// Used for the old type of the video quality menu.
videoQualityBottomSheetListFragmentTitle = resourceMappings[
"layout",
"video_quality_bottom_sheet_list_fragment_title",
]
videoQualityQuickMenuAdvancedMenuDescription = resourceMappings[
"string",
"video_quality_quick_menu_advanced_menu_description",
]
}
}
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/playback/quality/AdvancedVideoQualityMenuPatch;"
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/components/AdvancedVideoQualityMenuFilter;"
internal val advancedVideoQualityMenuPatch = bytecodePatch {
dependsOn(
advancedVideoQualityMenuResourcePatch,
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
lithoFilterPatch,
recyclerViewTreeHookPatch,
)
execute {
addResources("youtube", "video.quality.advancedVideoQualityMenuPatch")
settingsMenuVideoQualityGroup.add(
SwitchPreference("revanced_advanced_video_quality_menu")
)
// region Patch for the old type of the video quality menu.
// Used for regular videos when spoofing to old app version,
// and for the Shorts quality flyout on newer app versions.
videoQualityMenuViewInflateFingerprint.let {
it.method.apply {
val checkCastIndex = it.patternMatch!!.endIndex
val listViewRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(
checkCastIndex + 1,
"invoke-static { v$listViewRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
"showAdvancedVideoQualityMenu(Landroid/widget/ListView;)V",
)
}
}
// Force YT to add the 'advanced' quality menu for Shorts.
videoQualityMenuOptionsFingerprint.let {
val patternMatch = it.patternMatch!!
val startIndex = patternMatch.startIndex
val insertIndex = patternMatch.endIndex
if (startIndex != 0) throw PatchException("Unexpected opcode start index: $startIndex")
it.method.apply {
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
// A condition controls whether to show the three or four items quality menu.
// Force the four items quality menu to make the "Advanced" item visible, necessary for the patch.
addInstructions(
insertIndex,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation(Z)Z
move-result v$register
"""
)
}
}
// endregion
// region Patch for the new type of the video quality menu.
addRecyclerViewTreeHook(EXTENSION_CLASS_DESCRIPTOR)
// Required to check if the video quality menu is currently shown in order to click on the "Advanced" item.
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
// endregion
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.video.quality package app.revanced.patches.youtube.video.quality
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@@ -35,3 +36,41 @@ internal val videoQualitySetterFingerprint = fingerprint {
) )
strings("menu_item_video_quality") strings("menu_item_video_quality")
} }
internal val videoQualityMenuOptionsFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC)
returns("[L")
parameters("Landroid/content/Context", "L", "L")
opcodes(
Opcode.CONST_4, // First instruction of method.
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN, // Use the quality menu, that contains the advanced menu.
Opcode.IF_NEZ,
)
literal { videoQualityQuickMenuAdvancedMenuDescription }
}
internal val videoQualityMenuViewInflateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
parameters("L", "L", "L")
opcodes(
Opcode.INVOKE_SUPER,
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_16,
Opcode.INVOKE_VIRTUAL,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
)
literal { videoQualityBottomSheetListFragmentTitle }
}

View File

@@ -10,7 +10,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
import app.revanced.patches.youtube.video.information.onCreateHook import app.revanced.patches.youtube.video.information.onCreateHook
@@ -22,47 +22,47 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch;" "Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch;"
val rememberVideoQualityPatch = bytecodePatch( val rememberVideoQualityPatch = bytecodePatch {
name = "Remember video quality",
description = "Adds an option to remember the last video quality selected.",
) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
videoInformationPatch, videoInformationPatch,
playerTypeHookPatch,
settingsPatch, settingsPatch,
addResourcesPatch, addResourcesPatch,
) )
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute { execute {
addResources("youtube", "video.quality.rememberVideoQualityPatch") addResources("youtube", "video.quality.rememberVideoQualityPatch")
PreferenceScreen.VIDEO.addPreferences( settingsMenuVideoQualityGroup.addAll(listOf(
SwitchPreference("revanced_remember_video_quality_last_selected"),
ListPreference(
key = "revanced_video_quality_default_wifi",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
ListPreference( ListPreference(
key = "revanced_video_quality_default_mobile", key = "revanced_video_quality_default_mobile",
summaryKey = null, summaryKey = null,
entriesKey = "revanced_video_quality_default_entries", entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values", entryValuesKey = "revanced_video_quality_default_entry_values",
), ),
) ListPreference(
key = "revanced_video_quality_default_wifi",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
SwitchPreference("revanced_remember_video_quality_last_selected"),
ListPreference(
key = "revanced_shorts_quality_default_mobile",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
ListPreference(
key = "revanced_shorts_quality_default_wifi",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
SwitchPreference("revanced_remember_shorts_quality_last_selected")
))
/* /*
* The following code works by hooking the method which is called when the user selects a video quality * The following code works by hooking the method which is called when the user selects a video quality

View File

@@ -0,0 +1,48 @@
package app.revanced.patches.youtube.video.quality
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
/**
* Video quality settings. Used to organize all speed related settings together.
*/
internal val settingsMenuVideoQualityGroup = mutableSetOf<BasePreference>()
@Suppress("unused")
val videoQualityPatch = bytecodePatch(
name = "Video quality",
description = "Adds options to use the advanced video quality menu and set default video qualities."
) {
dependsOn(
rememberVideoQualityPatch,
advancedVideoQualityMenuPatch,
)
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
)
)
execute {
PreferenceScreen.VIDEO.addPreferences(
// Keep the preferences organized together.
PreferenceCategory(
key = "revanced_01_video_key", // Dummy key to force the quality preferences first.
titleKey = null,
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = settingsMenuVideoQualityGroup
)
)
}
}

View File

@@ -1,20 +1,29 @@
package app.revanced.patches.youtube.video.speed package app.revanced.patches.youtube.video.speed
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.video.speed.button.playbackSpeedButtonPatch import app.revanced.patches.youtube.video.speed.button.playbackSpeedButtonPatch
import app.revanced.patches.youtube.video.speed.custom.customPlaybackSpeedPatch import app.revanced.patches.youtube.video.speed.custom.customPlaybackSpeedPatch
import app.revanced.patches.youtube.video.speed.remember.rememberPlaybackSpeedPatch import app.revanced.patches.youtube.video.speed.remember.rememberPlaybackSpeedPatch
/**
* Speed menu settings. Used to organize all speed related settings together.
*/
internal val settingsMenuVideoSpeedGroup = mutableSetOf<BasePreference>()
@Suppress("unused") @Suppress("unused")
val playbackSpeedPatch = bytecodePatch( val playbackSpeedPatch = bytecodePatch(
name = "Playback speed", name = "Playback speed",
description = "Adds options to customize available playback speeds, remember the last playback speed selected " + description = "Adds options to customize available playback speeds, set default a playback speed, " +
"and show a speed dialog button in the video player.", "and show a speed dialog button in the video player.",
) { ) {
dependsOn( dependsOn(
playbackSpeedButtonPatch,
customPlaybackSpeedPatch, customPlaybackSpeedPatch,
rememberPlaybackSpeedPatch, rememberPlaybackSpeedPatch,
playbackSpeedButtonPatch,
) )
compatibleWith( compatibleWith(
@@ -26,6 +35,18 @@ val playbackSpeedPatch = bytecodePatch(
"19.45.38", "19.45.38",
"19.46.42", "19.46.42",
"19.47.53", "19.47.53",
), )
) )
execute {
PreferenceScreen.VIDEO.addPreferences(
PreferenceCategory(
key = "revanced_zz_video_key", // Dummy key to force the speed settings last.
titleKey = null,
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = settingsMenuVideoSpeedGroup
)
)
}
} }

View File

@@ -5,6 +5,7 @@ import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playercontrols.* import app.revanced.patches.youtube.misc.playercontrols.*
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
@@ -35,11 +36,12 @@ val playbackSpeedButtonPatch = bytecodePatch(
description = "Adds the option to display playback speed dialog button in the video player.", description = "Adds the option to display playback speed dialog button in the video player.",
) { ) {
dependsOn( dependsOn(
playbackSpeedButtonResourcePatch, sharedExtensionPatch,
customPlaybackSpeedPatch,
playerControlsPatch,
settingsPatch, settingsPatch,
addResourcesPatch, addResourcesPatch,
customPlaybackSpeedPatch,
playbackSpeedButtonResourcePatch,
playerControlsPatch,
) )
execute { execute {

View File

@@ -25,8 +25,8 @@ import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.video.speed.settingsMenuVideoSpeedGroup
import app.revanced.util.* import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
@@ -60,24 +60,29 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
lithoFilterPatch,
settingsPatch, settingsPatch,
recyclerViewTreeHookPatch,
customPlaybackSpeedResourcePatch,
addResourcesPatch, addResourcesPatch,
versionCheckPatch lithoFilterPatch,
versionCheckPatch,
recyclerViewTreeHookPatch,
customPlaybackSpeedResourcePatch
) )
execute { execute {
addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch") addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch")
PreferenceScreen.VIDEO.addPreferences( settingsMenuVideoSpeedGroup.addAll(
SwitchPreference("revanced_custom_speed_menu"), listOf(
TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE), SwitchPreference("revanced_custom_speed_menu"),
TextPreference(
"revanced_custom_playback_speeds",
inputType = InputType.TEXT_MULTI_LINE
),
)
) )
if (is_19_25_or_greater) { if (is_19_25_or_greater) {
PreferenceScreen.VIDEO.addPreferences( settingsMenuVideoSpeedGroup.add(
TextPreference("revanced_speed_tap_and_hold", inputType = InputType.NUMBER_DECIMAL), TextPreference("revanced_speed_tap_and_hold", inputType = InputType.NUMBER_DECIMAL),
) )
} }

View File

@@ -9,10 +9,10 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.video.information.* import app.revanced.patches.youtube.video.information.*
import app.revanced.patches.youtube.video.speed.custom.customPlaybackSpeedPatch import app.revanced.patches.youtube.video.speed.custom.customPlaybackSpeedPatch
import app.revanced.patches.youtube.video.speed.settingsMenuVideoSpeedGroup
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
@@ -22,26 +22,29 @@ internal val rememberPlaybackSpeedPatch = bytecodePatch {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
settingsPatch, settingsPatch,
videoInformationPatch,
customPlaybackSpeedPatch,
addResourcesPatch, addResourcesPatch,
videoInformationPatch,
customPlaybackSpeedPatch
) )
execute { execute {
addResources("youtube", "video.speed.remember.rememberPlaybackSpeedPatch") addResources("youtube", "video.speed.remember.rememberPlaybackSpeedPatch")
PreferenceScreen.VIDEO.addPreferences( settingsMenuVideoSpeedGroup.addAll(
SwitchPreference("revanced_remember_playback_speed_last_selected"), listOf(
ListPreference( ListPreference(
key = "revanced_playback_speed_default", key = "revanced_playback_speed_default",
summaryKey = null, summaryKey = null,
// Entries and values are set by the extension code based on the actual speeds available. // Entries and values are set by the extension code based on the actual speeds available.
entriesKey = null, entriesKey = null,
entryValuesKey = null, entryValuesKey = null,
), ),
SwitchPreference("revanced_remember_playback_speed_last_selected")
)
) )
onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted") onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted")
userSelectedPlaybackSpeedHook( userSelectedPlaybackSpeedHook(
EXTENSION_CLASS_DESCRIPTOR, EXTENSION_CLASS_DESCRIPTOR,
"userSelectedPlaybackSpeed", "userSelectedPlaybackSpeed",

View File

@@ -1,43 +0,0 @@
package app.revanced.patches.youtube.video.videoqualitymenu
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val videoQualityMenuOptionsFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC)
returns("[L")
parameters("Landroid/content/Context", "L", "L")
opcodes(
Opcode.CONST_4, // First instruction of method.
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN, // Use the quality menu, that contains the advanced menu.
Opcode.IF_NEZ,
)
literal { videoQualityQuickMenuAdvancedMenuDescription }
}
internal val videoQualityMenuViewInflateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
parameters("L", "L", "L")
opcodes(
Opcode.INVOKE_SUPER,
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_16,
Opcode.INVOKE_VIRTUAL,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
)
literal { videoQualityBottomSheetListFragmentTitle }
}

View File

@@ -1,135 +1,10 @@
package app.revanced.patches.youtube.video.videoqualitymenu package app.revanced.patches.youtube.video.videoqualitymenu
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.youtube.video.quality.videoQualityPatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal var videoQualityBottomSheetListFragmentTitle = -1L
private set
internal var videoQualityQuickMenuAdvancedMenuDescription = -1L
private set
private val restoreOldVideoQualityMenuResourcePatch = resourcePatch {
dependsOn(
settingsPatch,
resourceMappingPatch,
addResourcesPatch,
)
execute {
addResources("youtube", "video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_restore_old_video_quality_menu"),
)
// Used for the old type of the video quality menu.
videoQualityBottomSheetListFragmentTitle = resourceMappings[
"layout",
"video_quality_bottom_sheet_list_fragment_title",
]
videoQualityQuickMenuAdvancedMenuDescription = resourceMappings[
"string",
"video_quality_quick_menu_advanced_menu_description",
]
}
}
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/components/VideoQualityMenuFilterPatch;"
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/playback/quality/RestoreOldVideoQualityMenuPatch;"
@Suppress("unused") @Suppress("unused")
val restoreOldVideoQualityMenuPatch = bytecodePatch( @Deprecated("Use 'Video Quality' instead.")
name = "Restore old video quality menu", val restoreOldVideoQualityMenuPatch = bytecodePatch {
description = "Adds an option to restore the old video quality menu with specific video resolution options.", dependsOn(videoQualityPatch)
}
) {
dependsOn(
sharedExtensionPatch,
restoreOldVideoQualityMenuResourcePatch,
lithoFilterPatch,
recyclerViewTreeHookPatch,
)
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
// region Patch for the old type of the video quality menu.
// Used for regular videos when spoofing to old app version,
// and for the Shorts quality flyout on newer app versions.
videoQualityMenuViewInflateFingerprint.method.apply {
val checkCastIndex = videoQualityMenuViewInflateFingerprint.patternMatch!!.endIndex
val listViewRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(
checkCastIndex + 1,
"invoke-static { v$listViewRegister }, " +
"$EXTENSION_CLASS_DESCRIPTOR->" +
"showOldVideoQualityMenu(Landroid/widget/ListView;)V",
)
}
// Force YT to add the 'advanced' quality menu for Shorts.
val patternMatch = videoQualityMenuOptionsFingerprint.patternMatch!!
val startIndex = patternMatch.startIndex
if (startIndex != 0) throw PatchException("Unexpected opcode start index: $startIndex")
val insertIndex = patternMatch.endIndex
videoQualityMenuOptionsFingerprint.method.apply {
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
// A condition controls whether to show the three or four items quality menu.
// Force the four items quality menu to make the "Advanced" item visible, necessary for the patch.
addInstructions(
insertIndex,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation(Z)Z
move-result v$register
""",
)
}
// endregion
// region Patch for the new type of the video quality menu.
addRecyclerViewTreeHook(EXTENSION_CLASS_DESCRIPTOR)
// Required to check if the video quality menu is currently shown in order to click on the "Advanced" item.
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
// endregion
}
}

View File

@@ -408,10 +408,13 @@ internal fun MutableMethod.insertFeatureFlagBooleanOverride(literal: Long, exten
val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(index).registerA val register = getInstruction<OneRegisterInstruction>(index).registerA
val operation = if (register < 16) "invoke-static { v$register }"
else "invoke-static/range { v$register .. v$register }"
addInstructions( addInstructions(
index + 1, index + 1,
""" """
invoke-static { v$register }, $extensionsMethod $operation, $extensionsMethod
move-result v$register move-result v$register
""" """
) )
@@ -458,7 +461,7 @@ fun MutableMethod.returnEarly(bool: Boolean = false) {
return v0 return v0
""" """
else -> throw Exception("This case should never happen.") else -> throw Exception("Return type is not supported: $this")
} }
addInstructions(0, stringInstructions) addInstructions(0, stringInstructions)

View File

@@ -130,7 +130,7 @@ Second \"item\" text"</string>
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. --> <!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. --> <!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch> </patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch"> <patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
</patch> </patch>
<patch id="layout.hide.time.hideTimestampPatch"> <patch id="layout.hide.time.hideTimestampPatch">
</patch> </patch>
@@ -177,8 +177,6 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="layout.theme.themePatch"> <patch id="layout.theme.themePatch">
</patch> </patch>
<patch id="layout.theme.themeResourcePatch">
</patch>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch"> <patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
</patch> </patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch"> <patch id="layout.thumbnails.alternativeThumbnailsPatch">
@@ -206,6 +204,7 @@ Second \"item\" text"</string>
<patch id="misc.zoomhaptics.zoomHapticsPatch"> <patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch> </patch>
<patch id="video.audio.forceOriginalAudioPatch"> <patch id="video.audio.forceOriginalAudioPatch">
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
</patch> </patch>
<patch id="video.quality.rememberVideoQualityPatch"> <patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto --> <!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -218,7 +217,7 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="video.hdr.disableHdrPatch"> <patch id="video.hdr.disableHdrPatch">
</patch> </patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch"> <patch id="video.quality.advancedVideoQualityMenuPatch">
</patch> </patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch"> <patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch> </patch>

View File

@@ -130,7 +130,7 @@ Second \"item\" text"</string>
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. --> <!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. --> <!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch> </patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch"> <patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
</patch> </patch>
<patch id="layout.hide.time.hideTimestampPatch"> <patch id="layout.hide.time.hideTimestampPatch">
</patch> </patch>
@@ -177,8 +177,6 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="layout.theme.themePatch"> <patch id="layout.theme.themePatch">
</patch> </patch>
<patch id="layout.theme.themeResourcePatch">
</patch>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch"> <patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
</patch> </patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch"> <patch id="layout.thumbnails.alternativeThumbnailsPatch">
@@ -206,6 +204,7 @@ Second \"item\" text"</string>
<patch id="misc.zoomhaptics.zoomHapticsPatch"> <patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch> </patch>
<patch id="video.audio.forceOriginalAudioPatch"> <patch id="video.audio.forceOriginalAudioPatch">
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
</patch> </patch>
<patch id="video.quality.rememberVideoQualityPatch"> <patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto --> <!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -218,7 +217,7 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="video.hdr.disableHdrPatch"> <patch id="video.hdr.disableHdrPatch">
</patch> </patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch"> <patch id="video.quality.advancedVideoQualityMenuPatch">
</patch> </patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch"> <patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch> </patch>

View File

@@ -301,13 +301,13 @@ Second \"item\" text"</string>
<string name="revanced_hide_description_components_screen_title">وصف الفيديو</string> <string name="revanced_hide_description_components_screen_title">وصف الفيديو</string>
<string name="revanced_hide_description_components_screen_summary">إخفاء أو عرض مكونات وصف الفيديو</string> <string name="revanced_hide_description_components_screen_summary">إخفاء أو عرض مكونات وصف الفيديو</string>
<string name="revanced_hide_filter_bar_screen_title">شريط التصفية</string> <string name="revanced_hide_filter_bar_screen_title">شريط التصفية</string>
<string name="revanced_hide_filter_bar_screen_summary">إخفاء شريط التصفية أو عرضه في الموجز والبحث الفيديوهات ذات الصلة</string> <string name="revanced_hide_filter_bar_screen_summary">إخفاء أو إظهار شريط الفلتر في الخلاصة ونتائج البحث ومقاطع الفيديو ذات الصلة</string>
<string name="revanced_hide_filter_bar_feed_in_feed_title">إخفاء في الموجز</string> <string name="revanced_hide_filter_bar_feed_in_feed_title">إخفاء في الموجز</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_on">مخفي في الموجز</string> <string name="revanced_hide_filter_bar_feed_in_feed_summary_on">مخفي في الموجز</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_off">يعرض في الموجز</string> <string name="revanced_hide_filter_bar_feed_in_feed_summary_off">يعرض في الموجز</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">إخفاء في البحث</string> <string name="revanced_hide_filter_bar_feed_in_search_title">إخفاء في نتائج البحث</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">مخفي في البحث</string> <string name="revanced_hide_filter_bar_feed_in_search_summary_on">مخفي في نتائج البحث</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">يعرض في البحث</string> <string name="revanced_hide_filter_bar_feed_in_search_summary_off">يظهر في نتائج البحث</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_title">إخفاء في الفيديوهات ذات الصلة</string> <string name="revanced_hide_filter_bar_feed_in_related_videos_title">إخفاء في الفيديوهات ذات الصلة</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">مخفي في الفيديوهات ذات الصلة</string> <string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">مخفي في الفيديوهات ذات الصلة</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">يعرض في الفيديوهات ذات الصلة</string> <string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">يعرض في الفيديوهات ذات الصلة</string>
@@ -404,7 +404,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_self_sponsor_ads_title">إخفاء بطاقات الرعاية الذاتية</string> <string name="revanced_hide_self_sponsor_ads_title">إخفاء بطاقات الرعاية الذاتية</string>
<string name="revanced_hide_self_sponsor_ads_summary_on">تم إخفاء بطاقات الرعاية الذاتية</string> <string name="revanced_hide_self_sponsor_ads_summary_on">تم إخفاء بطاقات الرعاية الذاتية</string>
<string name="revanced_hide_self_sponsor_ads_summary_off">يتم عرض بطاقات الرعاية الذاتية</string> <string name="revanced_hide_self_sponsor_ads_summary_off">يتم عرض بطاقات الرعاية الذاتية</string>
<string name="revanced_hide_products_banner_title">إخفاء لافتة لعرض المنتجات</string> <string name="revanced_hide_products_banner_title">إخفاء لافتة \"عرض المنتجات\"</string>
<string name="revanced_hide_products_banner_summary_on">تم إخفاء البانر</string> <string name="revanced_hide_products_banner_summary_on">تم إخفاء البانر</string>
<string name="revanced_hide_products_banner_summary_off">يتم عرض البانر</string> <string name="revanced_hide_products_banner_summary_off">يتم عرض البانر</string>
<string name="revanced_hide_end_screen_store_banner_title">إخفاء لافتة شاشة المتجر النهائية</string> <string name="revanced_hide_end_screen_store_banner_title">إخفاء لافتة شاشة المتجر النهائية</string>
@@ -663,7 +663,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">يتم عرض تذييل قائمة جودة الفيديو</string> <string name="revanced_hide_player_flyout_video_quality_footer_summary_off">يتم عرض تذييل قائمة جودة الفيديو</string>
</patch> </patch>
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch"> <patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
<string name="revanced_hide_player_previous_next_buttons_title">إخفاء أزرار الفيديو السابق &amp; التالي</string> <string name="revanced_hide_player_previous_next_buttons_title">إخفاء زري \"السابق\" و \"التالي\"</string>
<string name="revanced_hide_player_previous_next_buttons_summary_on">تم إخفاء الأزرار</string> <string name="revanced_hide_player_previous_next_buttons_summary_on">تم إخفاء الأزرار</string>
<string name="revanced_hide_player_previous_next_buttons_summary_off">يتم عرض الأزرار</string> <string name="revanced_hide_player_previous_next_buttons_summary_off">يتم عرض الأزرار</string>
<string name="revanced_hide_cast_button_title">إخفاء زر البث</string> <string name="revanced_hide_cast_button_title">إخفاء زر البث</string>
@@ -808,10 +808,13 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_navigation_bar_summary_on">تم إخفاء شريط التنقل</string> <string name="revanced_hide_shorts_navigation_bar_summary_on">تم إخفاء شريط التنقل</string>
<string name="revanced_hide_shorts_navigation_bar_summary_off">يتم عرض شريط التنقل</string> <string name="revanced_hide_shorts_navigation_bar_summary_off">يتم عرض شريط التنقل</string>
</patch> </patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch"> <patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
<string name="revanced_disable_suggested_video_end_screen_title">تعطيل شاشة نهاية الفيديو المقترح</string> <string name="revanced_end_screen_suggested_video_title">إخفاء الفيديو المقترح في شاشة النهاية</string>
<string name="revanced_disable_suggested_video_end_screen_summary_on">الفيديوهات المقترحة سيتم تعطيلها</string> <string name="revanced_end_screen_suggested_video_summary_on">"يتم إخفاء الفيديو المقترح في شاشة النهاية عند إيقاف التشغيل التلقائي
<string name="revanced_disable_suggested_video_end_screen_summary_off">الفيديوهات المقترحة سيتم عرضها</string>
يمكن تغيير التشغيل التلقائي في إعدادات YouTube:
الإعدادات ← التشغيل ← تشغيل الفيديو التالي تلقائيًا"</string>
<string name="revanced_end_screen_suggested_video_summary_off">يتم عرض الفيديو المقترح في شاشة النهاية</string>
</patch> </patch>
<patch id="layout.hide.time.hideTimestampPatch"> <patch id="layout.hide.time.hideTimestampPatch">
<string name="revanced_hide_timestamp_title">إخفاء الطابع الزمني للفيديو</string> <string name="revanced_hide_timestamp_title">إخفاء الطابع الزمني للفيديو</string>
@@ -1113,16 +1116,14 @@ Second \"item\" text"</string>
<string name="revanced_change_form_factor_entry_2">الجوّال</string> <string name="revanced_change_form_factor_entry_2">الجوّال</string>
<string name="revanced_change_form_factor_entry_3">الجهاز اللوحي</string> <string name="revanced_change_form_factor_entry_3">الجهاز اللوحي</string>
<string name="revanced_change_form_factor_entry_4">Automotive</string> <string name="revanced_change_form_factor_entry_4">Automotive</string>
<string name="revanced_change_form_factor_user_dialog_message">"تتضمن التغييرات: <string name="revanced_change_form_factor_user_dialog_message">"التغييرات تشمل:
تخطيط الجهاز اللوحي تصميم الجهاز اللوحي
إخفاء منشورات المجتمع مشاركات المجتمع مخفية
تخطيط Automotive تصميم السيارة
إخفاء قائمة سجل المشاهدة يتم فتح Shorts في المشغل العادي
استعادة علامة التبويب \"استكشاف\" يتم تنظيم الخلاصة حسب المواضيع والقنوات"</string>
• فتح فيديوهات Shorts في المشغل العادي
• تنظيم الخلاصة حسب الموضوعات والقناة"</string>
</patch> </patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch"> <patch id="layout.spoofappversion.spoofAppVersionPatch">
<string name="revanced_spoof_app_version_title">خِداع إصدار التطبيق</string> <string name="revanced_spoof_app_version_title">خِداع إصدار التطبيق</string>
@@ -1245,8 +1246,6 @@ Second \"item\" text"</string>
<string name="revanced_gradient_loading_screen_title">تمكين شاشة التحميل المتدرجة</string> <string name="revanced_gradient_loading_screen_title">تمكين شاشة التحميل المتدرجة</string>
<string name="revanced_gradient_loading_screen_summary_on">ستحتوي شاشة التحميل على خلفية متدرجة</string> <string name="revanced_gradient_loading_screen_summary_on">ستحتوي شاشة التحميل على خلفية متدرجة</string>
<string name="revanced_gradient_loading_screen_summary_off">ستحتوي شاشة التحميل على خلفية ثابتة</string> <string name="revanced_gradient_loading_screen_summary_off">ستحتوي شاشة التحميل على خلفية ثابتة</string>
</patch>
<patch id="layout.theme.themeResourcePatch">
<string name="revanced_seekbar_custom_color_title">تمكين لون شريط تقدم الفيديو المخصص</string> <string name="revanced_seekbar_custom_color_title">تمكين لون شريط تقدم الفيديو المخصص</string>
<string name="revanced_seekbar_custom_color_summary_on">يتم عرض لون شريط تقدم الفيديو المخصص</string> <string name="revanced_seekbar_custom_color_summary_on">يتم عرض لون شريط تقدم الفيديو المخصص</string>
<string name="revanced_seekbar_custom_color_summary_off">يتم عرض لون شريط تقدم الفيديو الاصلي</string> <string name="revanced_seekbar_custom_color_summary_off">يتم عرض لون شريط تقدم الفيديو الاصلي</string>
@@ -1338,8 +1337,8 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="misc.links.openLinksExternallyPatch"> <patch id="misc.links.openLinksExternallyPatch">
<string name="revanced_external_browser_title">فتح الروابط في المتصفح</string> <string name="revanced_external_browser_title">فتح الروابط في المتصفح</string>
<string name="revanced_external_browser_summary_on">فتح الروابط خارجيًا</string> <string name="revanced_external_browser_summary_on">فتح الروابط في متصفح خارجي</string>
<string name="revanced_external_browser_summary_off">فتح الروابط في التطبيق</string> <string name="revanced_external_browser_summary_off">فتح الروابط في متصفح داخل التطبيق</string>
</patch> </patch>
<patch id="misc.privacy.removeTrackingQueryParameterPatch"> <patch id="misc.privacy.removeTrackingQueryParameterPatch">
<string name="revanced_remove_tracking_query_parameter_title">إزالة معلمة تتبع الاستعلام</string> <string name="revanced_remove_tracking_query_parameter_title">إزالة معلمة تتبع الاستعلام</string>
@@ -1352,10 +1351,11 @@ Second \"item\" text"</string>
<string name="revanced_disable_zoom_haptics_summary_off">تم تمكين الاهتزاز</string> <string name="revanced_disable_zoom_haptics_summary_off">تم تمكين الاهتزاز</string>
</patch> </patch>
<patch id="video.audio.forceOriginalAudioPatch"> <patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">فرض الصوت الأصلي</string> <string name="revanced_force_original_audio_title">فرض لغة الصوت الأصلية</string>
<string name="revanced_force_original_audio_summary_on">استخدام الصوت الأصلي</string> <string name="revanced_force_original_audio_summary_on">استخدام لغة الصوت الأصلية</string>
<string name="revanced_force_original_audio_summary_off">استخدام الصوت الافتراضي</string> <string name="revanced_force_original_audio_summary_off">استخدام الصوت الافتراضي</string>
<string name="revanced_force_original_audio_not_available">لاستخدام هذه الميزة، قم بتغيير محاكاة بث المحتوى إلى نوع العميل iOS</string> <!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
<string name="revanced_force_original_audio_not_available">لاستخدام هذه الميزة، غيّر \"انتحال دفقات الفيديو\" إلى iOS TV</string>
</patch> </patch>
<patch id="video.quality.rememberVideoQualityPatch"> <patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto --> <!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -1365,9 +1365,15 @@ Second \"item\" text"</string>
<string name="revanced_remember_video_quality_last_selected_summary_off">تنطبق تغييرات الجودة على الفيديو الحالي فقط</string> <string name="revanced_remember_video_quality_last_selected_summary_off">تنطبق تغييرات الجودة على الفيديو الحالي فقط</string>
<string name="revanced_video_quality_default_wifi_title">جودة الفيديو الافتراضية على شبكة Wi-Fi</string> <string name="revanced_video_quality_default_wifi_title">جودة الفيديو الافتراضية على شبكة Wi-Fi</string>
<string name="revanced_video_quality_default_mobile_title">جودة الفيديو الافتراضية على شبكة الجوَّال</string> <string name="revanced_video_quality_default_mobile_title">جودة الفيديو الافتراضية على شبكة الجوَّال</string>
<string name="revanced_remember_shorts_quality_last_selected_title">تذكر تغييرات جودة Shorts</string>
<string name="revanced_remember_shorts_quality_last_selected_summary_on">تنطبق تغييرات الجودة على جميع فيديوهات Shorts</string>
<string name="revanced_remember_shorts_quality_last_selected_summary_off">تنطبق تغييرات الجودة فقط على فيديو Short الحالي</string>
<string name="revanced_shorts_quality_default_wifi_title">جودة Shorts الافتراضية على شبكة Wi-Fi</string>
<string name="revanced_shorts_quality_default_mobile_title">جودة Shorts الافتراضية على شبكة الجوال</string>
<string name="revanced_remember_video_quality_mobile">الجوّال</string> <string name="revanced_remember_video_quality_mobile">الجوّال</string>
<string name="revanced_remember_video_quality_wifi">Wi-Fi</string> <string name="revanced_remember_video_quality_wifi">Wi-Fi</string>
<string name="revanced_remember_video_quality_toast">تم تغيير جودة %1$s الافتراضية إلى: %2$s</string> <string name="revanced_remember_video_quality_toast">تم تغيير جودة %1$s الافتراضية إلى: %2$s</string>
<string name="revanced_remember_video_quality_toast_shorts">تم تغيير جودة Shorts %1$s إلى: %2$s</string>
</patch> </patch>
<patch id="video.speed.button.playbackSpeedButtonPatch"> <patch id="video.speed.button.playbackSpeedButtonPatch">
<string name="revanced_playback_speed_dialog_button_title">عرض زر مربع حوار السرعة</string> <string name="revanced_playback_speed_dialog_button_title">عرض زر مربع حوار السرعة</string>
@@ -1398,10 +1404,10 @@ Second \"item\" text"</string>
<string name="revanced_disable_hdr_video_summary_on">تم تعطيل فيديو HDR</string> <string name="revanced_disable_hdr_video_summary_on">تم تعطيل فيديو HDR</string>
<string name="revanced_disable_hdr_video_summary_off">تم تمكين فيديو HDR</string> <string name="revanced_disable_hdr_video_summary_off">تم تمكين فيديو HDR</string>
</patch> </patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch"> <patch id="video.quality.advancedVideoQualityMenuPatch">
<string name="revanced_restore_old_video_quality_menu_title">استعادة قائمة جودة الفيديو القديمة</string> <string name="revanced_advanced_video_quality_menu_title">إظهار قائمة جودة الفيديو المتقدمة</string>
<string name="revanced_restore_old_video_quality_menu_summary_on">يتم عرض قائمة جودة الفيديو القديمة</string> <string name="revanced_advanced_video_quality_menu_summary_on">يتم عرض قائمة جودة الفيديو المتقدمة</string>
<string name="revanced_restore_old_video_quality_menu_summary_off">لا يتم عرض قائمة جودة الفيديو القديمة</string> <string name="revanced_advanced_video_quality_menu_summary_off">لا يتم عرض قائمة جودة الفيديو المتقدمة</string>
</patch> </patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch"> <patch id="interaction.seekbar.enableSlideToSeekPatch">
<string name="revanced_slide_to_seek_title">تمكين التمرير للتقديم أو الترجيع</string> <string name="revanced_slide_to_seek_title">تمكين التمرير للتقديم أو الترجيع</string>

View File

@@ -130,7 +130,7 @@ Second \"item\" text"</string>
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. --> <!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. --> <!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch> </patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch"> <patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
</patch> </patch>
<patch id="layout.hide.time.hideTimestampPatch"> <patch id="layout.hide.time.hideTimestampPatch">
</patch> </patch>
@@ -177,8 +177,6 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="layout.theme.themePatch"> <patch id="layout.theme.themePatch">
</patch> </patch>
<patch id="layout.theme.themeResourcePatch">
</patch>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch"> <patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
</patch> </patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch"> <patch id="layout.thumbnails.alternativeThumbnailsPatch">
@@ -208,6 +206,7 @@ Second \"item\" text"</string>
<patch id="misc.zoomhaptics.zoomHapticsPatch"> <patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch> </patch>
<patch id="video.audio.forceOriginalAudioPatch"> <patch id="video.audio.forceOriginalAudioPatch">
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
</patch> </patch>
<patch id="video.quality.rememberVideoQualityPatch"> <patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto --> <!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -220,7 +219,7 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="video.hdr.disableHdrPatch"> <patch id="video.hdr.disableHdrPatch">
</patch> </patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch"> <patch id="video.quality.advancedVideoQualityMenuPatch">
</patch> </patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch"> <patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch> </patch>

View File

@@ -301,13 +301,13 @@ Gözlənilməz hallardan xəbərdar olmayacaqsınız."</string>
<string name="revanced_hide_description_components_screen_title">Video açıqlaması</string> <string name="revanced_hide_description_components_screen_title">Video açıqlaması</string>
<string name="revanced_hide_description_components_screen_summary">Video açıqlaması elementlərini gizlət və ya göstər</string> <string name="revanced_hide_description_components_screen_summary">Video açıqlaması elementlərini gizlət və ya göstər</string>
<string name="revanced_hide_filter_bar_screen_title">Filtr çubuğu</string> <string name="revanced_hide_filter_bar_screen_title">Filtr çubuğu</string>
<string name="revanced_hide_filter_bar_screen_summary">Axında, axtarışda və əlaqəli videolardakı filtr çubuğunu gizlət və ya göstər</string> <string name="revanced_hide_filter_bar_screen_summary">Axında, axtarış nəticələrində və əlaqəli videolarda filtr cərgəsin gizlət və ya göstər</string>
<string name="revanced_hide_filter_bar_feed_in_feed_title">Axında gizlət</string> <string name="revanced_hide_filter_bar_feed_in_feed_title">Axında gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_on">Axında gizlidir</string> <string name="revanced_hide_filter_bar_feed_in_feed_summary_on">Axında gizlidir</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_off">Axında göstərilir</string> <string name="revanced_hide_filter_bar_feed_in_feed_summary_off">Axında göstərilir</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">Axtarışda gizlət</string> <string name="revanced_hide_filter_bar_feed_in_search_title">Axtarış nəticələrində gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">Axtarışda gizlidir</string> <string name="revanced_hide_filter_bar_feed_in_search_summary_on">Axtarış nəticələrində gizlədilib</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">Axtarışda görünür</string> <string name="revanced_hide_filter_bar_feed_in_search_summary_off">Axtarış nəticələrində göstərilir</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_title">Əlaqəli videolarda gizlət</string> <string name="revanced_hide_filter_bar_feed_in_related_videos_title">Əlaqəli videolarda gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">Əlaqəli videolarda gizlidir</string> <string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">Əlaqəli videolarda gizlidir</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">Əlaqəli videolarda görünür</string> <string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">Əlaqəli videolarda görünür</string>
@@ -404,7 +404,6 @@ Bu xüsusiyyət yalnız köhnə cihazlar üçün mövcuddur"</string>
<string name="revanced_hide_self_sponsor_ads_title">Öz-sponsorlu kartları gizlət</string> <string name="revanced_hide_self_sponsor_ads_title">Öz-sponsorlu kartları gizlət</string>
<string name="revanced_hide_self_sponsor_ads_summary_on">Özünə sponsorluq edilən kartlar gizlidir</string> <string name="revanced_hide_self_sponsor_ads_summary_on">Özünə sponsorluq edilən kartlar gizlidir</string>
<string name="revanced_hide_self_sponsor_ads_summary_off">Özünə sponsorluq edilən kartlar göstərilir</string> <string name="revanced_hide_self_sponsor_ads_summary_off">Özünə sponsorluq edilən kartlar göstərilir</string>
<string name="revanced_hide_products_banner_title">Məhsullara baxma etiketin gizlət</string>
<string name="revanced_hide_products_banner_summary_on">Etiket gizlədilib</string> <string name="revanced_hide_products_banner_summary_on">Etiket gizlədilib</string>
<string name="revanced_hide_products_banner_summary_off">Etiket göstərilir</string> <string name="revanced_hide_products_banner_summary_off">Etiket göstərilir</string>
<string name="revanced_hide_end_screen_store_banner_title">Son ekran mağaza etiketini gizlət</string> <string name="revanced_hide_end_screen_store_banner_title">Son ekran mağaza etiketini gizlət</string>
@@ -663,7 +662,6 @@ Bu seçimi dəyişdirmə işə düşmürsə, Gizli rejimə keçməyə çalışı
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">Video keyfiyyət menyusu alt məlumatı göstərilir</string> <string name="revanced_hide_player_flyout_video_quality_footer_summary_off">Video keyfiyyət menyusu alt məlumatı göstərilir</string>
</patch> </patch>
<patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch"> <patch id="layout.buttons.overlay.hidePlayerOverlayButtonsPatch">
<string name="revanced_hide_player_previous_next_buttons_title">Əvvəlki/növbəti video düymələrin gizlət</string>
<string name="revanced_hide_player_previous_next_buttons_summary_on">Düymələr gizlidir</string> <string name="revanced_hide_player_previous_next_buttons_summary_on">Düymələr gizlidir</string>
<string name="revanced_hide_player_previous_next_buttons_summary_off">Düymələr göstərilir</string> <string name="revanced_hide_player_previous_next_buttons_summary_off">Düymələr göstərilir</string>
<string name="revanced_hide_cast_button_title">Yayımla düyməsini gizlət</string> <string name="revanced_hide_cast_button_title">Yayımla düyməsini gizlət</string>
@@ -808,10 +806,12 @@ Bu seçimi dəyişdirmə işə düşmürsə, Gizli rejimə keçməyə çalışı
<string name="revanced_hide_shorts_navigation_bar_summary_on">Fəaliyyət çubuğu gizlidir</string> <string name="revanced_hide_shorts_navigation_bar_summary_on">Fəaliyyət çubuğu gizlidir</string>
<string name="revanced_hide_shorts_navigation_bar_summary_off">Fəaliyyət çubuğu göstərilir</string> <string name="revanced_hide_shorts_navigation_bar_summary_off">Fəaliyyət çubuğu göstərilir</string>
</patch> </patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch"> <patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
<string name="revanced_disable_suggested_video_end_screen_title">Təklif edilən video bitiş ekranın qapadın</string> <string name="revanced_end_screen_suggested_video_title">Son ekran bildirilən videonu gizlət</string>
<string name="revanced_disable_suggested_video_end_screen_summary_on">Təklif olunan videolar qeyri-aktiv ediləcək</string> <string name="revanced_end_screen_suggested_video_summary_on">"Avtomatik oynatma qapadılanda son ekran bildirilən video gizlədilir
<string name="revanced_disable_suggested_video_end_screen_summary_off">Təklif olunan videolar göstəriləcək</string>
Avtomatik oynatma YouTube ayarlarında dəyişdirilə bilər: Ayarlar → Oxunuş → Növbəti videonu avtomatik oxudun"</string>
<string name="revanced_end_screen_suggested_video_summary_off">Son ekranda bildirilən video göstərilir</string>
</patch> </patch>
<patch id="layout.hide.time.hideTimestampPatch"> <patch id="layout.hide.time.hideTimestampPatch">
<string name="revanced_hide_timestamp_title">Video vaxt möhürünü gizlət</string> <string name="revanced_hide_timestamp_title">Video vaxt möhürünü gizlət</string>
@@ -1113,16 +1113,6 @@ Təqdim etməyə hazırdır?"</string>
<string name="revanced_change_form_factor_entry_2">Telefon</string> <string name="revanced_change_form_factor_entry_2">Telefon</string>
<string name="revanced_change_form_factor_entry_3">Planşet</string> <string name="revanced_change_form_factor_entry_3">Planşet</string>
<string name="revanced_change_form_factor_entry_4">Avtomobil</string> <string name="revanced_change_form_factor_entry_4">Avtomobil</string>
<string name="revanced_change_form_factor_user_dialog_message">"Dəyişikliklərə daxildir:
Planşet tərtibatı
• İcma elanları gizlidir
Avtomobil tərtibatı
• Baxış tarixçəsi seçimi gizlidir
• \"Kəşf et\" bölməsi qaytarılıb
• Shorts daimi oynadıcıda açılır
• Axın mövzulara və kanala görə hazırlanıb"</string>
</patch> </patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch"> <patch id="layout.spoofappversion.spoofAppVersionPatch">
<string name="revanced_spoof_app_version_title">Tətbiq versiyasını saxtalaşdır</string> <string name="revanced_spoof_app_version_title">Tətbiq versiyasını saxtalaşdır</string>
@@ -1192,7 +1182,6 @@ Sonradan qapadılarsa, UI səhvlərin önləmək üçün tətbiq məlumatların
</patch> </patch>
<patch id="layout.miniplayer.miniplayerPatch"> <patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">Kiçik oynadıcı</string> <string name="revanced_miniplayer_screen_title">Kiçik oynadıcı</string>
<string name="revanced_miniplayer_screen_summary">Tətbiqdə kiçildilən oynadıcı üslubunu dəyişdir</string>
<string name="revanced_miniplayer_type_title">Kiçik oynadıcı növü</string> <string name="revanced_miniplayer_type_title">Kiçik oynadıcı növü</string>
<string name="revanced_miniplayer_type_entry_0">Qeyri-aktivdir</string> <string name="revanced_miniplayer_type_entry_0">Qeyri-aktivdir</string>
<string name="revanced_miniplayer_type_entry_1">İlkin</string> <string name="revanced_miniplayer_type_entry_1">İlkin</string>
@@ -1245,8 +1234,6 @@ Genişləndirmək və ya bağlamaq üçün sürüşdür"</string>
<string name="revanced_gradient_loading_screen_title">Dəyişkən yükləmə ekranını aktivləşdir</string> <string name="revanced_gradient_loading_screen_title">Dəyişkən yükləmə ekranını aktivləşdir</string>
<string name="revanced_gradient_loading_screen_summary_on">Yükləmə ekranı, dəyişkən arxa plana malik olacaq</string> <string name="revanced_gradient_loading_screen_summary_on">Yükləmə ekranı, dəyişkən arxa plana malik olacaq</string>
<string name="revanced_gradient_loading_screen_summary_off">Yükləmə ekranı, vahid arxa plana malik olacaq</string> <string name="revanced_gradient_loading_screen_summary_off">Yükləmə ekranı, vahid arxa plana malik olacaq</string>
</patch>
<patch id="layout.theme.themeResourcePatch">
<string name="revanced_seekbar_custom_color_title">Fərdi irəliləmə cizgisi rəngini aktivləşdir</string> <string name="revanced_seekbar_custom_color_title">Fərdi irəliləmə cizgisi rəngini aktivləşdir</string>
<string name="revanced_seekbar_custom_color_summary_on">Fərdi irəliləmə cizgisi rəngi göstərilir</string> <string name="revanced_seekbar_custom_color_summary_on">Fərdi irəliləmə cizgisi rəngi göstərilir</string>
<string name="revanced_seekbar_custom_color_summary_off">Orijinal irəliləmə cizgisi rəngi göstərilir</string> <string name="revanced_seekbar_custom_color_summary_off">Orijinal irəliləmə cizgisi rəngi göstərilir</string>
@@ -1338,8 +1325,6 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
</patch> </patch>
<patch id="misc.links.openLinksExternallyPatch"> <patch id="misc.links.openLinksExternallyPatch">
<string name="revanced_external_browser_title">Bağlantıları brauzerdə aç</string> <string name="revanced_external_browser_title">Bağlantıları brauzerdə aç</string>
<string name="revanced_external_browser_summary_on">Bağlantılar xarici yolla açılır</string>
<string name="revanced_external_browser_summary_off">Bağlantılar tətbiqdə açılır</string>
</patch> </patch>
<patch id="misc.privacy.removeTrackingQueryParameterPatch"> <patch id="misc.privacy.removeTrackingQueryParameterPatch">
<string name="revanced_remove_tracking_query_parameter_title">İzləmə sorğusu faktorun sil</string> <string name="revanced_remove_tracking_query_parameter_title">İzləmə sorğusu faktorun sil</string>
@@ -1352,10 +1337,11 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
<string name="revanced_disable_zoom_haptics_summary_off">Reaksiya aktivdir</string> <string name="revanced_disable_zoom_haptics_summary_off">Reaksiya aktivdir</string>
</patch> </patch>
<patch id="video.audio.forceOriginalAudioPatch"> <patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">Orijinal səsi tələb et</string> <string name="revanced_force_original_audio_title">Orijinal səs dilini zorla</string>
<string name="revanced_force_original_audio_summary_on">Orijinal səs istifadəsi</string> <string name="revanced_force_original_audio_summary_on">Orijinal səs dilini istifadə</string>
<string name="revanced_force_original_audio_summary_off">İlkin səs istifadəsi</string> <string name="revanced_force_original_audio_summary_off">İlkin səs istifadəsi</string>
<string name="revanced_force_original_audio_not_available">Bu xüsusiyyəti işlətmək üçün yayım saxtalaşdırmanı iOS ötürücü növünə dəyiş</string> <!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
<string name="revanced_force_original_audio_not_available">Bu xüsusiyyəti istifadə etmək üçün \"Saxta video yayımların\" iOS TV-yə dəyiş</string>
</patch> </patch>
<patch id="video.quality.rememberVideoQualityPatch"> <patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto --> <!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -1398,10 +1384,7 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
<string name="revanced_disable_hdr_video_summary_on">HDR video qapalıdır</string> <string name="revanced_disable_hdr_video_summary_on">HDR video qapalıdır</string>
<string name="revanced_disable_hdr_video_summary_off">HDR video aktivdir</string> <string name="revanced_disable_hdr_video_summary_off">HDR video aktivdir</string>
</patch> </patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch"> <patch id="video.quality.advancedVideoQualityMenuPatch">
<string name="revanced_restore_old_video_quality_menu_title">Köhnə video keyfiyyət menusun qaytar</string>
<string name="revanced_restore_old_video_quality_menu_summary_on">Köhnə video keyfiyyət siyahısı göstərilir</string>
<string name="revanced_restore_old_video_quality_menu_summary_off">Köhnə video keyfiyyət siyahısı görünmür</string>
</patch> </patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch"> <patch id="interaction.seekbar.enableSlideToSeekPatch">
<string name="revanced_slide_to_seek_title">Axtarmaq üçün sürüşdürməni aktiv et</string> <string name="revanced_slide_to_seek_title">Axtarmaq üçün sürüşdürməni aktiv et</string>

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