Compare commits

..

43 Commits

Author SHA1 Message Date
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
semantic-release-bot
79f1dfd3e8 chore: Release v5.13.0-dev.13 [skip ci]
# [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)

### Bug Fixes

* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([f5dd902](f5dd902915))
2025-02-24 18:37:58 +00:00
MarcaD
f5dd902915 fix(YouTube): Resolve button flickering when taping seekbar (#4500) 2025-02-24 20:34:28 +02:00
semantic-release-bot
10e2b08eb2 chore: Release v5.13.0-dev.12 [skip ci]
# [5.13.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.11...v5.13.0-dev.12) (2025-02-24)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([4ae1155](4ae1155e51))
2025-02-24 10:01:52 +00:00
LisoUseInAIKyrios
4ae1155e51 fix(YouTube - Return YouTube Dislike): Use correct number formatting if using a different ReVanced language 2025-02-24 11:58:57 +02:00
github-actions[bot]
69fbfaea19 chore: Sync translations (#4499)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-24 11:56:46 +02:00
semantic-release-bot
f44fede67c chore: Release v5.13.0-dev.11 [skip ci]
# [5.13.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.10...v5.13.0-dev.11) (2025-02-23)

### Bug Fixes

* **TikTok:** Resolve startup app crash ([3c52ab8](3c52ab8017))
2025-02-23 07:31:27 +00:00
LisoUseInAIKyrios
3c52ab8017 fix(TikTok): Resolve startup app crash 2025-02-23 09:28:37 +02:00
github-actions[bot]
d1641a6e3d chore: Sync translations (#4495) 2025-02-23 09:26:22 +02:00
semantic-release-bot
09773e8934 chore: Release v5.13.0-dev.10 [skip ci]
# [5.13.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.9...v5.13.0-dev.10) (2025-02-22)

### Bug Fixes

* **YouTube - Copy video URL:** Use correct button ordering ([d77d5bf](d77d5bfbdd))
2025-02-22 16:31:11 +00:00
LisoUseInAIKyrios
d77d5bfbdd fix(YouTube - Copy video URL): Use correct button ordering
Fixes refactoring oversight of button fade fix
2025-02-22 18:28:47 +02:00
semantic-release-bot
a84bded9e7 chore: Release v5.13.0-dev.9 [skip ci]
# [5.13.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.8...v5.13.0-dev.9) (2025-02-22)

### Bug Fixes

* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([e664a24](e664a24f73))
2025-02-22 15:48:25 +00:00
LisoUseInAIKyrios
e664a24f73 fix(YouTube): Do not hide player controls when using double tap to skip forward (#4487)
Co-authored-by: MarcaDian <tolan.sheremeev@gmail.com>
2025-02-22 17:44:53 +02:00
semantic-release-bot
5bf964fff6 chore: Release v5.13.0-dev.8 [skip ci]
# [5.13.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.7...v5.13.0-dev.8) (2025-02-22)

### Bug Fixes

* **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))
2025-02-22 09:43:04 +00:00
LisoUseInAIKyrios
0c0bbb8713 fix(YouTube - Spoof app version): Force old settings menus if spoofing to older app targets (#4490) 2025-02-22 11:40:06 +02:00
github-actions[bot]
8afe48cd92 chore: Sync translations (#4492) 2025-02-22 11:39:54 +02:00
github-actions[bot]
dde8ea31cb chore: Sync translations (#4491) 2025-02-22 11:34:59 +02:00
172 changed files with 4567 additions and 2002 deletions

View File

@@ -1,3 +1,131 @@
# [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)
### Bug Fixes
* **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))
# [5.13.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.11...v5.13.0-dev.12) (2025-02-24)
### Bug Fixes
* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([edf66f4](https://github.com/ReVanced/revanced-patches/commit/edf66f4e16d46156cb8b8e31d18cb8dbcb87737e))
# [5.13.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.10...v5.13.0-dev.11) (2025-02-23)
### Bug Fixes
* **TikTok:** Resolve startup app crash ([18c0fc2](https://github.com/ReVanced/revanced-patches/commit/18c0fc2a7f186f50a904fd25dbaa739abdd24993))
# [5.13.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.9...v5.13.0-dev.10) (2025-02-22)
### Bug Fixes
* **YouTube - Copy video URL:** Use correct button ordering ([5e622cc](https://github.com/ReVanced/revanced-patches/commit/5e622ccf66d34af31c6026fa7f4d332460c6ecb0))
# [5.13.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.8...v5.13.0-dev.9) (2025-02-22)
### Bug Fixes
* **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))
# [5.13.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.7...v5.13.0-dev.8) (2025-02-22)
### Bug Fixes
* **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))
# [5.13.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.6...v5.13.0-dev.7) (2025-02-22)

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

@@ -356,33 +356,24 @@ public class Utils {
public static Context getContext() {
if (context == null) {
Logger.initializationException(Utils.class, "Context is null, returning null!", null);
Logger.initializationException(Utils.class, "Context is not set by extension hook, returning null", null);
}
return context;
}
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;
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language != AppLanguage.DEFAULT) {
// Create a new context with the desired language.
Logger.printDebug(() -> "Using app language: " + language);
Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale());
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) {

View File

@@ -158,16 +158,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
/**
* 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 applySettingToPreference) {
// 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
// the Preferences.
for (int i = 0, prefCount = screen.getPreferenceCount(); i < prefCount; i++) {
Preference pref = screen.getPreference(i);
if (pref instanceof PreferenceScreen) {
updatePreferenceScreen((PreferenceScreen) pref, syncSettingValue, applySettingToPreference);
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
Preference pref = group.getPreference(i);
if (pref instanceof PreferenceGroup subGroup) {
updatePreferenceScreen(subGroup, syncSettingValue, applySettingToPreference);
} else if (pref.hasKey()) {
String key = pref.getKey();
Setting<?> setting = Setting.getSettingFromPath(key);

View File

@@ -0,0 +1,34 @@
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 empty, zero-height view to eliminate spacing
return new View(getContext());
}
}

View File

@@ -107,6 +107,21 @@ public class SpoofVideoStreamsPatch {
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.
*/

View File

@@ -1,37 +1,60 @@
package app.revanced.extension.tiktok.spoof.sim;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.tiktok.settings.Settings;
@SuppressWarnings("unused")
public class SpoofSimPatch {
private static final boolean ENABLED = Settings.SIM_SPOOF.get();
/**
* During app startup native code can be called with no obvious way to set the context.
* Cannot check if sim spoofing is enabled or the app will crash since no context is set.
*/
private static boolean isContextNotSet(String fieldSpoofed) {
if (Utils.getContext() != null) {
return false;
}
Logger.initializationException(SpoofSimPatch.class,
"Context is not yet set, cannot spoof: " + fieldSpoofed, null);
return true;
}
public static String getCountryIso(String value) {
if (ENABLED) {
if (isContextNotSet("countryIso")) return value;
if (Settings.SIM_SPOOF.get()) {
String iso = Settings.SIM_SPOOF_ISO.get();
Logger.printDebug(() -> "Spoofing sim ISO from: " + value + " to: " + iso);
Logger.printDebug(() -> "Spoofing countryIso from: " + value + " to: " + iso);
return iso;
}
return value;
}
public static String getOperator(String value) {
if (ENABLED) {
if (isContextNotSet("MCC-MNC")) return value;
if (Settings.SIM_SPOOF.get()) {
String mcc_mnc = Settings.SIMSPOOF_MCCMNC.get();
Logger.printDebug(() -> "Spoofing sim MCC-MNC from: " + value + " to: " + mcc_mnc);
return mcc_mnc;
}
return value;
}
public static String getOperatorName(String value) {
if (ENABLED) {
if (isContextNotSet("operatorName")) return value;
if (Settings.SIM_SPOOF.get()) {
String operator = Settings.SIMSPOOF_OP_NAME.get();
Logger.printDebug(() -> "Spoofing sim operator from: " + value + " to: " + operator);
Logger.printDebug(() -> "Spoofing sim operatorName from: " + value + " to: " + operator);
return operator;
}
return value;
}
}

View File

@@ -1,7 +1,7 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused")
public class BackgroundPlaybackPatch {
@@ -23,16 +23,7 @@ public class BackgroundPlaybackPatch {
// 7. Close the Short
// 8. Resume playing the regular video
// 9. Minimize the app (PIP should appear)
if (!VideoInformation.lastVideoIdIsShort()) {
return true; // Definitely is not a Short.
}
// 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();
return !ShortsPlayerState.isOpen();
}
/**

View File

@@ -1,9 +1,17 @@
package app.revanced.extension.youtube.patches;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.view.View;
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.shared.NavigationBar;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class ChangeFormFactorPatch {
@@ -41,14 +49,57 @@ public class ChangeFormFactorPatch {
@Nullable
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.
*/
public static int getFormFactor(int original) {
return FORM_FACTOR_TYPE == null
? original
: FORM_FACTOR_TYPE;
if (FORM_FACTOR_TYPE == null) return original;
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;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused")
public class DisableAutoCaptionsPatch {
@@ -14,7 +14,7 @@ public class DisableAutoCaptionsPatch {
public static boolean autoCaptionsEnabled() {
return Settings.AUTO_CAPTIONS.get()
// 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.
*/
public static boolean isBooleanFeatureFlagEnabled(boolean value, long flag) {
public static boolean isBooleanFeatureFlagEnabled(boolean value, Long flag) {
if (LOG_FEATURE_FLAGS && value) {
if (featureFlags.putIfAbsent(flag, true) == null) {
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;
import android.view.View;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
import app.revanced.extension.youtube.shared.VideoState;
@SuppressWarnings("unused")
@@ -24,4 +27,26 @@ public class PlayerTypeHookPatch {
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

@@ -98,6 +98,11 @@ public final class LayoutComponentsFilter extends Filter {
"compact_banner"
);
final var subscriptionsChipBar = new StringFilterGroup(
Settings.HIDE_FILTER_BAR_FEED_IN_FEED,
"subscriptions_chip_bar"
);
inFeedSurvey = new StringFilterGroup(
Settings.HIDE_FEED_SURVEY,
"in_feed_survey",
@@ -264,6 +269,7 @@ public final class LayoutComponentsFilter extends Filter {
singleItemInformationPanel,
emergencyBox,
subscribersCommunityGuidelines,
subscriptionsChipBar,
channelGuidelines,
audioTrackButton,
artistCard,

View File

@@ -12,15 +12,19 @@ import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.IntegerSetting;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused")
public class RememberVideoQualityPatch {
private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
private static final IntegerSetting wifiQualitySetting = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting mobileQualitySetting = Settings.VIDEO_QUALITY_DEFAULT_MOBILE;
private static final IntegerSetting videoQualityWifi = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
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;
@@ -41,17 +45,29 @@ public class RememberVideoQualityPatch {
@Nullable
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) {
String networkTypeMessage;
boolean useShortsPreference = ShortsPlayerState.isOpen();
if (Utils.getNetworkType() == NetworkType.MOBILE) {
mobileQualitySetting.save(defaultQuality);
if (useShortsPreference) shortsQualityMobile.save(defaultQuality);
else videoQualityMobile.save(defaultQuality);
networkTypeMessage = str("revanced_remember_video_quality_mobile");
} else {
wifiQualitySetting.save(defaultQuality);
if (useShortsPreference) shortsQualityWifi.save(defaultQuality);
else videoQualityWifi.save(defaultQuality);
networkTypeMessage = str("revanced_remember_video_quality_wifi");
}
Utils.showToastShort(
str("revanced_remember_video_quality_toast", networkTypeMessage, (defaultQuality + "p")));
Utils.showToastShort(str(
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) {
try {
boolean useShortsPreference = ShortsPlayerState.isOpen();
final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE
? mobileQualitySetting.get()
: wifiQualitySetting.get();
? (useShortsPreference ? shortsQualityMobile : videoQualityMobile).get()
: (useShortsPreference ? shortsQualityWifi : videoQualityWifi).get();
if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
return originalQualityIndex; // Nothing to do.
@@ -141,17 +158,17 @@ public class RememberVideoQualityPatch {
* Injection point. Old quality menu.
*/
public static void userChangedQuality(int selectedQualityIndex) {
if (!Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED.get()) return;
userSelectedQualityIndex = selectedQualityIndex;
userChangedDefaultQuality = true;
if (shouldRememberVideoQuality()) {
userSelectedQualityIndex = selectedQualityIndex;
userChangedDefaultQuality = true;
}
}
/**
* Injection point. New quality menu.
*/
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).
}

View File

@@ -352,13 +352,16 @@ public class ReturnYouTubeDislike {
}
private static String formatDislikeCount(long dislikeCount) {
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe.
if (dislikeCountFormatter == null) {
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
// Must use default locale and not Utils context locale,
// otherwise if using a different settings language then the
// formatting will use that of the different language.
Locale locale = Locale.getDefault();
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
// YouTube disregards locale specific number characters
// and instead shows english number characters everywhere.
// and instead shows English number characters everywhere.
// To use the same behavior, override the digit characters to use English
// so languages such as Arabic will show "1.234" instead of the native "Ûą,Û˛ÛŗŲ¤"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -375,15 +378,15 @@ public class ReturnYouTubeDislike {
private static String formatDislikePercentage(float dislikePercentage) {
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe, must synchronize.
if (dislikePercentageFormatter == null) {
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
Locale locale = Locale.getDefault();
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
// Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& dislikePercentageFormatter instanceof DecimalFormat) {
&& dislikePercentageFormatter instanceof DecimalFormat decimalFormatter) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
((DecimalFormat) dislikePercentageFormatter).setDecimalFormatSymbols(symbols);
decimalFormatter.setDecimalFormatSymbols(symbols);
}
}

View File

@@ -19,6 +19,7 @@ import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
@@ -63,6 +64,10 @@ public class LicenseActivityHook {
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
return false;
}
// Spoofing can cause half broken settings menus of old and new settings.
if (SpoofAppVersionPatch.isSpoofingToLessThan("19.35.36")) {
return false;
}
// On the first launch of a clean install, forcing the cairo menu can give a
// half broken appearance because all the preference icons may not be available yet.
@@ -121,8 +126,7 @@ public class LicenseActivityHook {
// This is required to fix submenu title alignment issue with Android ASOP 15+
ViewGroup toolBarParent = activity.findViewById(
getResourceIdentifier("revanced_toolbar_parent", "id"));
ViewGroup dummyToolbar = toolBarParent.findViewById(getResourceIdentifier(
"revanced_toolbar", "id"));
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
toolbarLayoutParams = dummyToolbar.getLayoutParams();
toolBarParent.removeView(dummyToolbar);

View File

@@ -48,10 +48,13 @@ import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
public class Settings extends BaseSettings {
// 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_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 RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
// Speed
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);
@@ -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 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_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 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);
@@ -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_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_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_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
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_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_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_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_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
// Description
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);
@@ -284,7 +287,6 @@ public class Settings extends BaseSettings {
"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 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_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));
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
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,
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));
@@ -384,9 +385,12 @@ public class Settings extends BaseSettings {
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
// 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_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);
static {
// region Migration
@@ -405,11 +409,7 @@ public class Settings extends BaseSettings {
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
// Old spoof versions that no longer work reliably.
if (SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0) {
Logger.printInfo(() -> "Resetting spoof app version target");
SPOOF_APP_VERSION_TARGET.resetToDefault();
}
migrateOldSettingToNew(DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN, HIDE_END_SCREEN_SUGGESTED_VIDEO);
// Migrate renamed enum.
//noinspection deprecation

View File

@@ -3,7 +3,9 @@ package app.revanced.extension.youtube.shared;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
@@ -24,12 +26,22 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
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<AppCompatToolbarPatchInterface> toolbarResultsRef
= new WeakReference<>(null);
/**
* Injection point.
*/
@@ -37,6 +49,22 @@ public final class NavigationBar {
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
* 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;
}
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
/**
* Main player type.
* Regular player type.
*/
enum class PlayerType {
/**
@@ -21,9 +21,6 @@ enum class PlayerType {
/**
* A regular video is minimized.
*
* When spoofing to 16.x YouTube and watching a short with a regular video in the background,
* the type can be this (and not [HIDDEN]).
*/
WATCH_WHILE_MINIMIZED,
WATCH_WHILE_MAXIMIZED,
@@ -56,8 +53,7 @@ enum class PlayerType {
val newType = nameToPlayerType[enumName]
if (newType == null) {
Logger.printException { "Unknown PlayerType encountered: $enumName" }
} else if (current != newType) {
Logger.printDebug { "PlayerType changed to: $newType" }
} else {
current = newType
}
}
@@ -68,9 +64,13 @@ enum class PlayerType {
@JvmStatic
var current
get() = currentPlayerType
private set(value) {
currentPlayerType = value
onChange(currentPlayerType)
private set(type) {
if (currentPlayerType != type) {
Logger.printDebug { "Changed to: $type" }
currentPlayerType = type
onChange(type)
}
}
@Volatile // Read/write from different threads.
@@ -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,
* 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].
*
* @see VideoInformation
*/
fun isNoneOrHidden(): Boolean {
return this == NONE || this == HIDDEN
@@ -107,8 +105,11 @@ enum class PlayerType {
* when spoofing to an old version this will return false even
* 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.
* @see VideoInformation
* @see ShortsPlayerState
*/
fun isNoneHiddenOrSlidingMinimized(): Boolean {
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,
* 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,
* a regular video is minimized (and a new video is not being opened).
* @see VideoInformation
* @see ShortsPlayerState
*/
fun isNoneHiddenOrMinimized(): Boolean {
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

@@ -0,0 +1,56 @@
package app.revanced.extension.youtube.sponsorblock.ui;
import android.view.View;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
public class CreateSegmentButton {
@Nullable
private static PlayerControlButton instance;
public static void hideControls() {
if (instance != null) instance.hide();
}
/**
* injection point
*/
public static void initialize(View controlsView) {
try {
instance = new PlayerControlButton(
controlsView,
"revanced_sb_create_segment_button",
null,
CreateSegmentButton::shouldBeShown,
v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility(),
null
);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
}
/**
* Injection point
*/
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* Injection point
*/
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated);
}
private static boolean shouldBeShown() {
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get()
&& !VideoInformation.isAtEndOfVideo();
}
}

View File

@@ -1,60 +0,0 @@
package app.revanced.extension.youtube.sponsorblock.ui;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.videoplayer.PlayerControlTopButton;
public class CreateSegmentButtonController extends PlayerControlTopButton {
@Nullable
private static CreateSegmentButtonController instance;
public static void hideControls() {
if (instance != null) instance.hide();
}
/**
* injection point
*/
public static void initialize(View youtubeControlsLayout) {
try {
Logger.printDebug(() -> "initializing new segment button");
ImageView imageView = Objects.requireNonNull(Utils.getChildViewByResourceName(
youtubeControlsLayout, "revanced_sb_create_segment_button"));
instance = new CreateSegmentButtonController(imageView);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
}
/**
* injection point
*/
public static void changeVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* injection point
*/
public static void changeVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated);
}
private CreateSegmentButtonController(ImageView imageView) {
super(imageView, v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility());
}
protected boolean shouldBeShown() {
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get()
&& !VideoInformation.isAtEndOfVideo();
}
}

View File

@@ -19,7 +19,6 @@ import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
import app.revanced.extension.youtube.videoplayer.PlayerControlTopButton;
import kotlin.Unit;
public class SponsorBlockViewController {
@@ -239,8 +238,8 @@ public class SponsorBlockViewController {
// but if buttons are showing when the end of the video is reached then they need
// to be forcefully hidden
if (!Settings.AUTO_REPEAT.get()) {
CreateSegmentButtonController.hideControls();
VotingButtonController.hideControls();
CreateSegmentButton.hideControls();
VotingButton.hideControls();
}
} catch (Exception ex) {
Logger.printException(() -> "endOfVideoReached failure", ex);

View File

@@ -1,23 +1,19 @@
package app.revanced.extension.youtube.sponsorblock.ui;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
import app.revanced.extension.youtube.videoplayer.PlayerControlTopButton;
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
public class VotingButtonController extends PlayerControlTopButton {
public class VotingButton {
@Nullable
private static VotingButtonController instance;
private static PlayerControlButton instance;
public static void hideControls() {
if (instance != null) instance.hide();
@@ -26,36 +22,36 @@ public class VotingButtonController extends PlayerControlTopButton {
/**
* injection point
*/
public static void initialize(View youtubeControlsLayout) {
public static void initialize(View controlsView) {
try {
Logger.printDebug(() -> "initializing voting button");
ImageView imageView = Objects.requireNonNull(Utils.getChildViewByResourceName(
youtubeControlsLayout, "revanced_sb_voting_button"));
instance = new VotingButtonController(imageView);
instance = new PlayerControlButton(
controlsView,
"revanced_sb_voting_button",
null,
VotingButton::shouldBeShown,
v -> SponsorBlockUtils.onVotingClicked(v.getContext()),
null
);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
}
/**
* injection point
* Injection point
*/
public static void changeVisibilityImmediate(boolean visible) {
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* injection point
* Injection point
*/
public static void changeVisibility(boolean visible, boolean animated) {
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated);
}
private VotingButtonController(ImageView imageView) {
super(imageView, v -> SponsorBlockUtils.onVotingClicked(v.getContext()));
}
protected boolean shouldBeShown() {
private static boolean shouldBeShown() {
return Settings.SB_ENABLED.get() && Settings.SB_VOTING_BUTTON.get()
&& SegmentPlaybackController.videoHasSegments() && !VideoInformation.isAtEndOfVideo();
}

View File

@@ -18,7 +18,7 @@ import kotlin.math.min
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(
context: Context,
@@ -69,7 +69,7 @@ class SwipeControlsOverlayLayout(
}
addView(circularProgressView)
// Initialize rectangular progress bar
// Initialize horizontal progress bar
val screenWidth = resources.displayMetrics.widthPixels
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
horizontalProgressView = HorizontalProgressView(
@@ -152,10 +152,7 @@ class SwipeControlsOverlayLayout(
}
/**
* Abstract base class for progress views to reduce code duplication.
*/
/**
* Abstract base class for progress views to reduce code duplication.
* Abstract base class for progress views.
*/
abstract class AbstractProgressView(
context: Context,
@@ -183,10 +180,9 @@ abstract class AbstractProgressView(
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor
textAlign = Paint.Align.CENTER
textSize = 30f // Can adjust based on need
textSize = 40f // Can adjust based on need
}
protected var progress = 0
protected var maxProgress = 100
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(
context: Context,
@@ -235,7 +231,7 @@ class CircularProgressView(
private val rectF = RectF()
init {
textPaint.textSize = 40f // Override default text size for horizontal view
textPaint.textSize = 40f // Override default text size for circular view
progressPaint.strokeWidth = 20f
fillBackgroundPaint.strokeWidth = 20f
progressPaint.strokeCap = Paint.Cap.ROUND
@@ -266,7 +262,7 @@ class CircularProgressView(
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) {
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
}
@@ -300,7 +296,7 @@ class HorizontalProgressView(
private val padding = 40f
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.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL

View File

@@ -1,38 +1,35 @@
package app.revanced.extension.youtube.videoplayer;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class CopyVideoUrlButton extends PlayerControlBottomButton {
public class CopyVideoUrlButton {
@Nullable
private static CopyVideoUrlButton instance;
public CopyVideoUrlButton(ViewGroup viewGroup) {
super(
viewGroup,
"revanced_copy_video_url_button",
Settings.COPY_VIDEO_URL,
view -> CopyVideoUrlPatch.copyUrl(false),
view -> {
CopyVideoUrlPatch.copyUrl(true);
return true;
}
);
}
private static PlayerControlButton instance;
/**
* Injection point.
*/
public static void initializeButton(View view) {
public static void initializeButton(View controlsView) {
try {
instance = new CopyVideoUrlButton((ViewGroup) view);
instance = new PlayerControlButton(
controlsView,
"revanced_copy_video_url_button",
"revanced_copy_video_url_button_placeholder",
Settings.COPY_VIDEO_URL::get,
view -> CopyVideoUrlPatch.copyUrl(false),
view -> {
CopyVideoUrlPatch.copyUrl(true);
return true;
}
);
} catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex);
}
@@ -41,14 +38,14 @@ public class CopyVideoUrlButton extends PlayerControlBottomButton {
/**
* injection point
*/
public static void changeVisibilityImmediate(boolean visible) {
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* injection point
*/
public static void changeVisibility(boolean visible, boolean animated) {
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated);
}
}

View File

@@ -1,38 +1,35 @@
package app.revanced.extension.youtube.videoplayer;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class CopyVideoUrlTimestampButton extends PlayerControlBottomButton {
public class CopyVideoUrlTimestampButton {
@Nullable
private static CopyVideoUrlTimestampButton instance;
public CopyVideoUrlTimestampButton(ViewGroup bottomControlsViewGroup) {
super(
bottomControlsViewGroup,
"revanced_copy_video_url_timestamp_button",
Settings.COPY_VIDEO_URL_TIMESTAMP,
view -> CopyVideoUrlPatch.copyUrl(true),
view -> {
CopyVideoUrlPatch.copyUrl(false);
return true;
}
);
}
private static PlayerControlButton instance;
/**
* Injection point.
*/
public static void initializeButton(View bottomControlsViewGroup) {
public static void initializeButton(View controlsView) {
try {
instance = new CopyVideoUrlTimestampButton((ViewGroup) bottomControlsViewGroup);
instance = new PlayerControlButton(
controlsView,
"revanced_copy_video_url_timestamp_button",
"revanced_copy_video_url_timestamp_button_placeholder",
Settings.COPY_VIDEO_URL_TIMESTAMP::get,
view -> CopyVideoUrlPatch.copyUrl(true),
view -> {
CopyVideoUrlPatch.copyUrl(false);
return true;
}
);
} catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex);
}
@@ -41,14 +38,14 @@ public class CopyVideoUrlTimestampButton extends PlayerControlBottomButton {
/**
* injection point
*/
public static void changeVisibilityImmediate(boolean visible) {
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* injection point
*/
public static void changeVisibility(boolean visible, boolean animated) {
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated);
}
}

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.youtube.videoplayer;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
@@ -11,26 +10,23 @@ import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class ExternalDownloadButton extends PlayerControlBottomButton {
public class ExternalDownloadButton {
@Nullable
private static ExternalDownloadButton instance;
public ExternalDownloadButton(ViewGroup viewGroup) {
super(
viewGroup,
"revanced_external_download_button",
Settings.EXTERNAL_DOWNLOADER,
ExternalDownloadButton::onDownloadClick,
null
);
}
private static PlayerControlButton instance;
/**
* Injection point.
*/
public static void initializeButton(View view) {
public static void initializeButton(View controlsView) {
try {
instance = new ExternalDownloadButton((ViewGroup) view);
instance = new PlayerControlButton(
controlsView,
"revanced_external_download_button",
"revanced_external_download_button_placeholder",
Settings.EXTERNAL_DOWNLOADER::get,
ExternalDownloadButton::onDownloadClick,
null
);
} catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex);
}
@@ -39,14 +35,14 @@ public class ExternalDownloadButton extends PlayerControlBottomButton {
/**
* injection point
*/
public static void changeVisibilityImmediate(boolean visible) {
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* injection point
* Injection point
*/
public static void changeVisibility(boolean visible, boolean animated) {
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated);
}

View File

@@ -1,35 +1,31 @@
package app.revanced.extension.youtube.videoplayer;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class PlaybackSpeedDialogButton extends PlayerControlBottomButton {
public class PlaybackSpeedDialogButton {
@Nullable
private static PlaybackSpeedDialogButton instance;
public PlaybackSpeedDialogButton(ViewGroup viewGroup) {
super(
viewGroup,
"revanced_playback_speed_dialog_button",
Settings.PLAYBACK_SPEED_DIALOG_BUTTON,
view -> CustomPlaybackSpeedPatch.showOldPlaybackSpeedMenu(),
null
);
}
private static PlayerControlButton instance;
/**
* Injection point.
*/
public static void initializeButton(View view) {
public static void initializeButton(View controlsView) {
try {
instance = new PlaybackSpeedDialogButton((ViewGroup) view);
instance = new PlayerControlButton(
controlsView,
"revanced_playback_speed_dialog_button",
"revanced_playback_speed_dialog_button_placeholder",
Settings.PLAYBACK_SPEED_DIALOG_BUTTON::get,
view -> CustomPlaybackSpeedPatch.showOldPlaybackSpeedMenu(),
null
);
} catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex);
}
@@ -38,14 +34,14 @@ public class PlaybackSpeedDialogButton extends PlayerControlBottomButton {
/**
* injection point
*/
public static void changeVisibilityImmediate(boolean visible) {
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* injection point
*/
public static void changeVisibility(boolean visible, boolean animated) {
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated);
}
}

View File

@@ -1,103 +0,0 @@
package app.revanced.extension.youtube.videoplayer;
import static app.revanced.extension.youtube.videoplayer.PlayerControlTopButton.fadeOutDuration;
import android.transition.Fade;
import android.transition.TransitionManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BooleanSetting;
public abstract class PlayerControlBottomButton {
private final WeakReference<ImageView> buttonRef;
private final BooleanSetting setting;
private boolean isVisible;
protected PlayerControlBottomButton(ViewGroup bottomControlsViewGroup, String imageViewButtonId,
BooleanSetting booleanSetting, View.OnClickListener onClickListener,
@Nullable View.OnLongClickListener longClickListener) {
Logger.printDebug(() -> "Initializing button: " + imageViewButtonId);
ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById(
Utils.getResourceIdentifier(imageViewButtonId, "id")
));
imageView.setVisibility(View.GONE);
imageView.setOnClickListener(onClickListener);
if (longClickListener != null) {
imageView.setOnLongClickListener(longClickListener);
}
setting = booleanSetting;
buttonRef = new WeakReference<>(imageView);
}
protected void setVisibilityImmediate(boolean visible) {
private_setVisibility(visible, false);
}
protected void setVisibility(boolean visible, boolean animated) {
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
if (visible && !animated) return;
private_setVisibility(visible, animated);
}
private void private_setVisibility(boolean visible, boolean animated) {
try {
// If the visibility state hasn't changed, return early.
if (isVisible == visible) return;
isVisible = visible;
ImageView iView = buttonRef.get();
if (iView == null) {
return;
}
ViewGroup parent = (ViewGroup) iView.getParent();
if (parent == null) {
return;
}
// Apply transition if animation is enabled.
if (animated) {
Fade fade = visible
? PlayerControlTopButton.fadeInTransition
: PlayerControlTopButton.fadeOutTransition;
TransitionManager.beginDelayedTransition(parent, fade);
}
// If the view should be visible and the setting allows it.
if (visible && setting.get()) {
// Set the view to VISIBLE.
iView.setVisibility(View.VISIBLE);
} else if (iView.getVisibility() == View.VISIBLE) {
// First, set visibility to INVISIBLE for animation.
iView.setVisibility(View.INVISIBLE);
if (animated) {
// Set the view to GONE after the fade animation ends.
Utils.runOnMainThreadDelayed(() -> {
if (!isVisible) {
iView.setVisibility(View.GONE);
}
}, fadeOutDuration);
} else {
// If no animation, immediately set the view to GONE.
iView.setVisibility(View.GONE);
}
}
} catch (Exception ex) {
Logger.printException(() -> "private_setVisibility failure", ex);
}
}
}

View File

@@ -0,0 +1,190 @@
package app.revanced.extension.youtube.videoplayer;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.shared.PlayerType;
import kotlin.Unit;
public class PlayerControlButton {
public interface PlayerControlButtonVisibility {
/**
* @return If the button should be shown when the player overlay is visible.
*/
boolean shouldBeShown();
}
private static final int fadeInDuration;
private static final int fadeOutDuration;
private static final Animation fadeInAnimation;
private static final Animation fadeOutAnimation;
private static final Animation fadeOutImmediate;
static {
fadeInDuration = Utils.getResourceInteger("fade_duration_fast");
fadeOutDuration = Utils.getResourceInteger("fade_duration_scheduled");
fadeInAnimation = Utils.getResourceAnimation("fade_in");
fadeInAnimation.setDuration(fadeInDuration);
fadeOutAnimation = Utils.getResourceAnimation("fade_out");
fadeOutAnimation.setDuration(fadeOutDuration);
// Animation for the fast fade out after tapping the overlay.
// Currently not used but should be.
fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out");
fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast"));
}
private final WeakReference<View> buttonRef;
/**
* Empty view with the same layout size as the button. Used to fill empty space while the
* fade out animation runs. Without this the chapter titles overlapping the button when fading out.
*/
private final WeakReference<View> placeHolderRef;
private final PlayerControlButtonVisibility visibilityCheck;
private boolean isVisible;
public PlayerControlButton(View controlsViewGroup,
String imageViewButtonId,
@Nullable String placeholderId,
PlayerControlButtonVisibility buttonVisibility,
View.OnClickListener onClickListener,
@Nullable View.OnLongClickListener longClickListener) {
ImageView imageView = Utils.getChildViewByResourceName(controlsViewGroup, imageViewButtonId);
imageView.setVisibility(View.GONE);
View tempPlaceholder = null;
if (placeholderId != null) {
tempPlaceholder = Utils.getChildViewByResourceName(controlsViewGroup, placeholderId);
tempPlaceholder.setVisibility(View.GONE);
}
placeHolderRef = new WeakReference<>(tempPlaceholder);
imageView.setOnClickListener(onClickListener);
if (longClickListener != null) {
imageView.setOnLongClickListener(longClickListener);
}
visibilityCheck = buttonVisibility;
buttonRef = new WeakReference<>(imageView);
isVisible = false;
// Update the visibility after the player type changes.
// This ensures that button animations are cleared and their states are updated correctly
// when switching between states like minimized, maximized, or fullscreen, preventing
// "stuck" animations or incorrect visibility. Without this fix the issue is most noticable
// when maximizing type 3 miniplayer.
PlayerType.getOnChange().addObserver((PlayerType type) -> {
playerTypeChanged(type);
return Unit.INSTANCE;
});
}
public void setVisibilityImmediate(boolean visible) {
if (visible) {
// Fix button flickering, by pushing this call to the back of
// the main thread and letting other layout code run first.
Utils.runOnMainThread(() -> private_setVisibility(true, false));
} else {
private_setVisibility(false, false);
}
}
public void setVisibility(boolean visible, boolean animated) {
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
if (visible && !animated) return;
private_setVisibility(visible, animated);
}
private void private_setVisibility(boolean visible, boolean animated) {
try {
if (isVisible == visible) return;
isVisible = visible;
View button = buttonRef.get();
if (button == null) return;
View placeholder = placeHolderRef.get();
final boolean shouldBeShown = visibilityCheck.shouldBeShown();
if (visible && shouldBeShown) {
button.clearAnimation();
if (animated) {
button.startAnimation(PlayerControlButton.fadeInAnimation);
}
button.setVisibility(View.VISIBLE);
if (placeholder != null) {
placeholder.setVisibility(View.GONE);
}
} else {
if (button.getVisibility() == View.VISIBLE) {
button.clearAnimation();
if (animated) {
button.startAnimation(PlayerControlButton.fadeOutAnimation);
}
button.setVisibility(View.GONE);
}
if (placeholder != null) {
placeholder.setVisibility(shouldBeShown
? View.VISIBLE
: View.GONE);
}
}
} catch (Exception ex) {
Logger.printException(() -> "private_setVisibility failure", ex);
}
}
/**
* Synchronizes the button state after the player state changes.
*/
private void playerTypeChanged(PlayerType newType) {
if (newType != PlayerType.WATCH_WHILE_MINIMIZED && !newType.isMaximizedOrFullscreen()) {
return;
}
View button = buttonRef.get();
if (button == null) return;
button.clearAnimation();
View placeholder = placeHolderRef.get();
if (visibilityCheck.shouldBeShown()) {
if (isVisible) {
button.setVisibility(View.VISIBLE);
if (placeholder != null) placeholder.setVisibility(View.GONE);
} else {
button.setVisibility(View.GONE);
if (placeholder != null) placeholder.setVisibility(View.VISIBLE);
}
} else {
button.setVisibility(View.GONE);
if (placeholder != null) placeholder.setVisibility(View.GONE);
}
}
public void hide() {
if (!isVisible) return;
Utils.verifyOnMainThread();
View view = buttonRef.get();
if (view == null) return;
view.setVisibility(View.GONE);
view = placeHolderRef.get();
if (view != null) view.setVisibility(View.GONE);
isVisible = false;
}
}

View File

@@ -1,110 +0,0 @@
package app.revanced.extension.youtube.videoplayer;
import android.transition.Fade;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
// Ideally this should be refactored into PlayerControlBottomButton,
// but the show/hide logic is not the same so keeping this as two classes might be simpler.
public abstract class PlayerControlTopButton {
static final int fadeInDuration;
static final int fadeOutDuration;
private static final Animation fadeInAnimation;
private static final Animation fadeOutAnimation;
static final Fade fadeInTransition;
static final Fade fadeOutTransition;
private final WeakReference<ImageView> buttonReference;
private boolean isShowing;
static {
fadeInDuration = Utils.getResourceInteger("fade_duration_fast");
fadeOutDuration = Utils.getResourceInteger("fade_duration_scheduled");
fadeInAnimation = Utils.getResourceAnimation("fade_in");
fadeInAnimation.setDuration(fadeInDuration);
fadeOutAnimation = Utils.getResourceAnimation("fade_out");
fadeOutAnimation.setDuration(fadeOutDuration);
fadeInTransition = new Fade();
fadeInTransition.setDuration(fadeInDuration);
fadeOutTransition = new Fade();
fadeOutTransition.setDuration(fadeOutDuration);
}
protected PlayerControlTopButton(ImageView imageView, View.OnClickListener onClickListener) {
imageView.setVisibility(View.GONE);
imageView.setOnClickListener(onClickListener);
buttonReference = new WeakReference<>(imageView);
}
protected void setVisibilityImmediate(boolean visible) {
private_setVisibility(visible, false);
}
protected void setVisibility(boolean visible, boolean animated) {
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
if (visible && !animated) return;
private_setVisibility(visible, animated);
}
private void private_setVisibility(boolean visible, boolean animated) {
try {
if (isShowing == visible) return;
isShowing = visible;
ImageView iView = buttonReference.get();
if (iView == null) return;
if (visible) {
iView.clearAnimation();
if (!shouldBeShown()) {
return;
}
if (animated) {
iView.startAnimation(fadeInAnimation);
}
iView.setVisibility(View.VISIBLE);
return;
}
if (iView.getVisibility() == View.VISIBLE) {
iView.clearAnimation();
if (animated) {
iView.startAnimation(fadeOutAnimation);
}
iView.setVisibility(View.GONE);
}
} catch (Exception ex) {
Logger.printException(() -> "private_setVisibility failure", ex);
}
}
protected abstract boolean shouldBeShown();
public void hide() {
if (!isShowing) {
return;
}
Utils.verifyOnMainThread();
View v = buttonReference.get();
if (v == null) {
return;
}
v.setVisibility(View.GONE);
isShowing = false;
}
}

View File

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

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 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 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 static final fun spoofVideoStreamsPatch (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 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;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
}
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 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 static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

View File

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

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

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

View File

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

View File

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

View File

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

View File

@@ -137,3 +137,15 @@ internal val patchIncludedExtensionMethodFingerprint = fingerprint {
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(
block: BytecodePatchBuilder.() -> Unit = {},
applyMediaFetchHotConfigChanges: BytecodePatchBuilder.() -> Boolean = { false },
executeBlock: BytecodePatchContext.() -> Unit = {},
) = bytecodePatch(
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()
@@ -238,6 +239,17 @@ fun spoofVideoStreamsPatch(
// 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()
}
}

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
val enableSeekbarTappingPatch = bytecodePatch(
name = "Seekbar tapping",
description = "Adds an option to enable tap-to-seek on the seekbar of the video player.",
name = "Enable tap to seek",
description = "Adds an option to enable tap to seek on the seekbar of the video player.",
) {
dependsOn(
sharedExtensionPatch,

View File

@@ -43,7 +43,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
val hidePlayerOverlayButtonsPatch = bytecodePatch(
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(
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.addResourcesPatch
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.navigation.hookNavigationButtonCreated
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
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.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")
val changeFormFactorPatch = bytecodePatch(
@@ -26,6 +28,7 @@ val changeFormFactorPatch = bytecodePatch(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
navigationButtonsPatch
)
compatibleWith(
@@ -50,6 +53,8 @@ val changeFormFactorPatch = bytecodePatch(
)
)
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
createPlayerRequestBodyWithModelFingerprint.method.apply {
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")
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.",
) {
dependsOn(
@@ -78,9 +81,7 @@ val hideEndscreenCardsPatch = bytecodePatch(
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, " +
"Lapp/revanced/extension/youtube/patches/HideEndscreenCardsPatch;->" +
"hideEndscreen(Landroid/view/View;)V",
"invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR->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

@@ -158,9 +158,9 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_comments_by_members_header"),
SwitchPreference("revanced_hide_comments_section"),
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_thanks_button"),
SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons"),
),
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
),

View File

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

View File

@@ -1,79 +1,9 @@
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.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.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import app.revanced.patches.youtube.layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch
internal var sizeAdjustableLiteAutoNavOverlay = -1L
private set
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",
)
}
}
}
@Deprecated("Use 'Hide suggested video end screen' instead.")
val disableSuggestedVideoEndScreenPatch = bytecodePatch {
dependsOn(hideEndScreenSuggestedVideoPatch)
}

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")
val miniplayerPatch = bytecodePatch(
name = "Miniplayer",
description = "Adds options to change the in app minimized player."
description = "Adds options to change the in-app minimized player."
) {
dependsOn(
sharedExtensionPatch,

View File

@@ -82,9 +82,9 @@ private val sponsorBlockResourcePatch = resourcePatch {
private const val EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/sponsorblock/SegmentPlaybackController;"
private const val EXTENSION_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButtonController;"
"Lapp/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton;"
private const val EXTENSION_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/sponsorblock/ui/VotingButtonController;"
"Lapp/revanced/extension/youtube/sponsorblock/ui/VotingButton;"
private const val EXTENSION_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController;"

View File

@@ -1,8 +1,29 @@
package app.revanced.patches.youtube.layout.spoofappversion
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val toolBarButtonFingerprint = fingerprint {
returns("V")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("Landroid/view/MenuItem;")
custom { method, _ ->
method.containsLiteralInstruction(menuItemView) &&
indexOfGetDrawableInstruction(method) >= 0
}
}
internal fun indexOfGetDrawableInstruction(method: Method) = method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Landroid/content/res/Resources;" &&
reference.name == "getDrawable"
}
internal val spoofAppVersionFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)

View File

@@ -1,18 +1,44 @@
package app.revanced.patches.youtube.layout.spoofappversion
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
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.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.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.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_17_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.settingsPatch
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.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal var menuItemView = -1L
private set
internal val spoofAppVersionResourcePatch = resourcePatch {
dependsOn(
resourceMappingPatch
)
execute {
menuItemView = resourceMappings["id", "menu_item_view"]
}
}
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch;"
@@ -24,6 +50,7 @@ val spoofAppVersionPatch = bytecodePatch(
"Patching 19.16.39 includes additional older spoofing targets.",
) {
dependsOn(
spoofAppVersionResourcePatch,
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
@@ -46,22 +73,59 @@ val spoofAppVersionPatch = bytecodePatch(
addResources("youtube", "layout.spoofappversion.spoofAppVersionPatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_spoof_app_version"),
if (is_19_17_or_greater) {
ListPreference(
key = "revanced_spoof_app_version_target",
summaryKey = null,
// Group the switch and list preference together, since General menu is sorted by name
// and the preferences can be scattered apart with non English langauges.
PreferenceCategory(
key = null,
// The title does not show, but is used for sorting the group.
titleKey = "revanced_spoof_app_version_title",
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"
)
}
)
)
/**
* If a user really wants to spoof to very old versions with the latest app target
* they can modify the import/export spoof version. But when spoofing the 19.20.xx
* or earlier the Library tab can crash due to missing image resources trying to load.
* As a temporary workaround, do not set an image in the toolbar when the enum name is UNKNOWN.
*/
toolBarButtonFingerprint.method.apply {
val getDrawableIndex = indexOfGetDrawableInstruction(this)
val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) {
opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.returnType == "I"
}
val insertIndex = enumOrdinalIndex + 2
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setImageDrawable"
} + 1
addInstructionsWithLabels(
insertIndex,
"if-eqz v$insertRegister, :ignore",
ExternalLabel("ignore", getInstruction(jumpIndex))
)
}
val insertIndex = spoofAppVersionFingerprint.patternMatch!!.startIndex + 1
val buildOverrideNameRegister =
spoofAppVersionFingerprint.method.getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
@@ -71,7 +135,7 @@ val spoofAppVersionPatch = bytecodePatch(
"""
invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$buildOverrideNameRegister
""",
"""
)
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.misc.debugging
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
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.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
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.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/EnableDebuggingPatch;"
@@ -61,19 +64,17 @@ val enableDebuggingPatch = bytecodePatch(
experimentalBooleanFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).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',
// so there's no need to pass in the default.
addInstructions(
insertIndex,
"""
move-result v0
invoke-static { v0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZJ)Z
move-result v0
return v0
"""
)
addInstructions(
index,
"""
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
move-result v$register
"""
)
}
}
experimentalDoubleFeatureFlagFingerprint.match(
@@ -92,7 +93,6 @@ val enableDebuggingPatch = bytecodePatch(
)
}
experimentalLongFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
@@ -108,21 +108,22 @@ val enableDebuggingPatch = bytecodePatch(
"""
)
experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
}
addInstructions(
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
"""
)
}
experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
addInstructions(
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[]

View File

@@ -11,9 +11,9 @@ internal val experimentalFeatureFlagParentFingerprint = fingerprint {
}
internal val experimentalBooleanFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Z")
parameters("J", "Z")
parameters("L", "J", "Z")
}
internal val experimentalDoubleFeatureFlagFingerprint = fingerprint {
@@ -33,4 +33,3 @@ internal val experimentalStringFeatureFlagFingerprint = fingerprint {
returns("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(
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(
transformInstructionsPatch(

View File

@@ -16,6 +16,23 @@ internal val actionBarSearchResultsFingerprint = fingerprint {
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].
*/

View File

@@ -8,6 +8,7 @@ import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
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.resourceMappingPatch
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.indexOfFirstInstructionReversedOrThrow
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.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.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
internal var imageOnlyTabResourceId = -1L
@@ -32,6 +37,8 @@ internal var actionBarSearchResultsViewMicId = -1L
private set
internal var ytFillBellId = -1L
private set
internal var toolbarContainerId = -1L
private set
private val navigationBarHookResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
@@ -40,6 +47,7 @@ private val navigationBarHookResourcePatch = resourcePatch {
imageOnlyTabResourceId = resourceMappings["layout", "image_only_tab"]
actionBarSearchResultsViewMicId = resourceMappings["layout", "action_bar_search_results_view_mic"]
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;"
internal const val EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR =
"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
@@ -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 ->
navigationBarHookCallbackFingerprint.method.addInstruction(
0,
"invoke-static { p0, p1 }, " +
"$extensionClassDescriptor->navigationTabCreated" +
"invoke-static { p0, p1 }, $extensionClassDescriptor->navigationTabCreated" +
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
)
}

View File

@@ -77,12 +77,9 @@ val playerControlsResourcePatch = resourcePatch {
).item(0)
val bottomTargetDocumentChildNodes = bottomTargetDocument.childNodes
var bottomInsertBeforeNode: Node = bottomTargetDocumentChildNodes.findElementByAttributeValue(
var bottomInsertBeforeNode: Node = bottomTargetDocumentChildNodes.findElementByAttributeValueOrThrow(
"android:inflatedId",
bottomLastLeftOf,
) ?: bottomTargetDocumentChildNodes.findElementByAttributeValueOrThrow(
"android:id", // Older targets use non-inflated id.
bottomLastLeftOf,
)
addTopControl = { resourceDirectoryName ->
@@ -123,7 +120,7 @@ val playerControlsResourcePatch = resourcePatch {
).item(0).childNodes
// Copy the patch layout xml into the target layout file.
for (index in 1 until sourceElements.length) {
for (index in sourceElements.length - 1 downTo 1) {
val element = sourceElements.item(index).cloneNode(true)
// If the element has no attributes there's no point adding it to the destination.
@@ -189,7 +186,7 @@ fun initializeBottomControl(descriptor: String) {
fun injectVisibilityCheckCall(descriptor: String) {
visibilityMethod.addInstruction(
visibilityInsertIndex++,
"invoke-static { p1 , p2 }, $descriptor->changeVisibility(ZZ)V",
"invoke-static { p1 , p2 }, $descriptor->setVisibility(ZZ)V",
)
if (!visibilityImmediateCallbacksExistModified) {
@@ -199,7 +196,7 @@ fun injectVisibilityCheckCall(descriptor: String) {
visibilityImmediateMethod.addInstruction(
visibilityImmediateInsertIndex++,
"invoke-static { p0 }, $descriptor->changeVisibilityImmediate(Z)V",
"invoke-static { p0 }, $descriptor->setVisibilityImmediate(Z)V",
)
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.misc.playertype
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -15,6 +16,12 @@ internal val playerTypeFingerprint = fingerprint {
custom { _, classDef -> classDef.endsWith("/YouTubePlayerOverlaysLayout;") }
}
internal val reelWatchPagerFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")
literal { reelWatchPlayerId }
}
internal val videoStateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
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.getInstruction
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.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
internal 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(
description = "Hook to get the current player type and video playback state.",
) {
dependsOn(sharedExtensionPatch)
dependsOn(sharedExtensionPatch, playerTypeHookResourcePatch)
execute {
playerTypeFingerprint.method.addInstruction(
@@ -20,6 +39,17 @@ val playerTypeHookPatch = bytecodePatch(
"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 {
val endIndex = videoStateFingerprint.patternMatch!!.endIndex
val videoStateFieldName = getInstruction<ReferenceInstruction>(endIndex).reference
@@ -27,9 +57,9 @@ val playerTypeHookPatch = bytecodePatch(
addInstructions(
0,
"""
iget-object v0, p1, $videoStateFieldName # copy VideoState parameter field
invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->setVideoState(Ljava/lang/Enum;)V
""",
iget-object v0, p1, $videoStateFieldName # copy VideoState parameter field
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(
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(
sharedExtensionPatch,

View File

@@ -323,6 +323,7 @@ object PreferenceScreen : BasePreferenceScreen() {
val VIDEO = Screen(
key = "revanced_settings_screen_12_video",
summaryKey = null,
sorting = Sorting.BY_KEY,
)
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.SwitchPreference
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.settingsPatch
@@ -25,7 +27,10 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
dependsOn(
userAgentClientSpoofPatch,
settingsPatch,
versionCheckPatch
)
}, {
is_19_34_or_greater
}, {
addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch")

View File

@@ -8,8 +8,11 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
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.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
@@ -29,6 +32,7 @@ val rememberVideoQualityPatch = bytecodePatch(
dependsOn(
sharedExtensionPatch,
videoInformationPatch,
playerTypeHookPatch,
settingsPatch,
addResourcesPatch,
)
@@ -49,19 +53,42 @@ val rememberVideoQualityPatch = bytecodePatch(
addResources("youtube", "video.quality.rememberVideoQualityPatch")
PreferenceScreen.VIDEO.addPreferences(
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(
key = "revanced_video_quality_default_mobile",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
// 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 = setOf(
ListPreference(
key = "revanced_video_quality_default_mobile",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
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")
)
)
)
/*

View File

@@ -1,10 +1,19 @@
package app.revanced.patches.youtube.video.speed
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.custom.customPlaybackSpeedPatch
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")
val playbackSpeedPatch = bytecodePatch(
name = "Playback speed",
@@ -26,6 +35,18 @@ val playbackSpeedPatch = bytecodePatch(
"19.45.38",
"19.46.42",
"19.47.53",
),
)
)
finalize {
PreferenceScreen.VIDEO.addPreferences(
PreferenceCategory(
key = "revanced_zz_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

@@ -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.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 app.revanced.patches.youtube.video.speed.settingsMenuVideoSpeedGroup
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
@@ -71,13 +71,18 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
execute {
addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_custom_speed_menu"),
TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE),
settingsMenuVideoSpeedGroup.addAll(
listOf(
SwitchPreference("revanced_custom_speed_menu"),
TextPreference(
"revanced_custom_playback_speeds",
inputType = InputType.TEXT_MULTI_LINE
),
)
)
if (is_19_25_or_greater) {
PreferenceScreen.VIDEO.addPreferences(
settingsMenuVideoSpeedGroup.add(
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.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 app.revanced.patches.youtube.video.information.*
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
private const val EXTENSION_CLASS_DESCRIPTOR =
@@ -30,15 +30,17 @@ internal val rememberPlaybackSpeedPatch = bytecodePatch {
execute {
addResources("youtube", "video.speed.remember.rememberPlaybackSpeedPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_remember_playback_speed_last_selected"),
ListPreference(
key = "revanced_playback_speed_default",
summaryKey = null,
// Entries and values are set by the extension code based on the actual speeds available.
entriesKey = null,
entryValuesKey = null,
),
settingsMenuVideoSpeedGroup.addAll(
listOf(
ListPreference(
key = "revanced_playback_speed_default",
summaryKey = null,
// Entries and values are set by the extension code based on the actual speeds available.
entriesKey = null,
entryValuesKey = null,
),
SwitchPreference("revanced_remember_playback_speed_last_selected")
)
)
onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted")

View File

@@ -408,10 +408,13 @@ internal fun MutableMethod.insertFeatureFlagBooleanOverride(literal: Long, exten
val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(index).registerA
val operation = if (register < 16) "invoke-static { v$register }"
else "invoke-static/range { v$register .. v$register }"
addInstructions(
index + 1,
"""
invoke-static { v$register }, $extensionsMethod
$operation, $extensionsMethod
move-result v$register
"""
)
@@ -458,7 +461,7 @@ fun MutableMethod.returnEarly(bool: Boolean = false) {
return v0
"""
else -> throw Exception("This case should never happen.")
else -> throw Exception("Return type is not supported: $this")
}
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. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
</patch>
@@ -206,6 +206,7 @@ Second \"item\" text"</string>
<patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

View File

@@ -130,7 +130,7 @@ Second \"item\" text"</string>
<!-- '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. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
</patch>
@@ -206,6 +206,7 @@ Second \"item\" text"</string>
<patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

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_summary">ØĨØŽŲØ§ØĄ ØŖŲˆ ØšØąØļ Ų…ŲƒŲˆŲ†Ø§ØĒ ؈Øĩ؁ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</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_summary_on">Ų…ØŽŲŲŠ ؁؊ Ø§Ų„Ų…ŲˆØŦØ˛</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_summary_on">Ų…ØŽŲŲŠ ؁؊ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ŲŠØšØąØļ ؁؊ Ø§Ų„Ø¨Ø­ØĢ</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_off">ŲŠØ¸Ų‡Øą ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</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_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_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø¨ØˇØ§Ų‚Ø§ØĒ Ø§Ų„ØąØšØ§ŲŠØŠ Ø§Ų„Ø°Ø§ØĒŲŠØŠ</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_off">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø¨Ø§Ų†Øą</string>
<string name="revanced_hide_end_screen_store_banner_title">ØĨØŽŲØ§ØĄ Ų„Ø§ŲØĒØŠ شاش؊ Ø§Ų„Ų…ØĒØŦØą Ø§Ų„Ų†Ų‡Ø§ØĻŲŠØŠ</string>
@@ -443,10 +443,10 @@ Second \"item\" text"</string>
<string name="revanced_share_copy_url_success">ØĒŲ… Ų†ØŗØŽ URL ØĨŲ„Ų‰ Ø§Ų„Ø­Ø§ŲØ¸ØŠ</string>
<string name="revanced_share_copy_url_timestamp_success">ØĒŲ… Ų†ØŗØŽ ØšŲ†ŲˆØ§Ų† URL Ų…Øš Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ</string>
<string name="revanced_copy_video_url_title">ØšØąØļ Ø˛Øą Ų†ØŗØŽ ØšŲ†ŲˆØ§Ų† URL Ų„Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_copy_video_url_summary_on">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą. Ø§Ų†Ų‚Øą Ų„Ų†ØŗØŽ ØąØ§Ø¨Øˇ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ. Ø§Ų†Ų‚Øą Ų…Øš Ø§Ų„Ø§ØŗØĒŲ…ØąØ§Øą Ų„Ų†ØŗØŽ URL Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų…Øš Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ</string>
<string name="revanced_copy_video_url_summary_on">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą. Ø§Ų†Ų‚Øą Ų„Ų†ØŗØŽ ØšŲ†ŲˆØ§Ų† URL Ų„Ų„ŲŲŠØ¯ŲŠŲˆ. Ø§Ų†Ų‚Øą Ų…Øš Ø§Ų„Ø§ØŗØĒŲ…ØąØ§Øą Ų„Ų„Ų†ØŗØŽ Ų…Øš Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ</string>
<string name="revanced_copy_video_url_summary_off">Ų„Ø§ ؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą</string>
<string name="revanced_copy_video_url_timestamp_title">ØšØąØļ Ø˛Øą Ų†ØŗØŽ URL Ų…Øš Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ</string>
<string name="revanced_copy_video_url_timestamp_summary_on">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą. Ø§Ų†Ų‚Øą Ų„Ų†ØŗØŽ ØšŲ†ŲˆØ§Ų† URL Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ų…Øš Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ. Ø§Ų†Ų‚Øą Ų…Øš Ø§Ų„Ø§ØŗØĒŲ…ØąØ§Øą Ų„Ų†ØŗØŽ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø¨Ø¯ŲˆŲ† Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ</string>
<string name="revanced_copy_video_url_timestamp_summary_on">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą. Ø§Ų†Ų‚Øą Ų„Ų†ØŗØŽ ØšŲ†ŲˆØ§Ų† URL Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ų…Øš Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ. Ø§Ų†Ų‚Øą Ų…Øš Ø§Ų„Ø§ØŗØĒŲ…ØąØ§Øą Ų„Ų„Ų†ØŗØŽ Ø¨Ø¯ŲˆŲ† Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ</string>
<string name="revanced_copy_video_url_timestamp_summary_off">Ų„Ø§ ؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
@@ -582,12 +582,18 @@ Second \"item\" text"</string>
<string name="revanced_hide_subscriptions_button_title">ØĨØŽŲØ§ØĄ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_hide_subscriptions_button_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø˛Øą Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_hide_subscriptions_button_summary_off">؊ØĒŲ… ØšØąØļ Ø˛Øą Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_hide_notifications_button_title">ØĨØŽŲØ§ØĄ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ</string>
<string name="revanced_hide_notifications_button_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø˛Øą Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ</string>
<string name="revanced_hide_notifications_button_summary_off">؊ØĒŲ… ØšØąØļ Ø˛Øą Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">ØĒØ¨Ø¯ŲŠŲ„ Ø§Ų„ØĨŲ†Ø´Ø§ØĄ Ų…Øš Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"ØĒŲ… ØĒØ¨Ø¯ŲŠŲ„ Ø˛Øą Ø§Ų„ØĨŲ†Ø´Ø§ØĄ Ø¨Ų€Ø˛Øą Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ
Ų…Ų„Ø§Ø­Ø¸ØŠ: ŲŠØ¤Ø¯ŲŠ ØĒŲ…ŲƒŲŠŲ† Ų‡Ø°Ø§ ØŖŲŠØļŲ‹Ø§ ØĨŲ„Ų‰ ØĨØŽŲØ§ØĄ ØĨØšŲ„Ø§Ų†Ø§ØĒ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø¨Ø§Ų„Ų‚ŲˆØŠ"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Ų„Ø§ ؊ØĒŲ… ØĒØ¨Ø¯ŲŠŲ„ Ø˛Øą Ø§Ų„ØĨŲ†Ø´Ø§ØĄ Ø¨Ø˛Øą Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ</string>
<string name="revanced_switch_create_with_notifications_button_user_dialog_message">"ØŗŲŠØ¤Ø¯ŲŠ ØĒØšØˇŲŠŲ„ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد ØŖŲŠØļŲ‹Ø§ ØĨŲ„Ų‰ ØĒØšØˇŲŠŲ„ Ø­Ø¸Øą ØĨØšŲ„Ø§Ų†Ø§ØĒ Shorts.
ØĨذا Ų„Ų… ؊ØĒŲ… ØĒŲØšŲŠŲ„ ØĒØēŲŠŲŠØą Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد، ŲØ­Ø§ŲˆŲ„ Ø§Ų„ØĒØ¨Ø¯ŲŠŲ„ ØĨŲ„Ų‰ ؈ØļØš Ø§Ų„ØĒØĩŲŲ‘ŲØ­ Ø§Ų„Ų…ØĒØŽŲŲŠ."</string>
<string name="revanced_hide_navigation_button_labels_title">ØĨØŽŲØ§ØĄ ØĒØŗŲ…ŲŠØ§ØĒ Ø˛Øą Ø§Ų„ØĒŲ†Ų‚Ų„</string>
<string name="revanced_hide_navigation_button_labels_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„ØĒØŗŲ…ŲŠØ§ØĒ</string>
<string name="revanced_hide_navigation_button_labels_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ØĒØŗŲ…ŲŠØ§ØĒ</string>
@@ -657,7 +663,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">؊ØĒŲ… ØšØąØļ ØĒØ°ŲŠŲŠŲ„ Ų‚Ø§ØĻŲ…ØŠ ØŦŲˆØ¯ØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
</patch>
<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_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ØŖØ˛ØąØ§Øą</string>
<string name="revanced_hide_cast_button_title">ØĨØŽŲØ§ØĄ Ø˛Øą Ø§Ų„Ø¨ØĢ</string>
@@ -802,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_off">؊ØĒŲ… ØšØąØļ Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„</string>
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<string name="revanced_disable_suggested_video_end_screen_title">ØĒØšØˇŲŠŲ„ شاش؊ Ų†Ų‡Ø§ŲŠØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ų‚ØĒØąØ­</string>
<string name="revanced_disable_suggested_video_end_screen_summary_on">Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„Ų…Ų‚ØĒØąØ­ØŠ ØŗŲŠØĒŲ… ØĒØšØˇŲŠŲ„Ų‡Ø§</string>
<string name="revanced_disable_suggested_video_end_screen_summary_off">Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„Ų…Ų‚ØĒØąØ­ØŠ ØŗŲŠØĒŲ… ØšØąØļŲ‡Ø§</string>
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
<string name="revanced_end_screen_suggested_video_title">ØĨØŽŲØ§ØĄ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ų‚ØĒØąØ­ ؁؊ شاش؊ Ø§Ų„Ų†Ų‡Ø§ŲŠØŠ</string>
<string name="revanced_end_screen_suggested_video_summary_on">"؊ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ų‚ØĒØąØ­ ؁؊ شاش؊ Ø§Ų„Ų†Ų‡Ø§ŲŠØŠ ØšŲ†Ø¯ ØĨŲŠŲ‚Ø§Ų Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊
ŲŠŲ…ŲƒŲ† ØĒØēŲŠŲŠØą Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ ؁؊ ØĨؚداداØĒ YouTube:
Ø§Ų„ØĨؚداداØĒ ← Ø§Ų„ØĒØ´ØēŲŠŲ„ ← ØĒØ´ØēŲŠŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĒØ§Ų„ŲŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§"</string>
<string name="revanced_end_screen_suggested_video_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ų‚ØĒØąØ­ ؁؊ شاش؊ Ø§Ų„Ų†Ų‡Ø§ŲŠØŠ</string>
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
<string name="revanced_hide_timestamp_title">ØĨØŽŲØ§ØĄ Ø§Ų„ØˇØ§Ø¨Øš Ø§Ų„Ø˛Ų…Ų†ŲŠ Ų„Ų„ŲŲŠØ¯ŲŠŲˆ</string>
@@ -1332,8 +1341,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.links.openLinksExternallyPatch">
<string name="revanced_external_browser_title">؁ØĒØ­ Ø§Ų„ØąŲˆØ§Ø¨Øˇ ؁؊ Ø§Ų„Ų…ØĒØĩŲØ­</string>
<string name="revanced_external_browser_summary_on">؁ØĒØ­ Ø§Ų„ØąŲˆØ§Ø¨Øˇ ØŽØ§ØąØŦŲŠŲ‹Ø§</string>
<string name="revanced_external_browser_summary_off">؁ØĒØ­ Ø§Ų„ØąŲˆØ§Ø¨Øˇ ؁؊ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
<string name="revanced_external_browser_summary_on">؁ØĒØ­ Ø§Ų„ØąŲˆØ§Ø¨Øˇ ؁؊ Ų…ØĒØĩŲØ­ ØŽØ§ØąØŦ؊</string>
<string name="revanced_external_browser_summary_off">؁ØĒØ­ Ø§Ų„ØąŲˆØ§Ø¨Øˇ ؁؊ Ų…ØĒØĩŲØ­ Ø¯Ø§ØŽŲ„ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
</patch>
<patch id="misc.privacy.removeTrackingQueryParameterPatch">
<string name="revanced_remove_tracking_query_parameter_title">ØĨØ˛Ø§Ų„ØŠ Ų…ØšŲ„Ų…ØŠ ØĒØĒبؚ Ø§Ų„Ø§ØŗØĒØšŲ„Ø§Ų…</string>
@@ -1346,10 +1355,10 @@ Second \"item\" text"</string>
<string name="revanced_disable_zoom_haptics_summary_off">ØĒŲ… ØĒŲ…ŲƒŲŠŲ† Ø§Ų„Ø§Ų‡ØĒØ˛Ø§Ø˛</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<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_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 id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

View File

@@ -130,7 +130,7 @@ Second \"item\" text"</string>
<!-- '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. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
</patch>
@@ -208,6 +208,7 @@ Second \"item\" text"</string>
<patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

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_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_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_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_search_title">AxtarÄąÅŸda 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_off">AxtarÄąÅŸda gÃļrÃŧnÃŧr</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ÄąÅŸ nəticələrində gizlədilib</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_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>
@@ -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_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_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_off">Etiket gÃļstərilir</string>
<string name="revanced_hide_end_screen_store_banner_title">Son ekran mağaza etiketini gizlət</string>
@@ -443,10 +442,10 @@ Bu xÃŧsusiyyət yalnÄąz kÃļhnə cihazlar ÃŧçÃŧn mÃļvcuddur"</string>
<string name="revanced_share_copy_url_success">URL buferə kÃļçÃŧrÃŧldÃŧ</string>
<string name="revanced_share_copy_url_timestamp_success">Vaxt mÃļhÃŧrlÃŧ URL kÃļçÃŧrÃŧldÃŧ</string>
<string name="revanced_copy_video_url_title">Video URL-i kÃļçÃŧrmə dÃŧyməsin gÃļstər</string>
<string name="revanced_copy_video_url_summary_on">DÃŧymə gÃļstərilir. Video URL-sini kÃļçÃŧrmək ÃŧçÃŧn toxun. Vaxt mÃļhÃŧrlÃŧ video URL-sini kÃļçÃŧrmək ÃŧçÃŧn basÄąlÄą saxla</string>
<string name="revanced_copy_video_url_summary_on">DÃŧymə gÃļstərilir. Video URL-ni kÃļçÃŧrmək ÃŧçÃŧn toxun. Vaxt mÃļhÃŧrÃŧ ilə kÃļçÃŧrmək ÃŧçÃŧn basÄąb saxlayÄąn</string>
<string name="revanced_copy_video_url_summary_off">DÃŧymə gÃļstərilmir</string>
<string name="revanced_copy_video_url_timestamp_title">Vaxt mÃļhÃŧrÃŧ URL kÃļçÃŧr dÃŧyməsi gÃļstər</string>
<string name="revanced_copy_video_url_timestamp_summary_on">DÃŧymə gÃļstərilir. Vaxt mÃļhÃŧrlÃŧ video URL-sini kÃļçÃŧrmək ÃŧçÃŧn toxun. Vaxt mÃļhÃŧrÃŧ olmadan kÃļçÃŧrmək ÃŧçÃŧn basÄąlÄą saxla</string>
<string name="revanced_copy_video_url_timestamp_summary_on">DÃŧymə gÃļstərilir. Video URL-ni vaxt mÃļhÃŧrÃŧ ilə kÃļçÃŧrmək ÃŧçÃŧn toxun. Vaxt mÃļhÃŧrÃŧ olmadan kÃļçÃŧrmək ÃŧçÃŧn basÄąb saxlayÄąn</string>
<string name="revanced_copy_video_url_timestamp_summary_off">DÃŧymə gÃļstərilmir</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
@@ -582,12 +581,18 @@ EkranÄąn sağ tərəfində dÃŧzÃŧnə sÃŧrÃŧşdÃŧrərək səs səviyyəsini tənz
<string name="revanced_hide_subscriptions_button_title">\"Abunəliklər\"i gizlət</string>
<string name="revanced_hide_subscriptions_button_summary_on">Abunəliklər dÃŧyməsi gizlidir</string>
<string name="revanced_hide_subscriptions_button_summary_off">Abunəliklər dÃŧyməsi gÃļstərilir</string>
<string name="revanced_hide_notifications_button_title">Bildirişləri Gizlət</string>
<string name="revanced_hide_notifications_button_summary_on">Bildirişlər dÃŧyməsi gizlidir</string>
<string name="revanced_hide_notifications_button_summary_off">Bildirişlər dÃŧyməsi gÃļrÃŧnÃŧr</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">\"Yarat\"Äą \"Bildirişlər\" ilə dəyişdir</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"Yarat dÃŧyməsi Bildirişlər dÃŧyməsi ilə dəyişdirilir
Qeyd: Bunu aktivləşdirmə video reklamlarÄą da məcburi olaraq gizlədir"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">\"Yarat\" dÃŧyməsi, \"Bildirişlər\" dÃŧyməsi ilə əvəzlənmir</string>
<string name="revanced_switch_create_with_notifications_button_user_dialog_message">"Bu seçimin qapanmasÄą Shorts reklam əngəllənməsin də qapadacaq.
Bu seçimi dəyişdirmə işə dÃŧşmÃŧrsə, Gizli rejimə keçməyə çalÄąÅŸÄąn."</string>
<string name="revanced_hide_navigation_button_labels_title">Fəaliyyət dÃŧymə etiketlərini gizlət</string>
<string name="revanced_hide_navigation_button_labels_summary_on">Etiketlər gizlidir</string>
<string name="revanced_hide_navigation_button_labels_summary_off">Etiketlər gÃļstərilir</string>
@@ -657,7 +662,6 @@ Qeyd: Bunu aktivləşdirmə video reklamlarÄą da məcburi olaraq gizlədir"</str
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">Video keyfiyyət menyusu alt məlumatÄą gÃļstərilir</string>
</patch>
<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_off">DÃŧymələr gÃļstərilir</string>
<string name="revanced_hide_cast_button_title">YayÄąmla dÃŧyməsini gizlət</string>
@@ -802,10 +806,12 @@ Qeyd: Bunu aktivləşdirmə video reklamlarÄą da məcburi olaraq gizlədir"</str
<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>
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<string name="revanced_disable_suggested_video_end_screen_title">Təklif edilən video bitiş ekranÄąn qapadÄąn</string>
<string name="revanced_disable_suggested_video_end_screen_summary_on">Təklif olunan videolar qeyri-aktiv ediləcək</string>
<string name="revanced_disable_suggested_video_end_screen_summary_off">Təklif olunan videolar gÃļstəriləcək</string>
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
<string name="revanced_end_screen_suggested_video_title">Son ekran bildirilən videonu gizlət</string>
<string name="revanced_end_screen_suggested_video_summary_on">"Avtomatik oynatma qapadılanda son ekran bildirilən video gizlədilir
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 id="layout.hide.time.hideTimestampPatch">
<string name="revanced_hide_timestamp_title">Video vaxt mÃļhÃŧrÃŧnÃŧ gizlət</string>
@@ -1186,7 +1192,6 @@ Sonradan qapadÄąlarsa, UI səhvlərin Ãļnləmək ÃŧçÃŧn tətbiq məlumatlarÄąn
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<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_entry_0">Qeyri-aktivdir</string>
<string name="revanced_miniplayer_type_entry_1">İlkin</string>
@@ -1332,8 +1337,6 @@ Bunu aktivləşdirmə daha yÃŧksək video keyfiyyətləri əngəlin silə bilər
</patch>
<patch id="misc.links.openLinksExternallyPatch">
<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 id="misc.privacy.removeTrackingQueryParameterPatch">
<string name="revanced_remove_tracking_query_parameter_title">İzləmə sorğusu faktorun sil</string>
@@ -1346,10 +1349,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>
</patch>
<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_summary_on">Orijinal səs istifadəsi</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 dilini istifadə</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 id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

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_summary">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ айО ĐŋаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐēаĐŧĐŋаĐŊĐĩĐŊ҂ҋ аĐŋŅ–ŅĐ°ĐŊĐŊŅ Đ˛Ņ–Đ´ŅĐ°</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_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊŅ‹ Ņž ŅŅ‚ŅƒĐļ҆ҋ</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_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊŅ‹ Ņž ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ПаĐēаСваĐĩŅ†Ņ†Đ° Ņž ĐŋĐžŅˆŅƒĐē҃</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_off">ПаĐēаСаĐŊа Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</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_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_summary_on">ĐĄĐŋĐžĐŊŅĐ°Ņ€ŅĐēŅ–Ņ ĐēĐ°Ņ€Ņ‚ĐēŅ– ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹Ņ</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_off">ПаĐēаСваĐĩŅ†Ņ†Đ° йаĐŊŅŅ€</string>
<string name="revanced_hide_end_screen_store_banner_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ йаĐŊĐĩŅ€ ĐēŅ€Đ°ĐŧŅ‹ ĐŊа ĐēаĐŊŅ‡Đ°Ņ‚ĐēĐžĐ˛Ņ‹Đŧ ŅĐēŅ€Đ°ĐŊĐĩ</string>
@@ -443,10 +443,10 @@ Second \"item\" text"</string>
<string name="revanced_share_copy_url_success">URL ҁĐēаĐŋŅ–Ņ€Đ°Đ˛Đ°ĐŊŅ‹ Ņž ĐąŅƒŅ„ĐĩŅ€ айĐŧĐĩĐŊ҃</string>
<string name="revanced_share_copy_url_timestamp_success">URL-Đ°Đ´Ņ€Đ°Ņ С ĐŋаСĐŊаĐēаК Ņ‡Đ°ŅŅƒ ҁĐēаĐŋŅ–Ņ€Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_copy_video_url_title">ПаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐēĐŊĐžĐŋĐē҃ ҁĐēаĐŋŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊŅ URL Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_copy_video_url_summary_on">ПаĐēаСаĐŊа ĐēĐŊĐžĐŋĐēа. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ, Đēай ҁĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ URL Đ˛Ņ–Đ´ŅĐ°. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ Ņ– ŅžŅ‚Ņ€Ņ‹ĐŧĐģŅ–Đ˛Đ°ĐšŅ†Đĩ, Đēай ҁĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ URL Đ˛Ņ–Đ´ŅĐ° С ĐŋаСĐŊаĐēаК Ņ‡Đ°ŅŅƒ</string>
<string name="revanced_copy_video_url_summary_on">КĐŊĐžĐŋĐēа ĐŋаĐēаСаĐŊа. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ, Đēай ҁĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ URL Đ˛Ņ–Đ´ŅĐ°. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ Ņ– ŅžŅ‚Ņ€Ņ‹ĐŧĐģŅ–Đ˛Đ°ĐšŅ†Đĩ, Đēай ҁĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ С ĐŋаСĐŊаĐēаК Ņ‡Đ°ŅŅƒ</string>
<string name="revanced_copy_video_url_summary_off">КĐŊĐžĐŋĐēа ĐŊĐĩ ĐŋаĐēаСваĐĩŅ†Ņ†Đ°</string>
<string name="revanced_copy_video_url_timestamp_title">ПаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐēĐŊĐžĐŋĐē҃ URL-Đ°Đ´Ņ€Đ°Ņ С Ņ‡Đ°ŅĐžĐ˛Đ°Đš ĐŋаСĐŊаĐēаК</string>
<string name="revanced_copy_video_url_timestamp_summary_on">ПаĐēаСаĐŊа ĐēĐŊĐžĐŋĐēа. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ, Đēай ҁĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ URL Đ˛Ņ–Đ´ŅĐ° С ĐŋаСĐŊаĐēаК Ņ‡Đ°ŅŅƒ. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ Ņ– ŅžŅ‚Ņ€Ņ‹ĐŧĐģŅ–Đ˛Đ°ĐšŅ†Đĩ, Đēай ҁĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ Đ˛Ņ–Đ´ŅĐ° ĐąĐĩС ĐŋаСĐŊаĐēŅ– Ņ‡Đ°ŅŅƒ</string>
<string name="revanced_copy_video_url_timestamp_summary_on">КĐŊĐžĐŋĐēа ĐŋаĐēаСаĐŊа. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ, Đēай ҁĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ URL Đ˛Ņ–Đ´ŅĐ° С ĐŋаСĐŊаĐēаК Ņ‡Đ°ŅŅƒ. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ Ņ– ŅžŅ‚Ņ€Ņ‹ĐŧĐģŅ–Đ˛Đ°ĐšŅ†Đĩ, Đēай ҁĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ ĐąĐĩС ĐŋаСĐŊаĐēŅ– Ņ‡Đ°ŅŅƒ</string>
<string name="revanced_copy_video_url_timestamp_summary_off">КĐŊĐžĐŋĐēа ĐŊĐĩ ĐŋаĐēаСваĐĩŅ†Ņ†Đ°</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
@@ -582,12 +582,18 @@ Second \"item\" text"</string>
<string name="revanced_hide_subscriptions_button_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋадĐŋҖҁĐēŅ–</string>
<string name="revanced_hide_subscriptions_button_summary_on">КĐŊĐžĐŋĐēа \"ПадĐŋҖҁĐēŅ–\" ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
<string name="revanced_hide_subscriptions_button_summary_off">ПаĐēаСаĐŊа ĐēĐŊĐžĐŋĐēа \"ПадĐŋҖҁĐēŅ–\"</string>
<string name="revanced_hide_notifications_button_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ аĐŋĐ°Đ˛ŅŅˆŅ‡ŅĐŊĐŊŅ–</string>
<string name="revanced_hide_notifications_button_summary_on">КĐŊĐžĐŋĐēа аĐŋĐ°Đ˛ŅŅˆŅ‡ŅĐŊĐŊŅŅž ŅŅ…Đ°Đ˛Đ°ĐŊĐ°Ņ</string>
<string name="revanced_hide_notifications_button_summary_off">КĐŊĐžĐŋĐēа аĐŋĐ°Đ˛ŅŅˆŅ‡ŅĐŊĐŊŅŅž ĐŋаĐēаСаĐŊĐ°Ņ</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">ПĐĩŅ€Đ°ĐēĐģŅŽŅ‡Đ°ĐģҌĐŊŅ–Đē \"ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ С аĐŋĐ°Đ˛ŅŅˆŅ‡ŅĐŊĐŊŅĐŧŅ–\"</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"КĐŊĐžĐŋĐēа ÂĢĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒÂģ СаĐŧĐĩĐŊĐĩĐŊа ĐēĐŊĐžĐŋĐēаК ÂĢАĐŋĐ°Đ˛ŅŅˆŅ‡ŅĐŊĐŊŅ–Âģ
Đ—Đ°ŅžĐ˛Đ°ĐŗĐ°: ĐŖĐēĐģŅŽŅ‡ŅĐŊĐŊĐĩ ĐŗŅŅ‚Đ°ĐŗĐ° Ņ‚Đ°ĐēŅĐ°Đŧа ĐŋҀҋĐŧŅƒŅĐžĐ˛Đ° ŅŅ…Đ°Đ˛Đ°Đĩ Đ˛Ņ–Đ´ŅĐ°Ņ€ŅĐēĐģаĐŧ҃"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">КĐŊĐžĐŋĐēа \"ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ\" ĐŊĐĩ ŅžĐˇĐ°ĐĩĐŧадСĐĩĐšĐŊŅ–Ņ‡Đ°Đĩ С ĐēĐŊĐžĐŋĐēаК \"АĐŋĐ°Đ˛ŅŅˆŅ‡ŅĐŊĐŊŅ–\"</string>
<string name="revanced_switch_create_with_notifications_button_user_dialog_message">"АдĐēĐģŅŽŅ‡ŅĐŊĐŊĐĩ ĐŗŅŅ‚Đ°Đš ĐŊаĐģĐ°Đ´Ņ‹ Ņ‚Đ°ĐēŅĐ°Đŧа адĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐąĐģаĐēŅ–Ņ€ĐžŅžĐē҃ Ņ€ŅĐēĐģаĐŧŅ‹ Shorts.
КаĐģŅ– СĐŧĐĩĐŊа ĐŗŅŅ‚Đ°Đš ĐŊаĐģĐ°Đ´Ņ‹ ĐŊĐĩ ŅžŅŅ‚ŅƒĐŋаĐĩ Ņž ҁҖĐģ҃, ĐŋĐ°ŅĐŋŅ€Đ°ĐąŅƒĐšŅ†Đĩ ĐŋĐĩŅ€Đ°ĐēĐģŅŽŅ‡Ņ‹Ņ†Ņ†Đ° Ņž Ņ€ŅĐļŅ‹Đŧ Ņ–ĐŊĐēĐžĐŗĐŊŅ–Ņ‚Đ°."</string>
<string name="revanced_hide_navigation_button_labels_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŧĐĩŅ‚ĐēŅ– ĐēĐŊĐžĐŋаĐē ĐŊĐ°Đ˛Ņ–ĐŗĐ°Ņ†Ņ‹Ņ–</string>
<string name="revanced_hide_navigation_button_labels_summary_on">ĐĻŅŅ‚ĐģŅ–ĐēŅ– ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹Ņ</string>
<string name="revanced_hide_navigation_button_labels_summary_off">Đ­Ņ‚Ņ‹ĐēĐĩŅ‚ĐēŅ– ĐŋаĐēаСаĐŊŅ‹</string>
@@ -657,7 +663,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">ПаĐēаСваĐĩŅ†Ņ†Đ° ĐŊŅ–ĐļĐŊŅ– ĐēаĐģĐžĐŊŅ‚Ņ‹Ņ‚ŅƒĐģ ĐŧĐĩĐŊŅŽ ŅĐēĐ°ŅŅ†Ņ– Đ˛Ņ–Đ´ŅĐ°</string>
</patch>
<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">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅ–Ņ &amp; ĐēĐŊĐžĐŋĐēŅ– ÂĢДаĐģĐĩĐšÂģ</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_cast_button_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐēĐŊĐžĐŋĐē҃ ÂĢĐĸŅ€Đ°ĐŊҁĐģŅŅ†Ņ‹ŅÂģ</string>
@@ -802,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_off">ПаĐēаСаĐŊа ĐŋаĐŊŅĐģҌ ĐŊĐ°Đ˛Ņ–ĐŗĐ°Ņ†Ņ‹Ņ–</string>
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<string name="revanced_disable_suggested_video_end_screen_title">АдĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐēаĐŊŅ‡Đ°Ņ‚ĐēĐžĐ˛Ņ‹ ŅĐēŅ€Đ°ĐŊ ĐŋŅ€Đ°ĐŋаĐŊаваĐŊĐ°ĐŗĐ° Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_disable_suggested_video_end_screen_summary_on">ĐŸŅ€Đ°ĐŋаĐŊаваĐŊŅ‹Ņ Đ˛Ņ–Đ´ŅĐ° ĐąŅƒĐ´ŅƒŅ†ŅŒ адĐēĐģŅŽŅ‡Đ°ĐŊŅ‹</string>
<string name="revanced_disable_suggested_video_end_screen_summary_off">Đ‘ŅƒĐ´ŅƒŅ†ŅŒ ĐŋаĐēаСаĐŊŅ‹ ĐŋŅ€Đ°ĐŋаĐŊаваĐŊŅ‹Ņ Đ˛Ņ–Đ´ŅĐ°</string>
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
<string name="revanced_end_screen_suggested_video_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋŅ€Đ°ĐŋаĐŊаваĐŊаĐĩ Đ˛Ņ–Đ´ŅĐ° ĐŊа ĐēаĐŊŅ‡Đ°Ņ‚ĐēĐžĐ˛Ņ‹Đŧ ŅĐēŅ€Đ°ĐŊĐĩ</string>
<string name="revanced_end_screen_suggested_video_summary_on">"ĐŸŅ€Đ°ĐŋаĐŊаваĐŊаĐĩ Đ˛Ņ–Đ´ŅĐ° ĐŊа ĐēаĐŊŅ‡Đ°Ņ‚ĐēĐžĐ˛Ņ‹Đŧ ŅĐēŅ€Đ°ĐŊĐĩ ĐąŅƒĐ´ĐˇĐĩ ŅŅ…Đ°Đ˛Đ°ĐŊа, ĐēаĐģŅ– Đ°ŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊаĐĩ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊĐĩ Đ˛Ņ‹ĐēĐģŅŽŅ‡Đ°ĐŊа.
ĐŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊаĐĩ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŧĐžĐļĐŊа СĐŧŅĐŊŅ–Ņ†ŅŒ ҃ ĐŊаĐģĐ°Đ´Đ°Ņ… YouTube:
НаĐģĐ°Đ´Ņ‹ → ĐŸŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊĐĩ → ĐŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊаĐĩ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊĐ°ĐŗĐ° Đ˛Ņ–Đ´ŅĐ°"</string>
<string name="revanced_end_screen_suggested_video_summary_off">ПаĐēĐ°ĐˇĐ˛Đ°Ņ†ŅŒ ĐŋŅ€Đ°ĐŋаĐŊаваĐŊаĐĩ Đ˛Ņ–Đ´ŅĐ° ĐŊа ĐēаĐŊŅ‡Đ°Ņ‚ĐēĐžĐ˛Ņ‹Đŧ ŅĐēŅ€Đ°ĐŊĐĩ</string>
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
<string name="revanced_hide_timestamp_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŧĐĩŅ‚Đē҃ Ņ‡Đ°ŅŅƒ Đ˛Ņ–Đ´ŅĐ°</string>
@@ -1187,7 +1196,7 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">ĐœŅ–ĐŊŅ–-ĐŋĐģŅĐĩŅ€</string>
<string name="revanced_miniplayer_screen_summary">ЗĐŧŅĐŊҖ҆Đĩ ҁ҂ҋĐģҌ ĐŧŅ–ĐŊŅ–ĐŧŅ–ĐˇĐ°Đ˛Đ°ĐŊĐ°ĐŗĐ° ĐŋĐģŅĐĩŅ€Đ° Ņž ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧĐĩ</string>
<string name="revanced_miniplayer_screen_summary">ЗĐŧŅĐŊŅ–Ņ†ŅŒ ҁ҂ҋĐģҌ ĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ°ĐŗĐ° ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐģҌĐŊŅ–Đēа Ņž ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧĐĩ</string>
<string name="revanced_miniplayer_type_title">ĐĸŅ‹Đŋ ĐŧŅ–ĐŊŅ–ĐŋĐģŅĐĩŅ€Đ°</string>
<string name="revanced_miniplayer_type_entry_0">ІĐŊваĐģŅ–Đ´Ņ‹</string>
<string name="revanced_miniplayer_type_entry_1">Па СĐŧĐ°ŅžŅ‡Đ°ĐŊĐŊŅ–</string>
@@ -1333,8 +1342,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.links.openLinksExternallyPatch">
<string name="revanced_external_browser_title">АдĐēŅ€Ņ‹Đ˛Đ°Ņ†ŅŒ ҁĐŋĐ°ŅŅ‹ĐģĐēŅ– Ņž ĐąŅ€Đ°ŅžĐˇĐĩҀҋ</string>
<string name="revanced_external_browser_summary_on">АдĐēҀҋ҆҆ґ ҁĐŋĐ°ŅŅ‹ĐģаĐē СвОĐŊĐē҃</string>
<string name="revanced_external_browser_summary_off">АдĐēҀҋ҆҆ґ ҁĐŋĐ°ŅŅ‹ĐģаĐē ҃ ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧĐĩ</string>
<string name="revanced_external_browser_summary_on">АдĐēҀҋ҆҆ґ ҁĐŋĐ°ŅŅ‹ĐģаĐē ҃ СĐŊĐĩ҈ĐŊŅ–Đŧ ĐąŅ€Đ°ŅžĐˇĐĩҀҋ</string>
<string name="revanced_external_browser_summary_off">АдĐēҀҋ҆҆ґ ҁĐŋĐ°ŅŅ‹ĐģаĐē ва ŅžĐąŅƒĐ´Đ°Đ˛Đ°ĐŊŅ‹Đŧ ĐąŅ€Đ°ŅžĐˇĐĩҀҋ</string>
</patch>
<patch id="misc.privacy.removeTrackingQueryParameterPatch">
<string name="revanced_remove_tracking_query_parameter_title">Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ СаĐŋŅ‹Ņ‚Ņƒ Đ°Đ´ŅĐžŅ‡Đ˛Đ°ĐŊĐŊŅ</string>
@@ -1347,10 +1356,11 @@ Second \"item\" text"</string>
<string name="revanced_disable_zoom_haptics_summary_off">ĐĸаĐē҂ҋĐģҌĐŊŅ‹Ņ ŅŅ–ĐŗĐŊаĐģŅ‹ ŅžĐēĐģŅŽŅ‡Đ°ĐŊŅ‹</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">Đ’Ņ‹ĐŧŅƒŅˆĐ°ĐŊаĐĩ Đ°Ņ€Ņ‹ĐŗŅ–ĐŊаĐģҌĐŊаĐĩ Đ°ŅžĐ´Ņ‹Ņ‘</string>
<string name="revanced_force_original_audio_summary_on">Đ’Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐŊĐŊĐĩ Đ°Ņ€Ņ‹ĐŗŅ–ĐŊаĐģҌĐŊĐ°ĐŗĐ° Đ°ŅžĐ´Ņ‹Ņ‘</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_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 id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

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_summary">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ иĐģи ĐŋĐžĐēаСваĐŊĐĩ ĐŊа ĐēĐžĐŧĐŋĐžĐŊĐĩĐŊŅ‚Đ¸Ņ‚Đĩ Са ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ ĐŊа видĐĩĐžĐēĐģиĐŋОвĐĩŅ‚Đĩ</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_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đ°</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_summary_on">ПаĐŊĐĩĐģŅŠŅ‚ ҁ Ņ„Đ¸ĐģŅ‚ŅŠŅ€Đ¸ ĐŊа Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ПаĐŊĐĩĐģŅŠŅ‚ ҁ Ņ„Đ¸ĐģŅ‚ŅŠŅ€Đ¸ ĐŊа Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ ҁĐĩ ĐŋĐžĐēаСва</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_off">ПоĐēаСаĐŊĐž в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</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_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_summary_on">ХаĐŧĐžŅĐŋĐžĐŊŅĐžŅ€Đ¸Ņ€Đ°ĐŊĐ¸Ņ‚Đĩ ĐēĐ°Ņ€Ņ‚Đ¸ ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</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_off">БаĐŊĐĩŅ€ŅŠŅ‚ Đĩ ĐŋĐžĐēаСаĐŊ</string>
<string name="revanced_hide_end_screen_store_banner_title">ĐĄĐēŅ€Đ¸Đš йаĐŊĐĩŅ€Đ° Са Ņ€ĐĩĐēĐģаĐŧа в ĐēŅ€Đ°Ņ ĐŊа ĐĩĐēŅ€Đ°ĐŊа</string>
@@ -443,10 +443,10 @@ Second \"item\" text"</string>
<string name="revanced_share_copy_url_success">Đ’Ņ€ŅŠĐˇĐēĐ°Ņ‚Đ° Đĩ ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊа в ĐēĐģиĐŋĐąĐžŅ€Đ´Đ°.</string>
<string name="revanced_share_copy_url_timestamp_success">URL Đ°Đ´Ņ€Đĩҁ ҁ ĐžŅ‚ĐŧĐĩŅ‚Đēа Са Đ˛Ņ€ĐĩĐŧĐĩ, ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊ в ĐēĐģиĐŋĐąĐžŅ€Đ´Đ°.</string>
<string name="revanced_copy_video_url_title">ПоĐēаСваĐŊĐĩ ĐŊа ĐąŅƒŅ‚ĐžĐŊа Са ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа URL Đ°Đ´Ņ€ĐĩŅĐ° ĐŊа видĐĩĐžĐēĐģиĐŋа</string>
<string name="revanced_copy_video_url_summary_on">ПоĐēаСаĐŊ Đĩ ĐąŅƒŅ‚ĐžĐŊ. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ, Са да ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ URL ĐŊа видĐĩĐžŅ‚Đž. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ и ĐˇĐ°Đ´Ņ€ŅŠĐļŅ‚Đĩ, Са да ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ URL Đ°Đ´Ņ€ĐĩŅĐ° ĐŊа видĐĩĐžĐēĐģиĐŋа ҁ ĐēĐģĐĩĐšĐŧĐž Са Đ˛Ņ€ĐĩĐŧĐĩ</string>
<string name="revanced_copy_video_url_summary_on">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Đĩ ĐŋĐžĐēаСаĐŊ. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ, Са да ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ URL Đ°Đ´Ņ€ĐĩŅĐ° ĐŊа видĐĩĐžĐēĐģиĐŋа. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ и ĐˇĐ°Đ´Ņ€ŅŠĐļŅ‚Đĩ, Са да ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ ҁ Đ˛Ņ€ĐĩĐŧĐĩви ĐŋĐĩŅ‡Đ°Ņ‚</string>
<string name="revanced_copy_video_url_summary_off">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ ĐŊĐĩ Đĩ ĐŋĐžĐēаСаĐŊ</string>
<string name="revanced_copy_video_url_timestamp_title">ПоĐēаСваĐŊĐĩ ĐŊа ĐąŅƒŅ‚ĐžĐŊа Са ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа URL Đ°Đ´Ņ€ĐĩŅĐ° ĐŊа Đ˛Ņ€ĐĩĐŧĐĩĐ˛Đ¸Ņ ĐžŅ‚ĐŋĐĩŅ‡Đ°Ņ‚ŅŠĐē ĐŊа видĐĩĐžĐēĐģиĐŋа</string>
<string name="revanced_copy_video_url_timestamp_summary_on">ПоĐēаСаĐŊ Đĩ ĐąŅƒŅ‚ĐžĐŊ. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ, Са да ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ URL Đ°Đ´Ņ€ĐĩŅĐ° ĐŊа видĐĩĐžĐēĐģиĐŋа ҁ ĐēĐģĐĩĐšĐŧĐž Са Đ˛Ņ€ĐĩĐŧĐĩ. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ и ĐˇĐ°Đ´Ņ€ŅŠĐļŅ‚Đĩ, Са да ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ видĐĩĐžĐēĐģиĐŋ ĐąĐĩС ĐēĐģĐĩĐšĐŧĐž Са Đ˛Ņ€ĐĩĐŧĐĩ</string>
<string name="revanced_copy_video_url_timestamp_summary_on">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Đĩ ĐŋĐžĐēаСаĐŊ. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ, Са да ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ URL Đ°Đ´Ņ€ĐĩŅĐ° ĐŊа видĐĩĐžĐēĐģиĐŋа ҁ Đ˛Ņ€ĐĩĐŧĐĩви ĐŋĐĩŅ‡Đ°Ņ‚. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ и ĐˇĐ°Đ´Ņ€ŅŠĐļŅ‚Đĩ, Са да ĐēĐžĐŋĐ¸Ņ€Đ°Ņ‚Đĩ ĐąĐĩС Đ˛Ņ€ĐĩĐŧĐĩви ĐŋĐĩŅ‡Đ°Ņ‚</string>
<string name="revanced_copy_video_url_timestamp_summary_off">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ ĐŊĐĩ Đĩ ĐŋĐžĐēаСаĐŊ</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
@@ -582,12 +582,18 @@ Second \"item\" text"</string>
<string name="revanced_hide_subscriptions_button_title">АйОĐŊаĐŧĐĩĐŊŅ‚Đ¸</string>
<string name="revanced_hide_subscriptions_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊа Са айОĐŊаĐŧĐĩĐŊŅ‚Đ¸ Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_subscriptions_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊа Са айОĐŊаĐŧĐĩĐŊŅ‚Đ¸ ҁĐĩ ĐŋĐžĐēаСва</string>
<string name="revanced_hide_notifications_button_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа иСвĐĩŅŅ‚Đ¸ŅŅ‚Đ°</string>
<string name="revanced_hide_notifications_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Са иСвĐĩŅŅ‚Đ¸Ņ Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_notifications_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Са иСвĐĩŅŅ‚Đ¸Ņ Đĩ ĐŋĐžĐēаСаĐŊ</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">ЗаĐŧĐĩĐŊĐĩŅ‚Đĩ ĐąŅƒŅ‚ĐžĐŊа â€žĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ“ ҁ ĐąŅƒŅ‚ĐžĐŊа „ИзвĐĩŅŅ‚Đ¸Ņâ€œ</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Са ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ Đĩ ҁĐŧĐĩĐŊĐĩĐŊ ҁ ĐąŅƒŅ‚ĐžĐŊа ИСвĐĩŅŅ‚Đ¸Ņ
ЗабĐĩĐģĐĩĐļĐēа: АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž ĐŊа Ņ‚ĐžĐ˛Đ° ŅŅŠŅ‰Đž ĐŋŅ€Đ¸ĐŊŅƒĐ´Đ¸Ņ‚ĐĩĐģĐŊĐž ҁĐēŅ€Đ¸Đ˛Đ° видĐĩĐž Ņ€ĐĩĐēĐģаĐŧĐ¸Ņ‚Đĩ"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊĐ¸Ņ‚Đĩ \"ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ\" и \"ИСвĐĩŅŅ‚Đ¸Ņ\" ĐŊĐĩ ŅĐ° Ņ€Đ°ĐˇĐŧĐĩĐŊĐĩĐŊи</string>
<string name="revanced_switch_create_with_notifications_button_user_dialog_message">"ДĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž ĐŊа Ņ‚Đ°ĐˇĐ¸ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ŅŅŠŅ‰Đž ҉Đĩ Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ° ĐąĐģĐžĐēĐ¸Ņ€Đ°ĐŊĐĩŅ‚Đž ĐŊа Ņ€ĐĩĐēĐģаĐŧĐ¸Ņ‚Đĩ Са Shorts.
АĐēĐž ĐŋŅ€ĐžĐŧŅĐŊĐ°Ņ‚Đ° ĐŊа Ņ‚Đ°ĐˇĐ¸ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŊĐĩ вĐģĐĩСĐĩ в ŅĐ¸Đģа, ĐžĐŋĐ¸Ņ‚Đ°ĐšŅ‚Đĩ да ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ в Ņ€ĐĩĐļиĐŧ „иĐŊĐēĐžĐŗĐŊĐ¸Ņ‚Đžâ€œ."</string>
<string name="revanced_hide_navigation_button_labels_title">ИĐŧĐĩĐŊа ĐŊа ĐąŅƒŅ‚ĐžĐŊĐ¸Ņ‚Đĩ ĐŊа ĐģĐĩĐŊŅ‚Đ°Ņ‚Đ° Са ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸Ņ</string>
<string name="revanced_hide_navigation_button_labels_summary_on">Đ•Ņ‚Đ¸ĐēĐĩŅ‚Đ¸Ņ‚Đĩ ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_navigation_button_labels_summary_off">Đ•Ņ‚Đ¸ĐēĐĩŅ‚Đ¸Ņ‚Đĩ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
@@ -657,7 +663,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">ДоĐģĐŊĐ¸ŅŅ‚ ĐēĐžĐģĐžĐŊŅ‚Đ¸Ņ‚ŅƒĐģ ĐŊа ĐŧĐĩĐŊŅŽŅ‚Đž Са ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž ĐŊа видĐĩĐžŅ‚Đž ҁĐĩ ĐŋĐžĐēаСва</string>
</patch>
<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_off">Đ‘ŅƒŅ‚ĐžĐŊĐ¸Ņ‚Đĩ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
<string name="revanced_hide_cast_button_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐąŅƒŅ‚ĐžĐŊа Cast</string>
@@ -802,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_off">ĐĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸ĐžĐŊĐŊĐ°Ņ‚Đ° ĐģĐĩĐŊŅ‚Đ° ҁĐĩ ĐŋĐžĐēаСва</string>
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<string name="revanced_disable_suggested_video_end_screen_title">ĐŸŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ°ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ в ĐēŅ€Đ°Ņ</string>
<string name="revanced_disable_suggested_video_end_screen_summary_on">ĐŸŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ°ĐŊĐ¸Ņ‚Đĩ видĐĩĐžĐēĐģиĐŋОвĐĩ в ĐēŅ€Đ°Ņ ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_disable_suggested_video_end_screen_summary_off">ĐŸŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ°ĐŊĐ¸Ņ‚Đĩ видĐĩĐžĐēĐģиĐŋОвĐĩ в ĐēŅ€Đ°Ņ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
<string name="revanced_end_screen_suggested_video_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐžŅ‚Đž видĐĩĐž в ĐēŅ€Đ°Ņ ĐŊа ĐĩĐēŅ€Đ°ĐŊа</string>
<string name="revanced_end_screen_suggested_video_summary_on">"ĐŸŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐžŅ‚Đž видĐĩĐž в ĐēŅ€Đ°Ņ ĐŊа ĐĩĐēŅ€Đ°ĐŊа Đĩ ҁĐēŅ€Đ¸Ņ‚Đž, ĐēĐžĐŗĐ°Ņ‚Đž Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐžŅ‚Đž Đŋ҃ҁĐēаĐŊĐĩ Đĩ иСĐēĐģŅŽŅ‡ĐĩĐŊĐž
ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐžŅ‚Đž Đŋ҃ҁĐēаĐŊĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩĐŊĐž в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ĐŊа YouTube:
ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи → Đ’ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ → ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Đŋ҃ҁĐēаĐŊĐĩ ĐŊа ҁĐģĐĩĐ´Đ˛Đ°Ņ‰ĐžŅ‚Đž видĐĩĐž"</string>
<string name="revanced_end_screen_suggested_video_summary_off">ĐŸŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐžŅ‚Đž видĐĩĐž в ĐēŅ€Đ°Ņ ĐŊа ĐĩĐēŅ€Đ°ĐŊа Đĩ ĐŋĐžĐēаСаĐŊĐž</string>
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
<string name="revanced_hide_timestamp_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐēĐģĐĩĐšĐŧĐžŅ‚Đž Са Đ˛Ņ€ĐĩĐŧĐĩ ĐŊа видĐĩĐžĐēĐģиĐŋа</string>
@@ -1186,7 +1195,7 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">МиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ ĐĩĐēŅ€Đ°ĐŊ Са Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
<string name="revanced_miniplayer_screen_summary">ĐŸŅ€ĐžĐŧĐĩĐŊĐĩŅ‚Đĩ ŅŅ‚Đ¸Đģа ĐŊа ĐŧиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐ¸Ņ ĐĩĐēŅ€Đ°ĐŊ Са Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
<string name="revanced_miniplayer_screen_summary">ĐŸŅ€ĐžĐŧŅĐŊа ĐŊа ŅŅ‚Đ¸Đģа ĐŊа ĐŧиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ ĐŋĐģĐĩĐšŅŠŅ€ в ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž</string>
<string name="revanced_miniplayer_type_title">МиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ Ņ‚Đ¸Đŋ ĐĩĐēŅ€Đ°ĐŊ Са ĐŗĐģĐĩдаĐŊĐĩ</string>
<string name="revanced_miniplayer_type_entry_0">ДĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž</string>
<string name="revanced_miniplayer_type_entry_1">По ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ</string>
@@ -1332,8 +1341,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.links.openLinksExternallyPatch">
<string name="revanced_external_browser_title">ĐžŅ‚Đ˛Đ°Ņ€ŅĐŊĐĩ ĐŊа Đ˛Ņ€ŅŠĐˇĐēи в ĐąŅ€Đ°ŅƒĐˇŅŠŅ€Đ°</string>
<string name="revanced_external_browser_summary_on">ĐžŅ‚Đ˛Đ°Ņ€ŅĐŊĐĩ ĐŊа Đ˛ŅŠĐŊ҈ĐŊи Đ˛Ņ€ŅŠĐˇĐēи</string>
<string name="revanced_external_browser_summary_off">ĐžŅ‚Đ˛Đ°Ņ€ŅĐŊĐĩ ĐŊа Đ˛Ņ€ŅŠĐˇĐēи в ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž</string>
<string name="revanced_external_browser_summary_on">ĐžŅ‚Đ˛Đ°Ņ€ŅĐŊĐĩ ĐŊа Đ˛Ņ€ŅŠĐˇĐēи Đ˛ŅŠĐ˛ Đ˛ŅŠĐŊ҈ĐĩĐŊ ĐąŅ€Đ°ŅƒĐˇŅŠŅ€</string>
<string name="revanced_external_browser_summary_off">ĐžŅ‚Đ˛Đ°Ņ€ŅĐŊĐĩ ĐŊа Đ˛Ņ€ŅŠĐˇĐēи Đ˛ŅŠĐ˛ Đ˛ĐŗŅ€Đ°Đ´ĐĩĐŊ ĐąŅ€Đ°ŅƒĐˇŅŠŅ€</string>
</patch>
<patch id="misc.privacy.removeTrackingQueryParameterPatch">
<string name="revanced_remove_tracking_query_parameter_title">ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊĐĩŅ‚Đĩ ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚ŅŠŅ€Đ° ĐŊа ĐˇĐ°ŅĐ˛ĐēĐ°Ņ‚Đ° Са ĐŋŅ€ĐžŅĐģĐĩĐ´ŅĐ˛Đ°ĐŊĐĩ</string>
@@ -1346,10 +1355,11 @@ Second \"item\" text"</string>
<string name="revanced_disable_zoom_haptics_summary_off">Đ’Đ¸ĐąŅ€Đ°Ņ†Đ¸Đ¸Ņ‚Đĩ ŅĐ° аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊи</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">ĐŸŅ€Đ¸ĐŊŅƒĐ´Đ¸Ņ‚ĐĩĐģĐŊĐž ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐž Đ°ŅƒĐ´Đ¸Đž</string>
<string name="revanced_force_original_audio_summary_on">ИСĐŋĐžĐģСваĐŊĐĩ ĐŊа ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐž Đ°ŅƒĐ´Đ¸Đž</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_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 id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

View File

@@ -301,13 +301,12 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_description_components_screen_title">āĻ­āĻŋāĻĄāĻŋāĻ“āϰ āĻŦāĻŋāĻŦāϰāĻŖ</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_summary">āĻĢāĻŋāĻĄ, āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻāĻŦāĻ‚ āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĢāĻŋāĻ˛ā§āϟāĻžāϰ āĻŦāĻžāϰ āϞ⧁āĻ•āĻžāύ āĻŦāĻž āĻĒā§āϰāĻĻāĻ°ā§āĻļāύ āĻ•āϰ⧁āύ</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_off">āĻĢāĻŋāĻĄā§‡ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</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_off">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ⧇ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</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_off">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</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_off">āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
@@ -404,7 +403,7 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<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_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_off">āĻŦā§āϝāĻžāύāĻžāϰ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_end_screen_store_banner_title">āĻļ⧇āώ āĻĒāĻ°ā§āĻĻāĻžāϰ āĻ¸ā§āĻŸā§‹āϰ āĻŦā§āϝāĻžāύāĻžāϰ āϞ⧁āĻ•āĻžāύ</string>
@@ -443,10 +442,10 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_share_copy_url_success">āĻ•ā§āϞāĻŋāĻĒāĻŦā§‹āĻ°ā§āĻĄā§‡ URL āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_share_copy_url_timestamp_success">āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āϏāĻš URL āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_copy_video_url_title">āĻ­āĻŋāĻĄāĻŋāĻ“ URL āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻŦā§‹āϤāĻžāĻŽ āĻĻ⧇āĻ–āĻžāύ</string>
<string name="revanced_copy_video_url_summary_on">āĻŦā§‹āϤāĻžāĻŽ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇āĨ¤ URL āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻ•āϰāϤ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧁āύāĨ¤ āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āϏāĻš URL āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻ•āϰāϤ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧇ āϧāϰ⧇ āϰāĻžāϖ⧁āύāĨ¤</string>
<string name="revanced_copy_video_url_summary_on">āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇āĨ¤ āĻ­āĻŋāĻĄāĻŋāĻ“ URL āĻ•āĻĒāĻŋ āĻ•āϰāϤ⧇ āφāϞāϤ⧋ āϚāĻžāĻĒ⧁āύāĨ¤ āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āϏāĻš āĻ•āĻĒāĻŋ āĻ•āϰāϤ⧇ āϟāĻŋāĻĒ⧁āύ āĻāĻŦāĻ‚ āϧāϰ⧇ āϰāĻžāϖ⧁āύāĨ¤</string>
<string name="revanced_copy_video_url_summary_off">āĻŦā§‹āϤāĻžāĻŽ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§ŸāύāĻŋ</string>
<string name="revanced_copy_video_url_timestamp_title">āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ URL āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻŦā§‹āϤāĻžāĻŽ āĻĻ⧇āĻ–āĻžāύ</string>
<string name="revanced_copy_video_url_timestamp_summary_on">āĻŦā§‹āϤāĻžāĻŽ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇āĨ¤ āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āϏāĻš URL āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻ•āϰāϤ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧁āύāĨ¤ āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āĻ›āĻžā§œāĻž URL āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻ•āϰāϤ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧇ āϧāϰ⧇ āϰāĻžāϖ⧁āύāĨ¤</string>
<string name="revanced_copy_video_url_timestamp_summary_on">āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇āĨ¤ āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āϏāĻš āĻ­āĻŋāĻĄāĻŋāĻ“ URL āĻ•āĻĒāĻŋ āĻ•āϰāϤ⧇ āφāϞāϤ⧋ āϚāĻžāĻĒ⧁āύāĨ¤ āϟāĻžāχāĻŽāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āĻ›āĻžā§œāĻž āĻ•āĻĒāĻŋ āĻ•āϰāϤ⧇ āϟāĻŋāĻĒ⧁āύ āĻāĻŦāĻ‚ āϧāϰ⧇ āϰāĻžāϖ⧁āύāĨ¤</string>
<string name="revanced_copy_video_url_timestamp_summary_off">āĻŦā§‹āϤāĻžāĻŽ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§ŸāύāĻŋ</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
@@ -588,6 +587,9 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
āĻŽāύ⧇ āϰāĻžāĻ–āĻŦ⧇āύ: āĻāϟāĻŋ āϏāĻ•ā§āϰāĻŋāϝāĻŧ āĻ•āϰāĻž āĻ­āĻŋāĻĄāĻŋāĻ“ āĻŦāĻŋāĻœā§āĻžāĻžāĻĒāύāϗ⧁āϞāĻŋāĻ“ āĻœā§‹āϰ āĻ•āϰ⧇ āϞ⧁āĻ•āĻŋāϝāĻŧ⧇ āĻĻ⧇āϝāĻŧ"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">āϤ⧈āϰāĻŋ āĻŦā§‹āϤāĻžāĻŽāϕ⧇ āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋ āĻŦā§‹āϤāĻžāĻŽ āĻĻā§āĻŦāĻžāϰāĻž āϏ⧁āχāϚ āĻ•āϰāĻž āĻšā§ŸāύāĻŋ</string>
<string name="revanced_switch_create_with_notifications_button_user_dialog_message">"āĻāχ āϏ⧇āϟāĻŋāĻ‚āϏ āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋāϝāĻŧ āĻ•āϰāϞ⧇ Shorts āĻŦāĻŋāĻœā§āĻžāĻžāĻĒāύ āĻŦā§āϞāĻ•āĻŋāĻ‚āĻ“ āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋāϝāĻŧ āĻšāϝāĻŧ⧇ āϝāĻžāĻŦ⧇āĨ¤
āϝāĻĻāĻŋ āĻāχ āϏ⧇āϟāĻŋāĻ‚āϏ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāĻžāϰ āĻĒāϰ⧇ āĻ•āĻžāϜ āύāĻž āĻ•āϰ⧇, āϤāĻžāĻšāϞ⧇ Incognito āĻŽā§‹āĻĄā§‡ āĻšā§‡āĻˇā§āϟāĻž āĻ•āϰ⧁āύāĨ¤"</string>
<string name="revanced_hide_navigation_button_labels_title">āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻŦā§‹āϤāĻžāĻŽ āϞ⧇āĻŦ⧇āϞ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_navigation_button_labels_summary_on">āϞ⧇āĻŦ⧇āϞ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_navigation_button_labels_summary_off">āϞ⧇āĻŦ⧇āϞ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
@@ -657,7 +659,7 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">āĻ­āĻŋāĻĄāĻŋāĻ“ āϗ⧁āĻŖāĻŽāĻžāύ āĻŽā§‡āύ⧁ āĻĢ⧁āϟāĻžāϰ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāĻšā§āϛ⧇</string>
</patch>
<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">āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āϞ⧁āĻ•āĻžāύ &amp; āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āĻŦā§‹āϤāĻžāĻŽ</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_cast_button_title">āĻ•āĻžāĻ¸ā§āϟ āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āϞ⧁āĻ•āĻžāύ</string>
@@ -802,10 +804,13 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_shorts_navigation_bar_summary_on">āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻŦāĻžāϰ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_navigation_bar_summary_off">āĻĒāύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻŦāĻžāϰ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<string name="revanced_disable_suggested_video_end_screen_title">āĻ­āĻŋāĻĄāĻŋāĻ“āϰ āĻļ⧇āώ āĻ¸ā§āĻ•ā§āϰāĻŋāϪ⧇ āϏāĻžāĻœā§‡āĻ¸ā§āϟ āĻ•āϰāĻž āĻ­āĻŋāĻĄāĻŋāĻ“ āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋ⧟ āĻ•āϰ⧁āύ</string>
<string name="revanced_disable_suggested_video_end_screen_summary_on">āϏāĻžāĻœā§‡āĻ¸ā§āϟ āĻ•āϰāĻž āĻ­āĻŋāĻĄāĻŋāĻ“ āύāĻŋāĻ¸ā§āĻ•ā§āϰāĻŋ⧟ āĻ•āϰāĻž āĻšāĻŦ⧇</string>
<string name="revanced_disable_suggested_video_end_screen_summary_off">āϏāĻžāĻœā§‡āĻ¸ā§āϟ āĻ•āϰāĻž āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšāĻŦ⧇</string>
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
<string name="revanced_end_screen_suggested_video_title">āĻļ⧇āώ āĻ¸ā§āĻ•ā§āϰ⧀āύ⧇ āĻĒā§āϰāĻ¸ā§āϤāĻžāĻŦāĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“ āϞ⧁āĻ•āĻžāύ⧋ āĻšāĻŦ⧇</string>
<string name="revanced_end_screen_suggested_video_summary_on">"āĻ…āĻŸā§‹ āĻĒā§āϞ⧇ āĻŦāĻ¨ā§āϧ āĻĨāĻžāĻ•āϞ⧇ āĻļ⧇āώ āĻ¸ā§āĻ•ā§āϰ⧀āύ⧇ āĻĒā§āϰāĻ¸ā§āϤāĻžāĻŦāĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“ āϞ⧁āĻ•āĻžāύ⧋ āĻĨāĻžāϕ⧇
YouTube āϏ⧇āϟāĻŋāĻ‚āϏ⧇ āĻ…āĻŸā§‹ āĻĒā§āϞ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:
āϏ⧇āϟāĻŋāĻ‚āϏ → āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• → āĻ…āĻŸā§‹ āĻĒā§āϞ⧇ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ āĻ­āĻŋāĻĄāĻŋāĻ“"</string>
<string name="revanced_end_screen_suggested_video_summary_off">āĻļ⧇āώ āĻ¸ā§āĻ•ā§āϰ⧀āύ⧇ āĻĒā§āϰāĻ¸ā§āϤāĻžāĻŦāĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
<string name="revanced_hide_timestamp_title">āĻ­āĻŋāĻĄāĻŋāĻ“āϰ āϏāĻŽā§ŸāĻ¸ā§āĻŸā§āϝāĻžāĻŽā§āĻĒ āϞ⧁āĻ•āĻžāύ</string>
@@ -1186,7 +1191,7 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">āĻŽāĻŋāύāĻŋāĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ</string>
<string name="revanced_miniplayer_screen_summary">āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻŽāĻ§ā§āϝāĻ•āĻžāϰ āĻŽāĻŋāύāĻŋāĻŽāĻžāχāϜāĻĄ āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ āĻāϰ āϧāϰāĻŖ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧁āύ</string>
<string name="revanced_miniplayer_screen_summary">āχāύ-āĻ…ā§āϝāĻžāĻĒ āĻŽāĻŋāύāĻŋāĻŽāĻžāχāϜāĻĄ āĻĒā§āϞ⧇āϝāĻŧāĻžāϰ⧇āϰ āĻļ⧈āϞ⧀ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧁āύ</string>
<string name="revanced_miniplayer_type_title">āĻŽāĻŋāύāĻŋāĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ āϧāϰāĻŖ</string>
<string name="revanced_miniplayer_type_entry_0">āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋ⧟ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_miniplayer_type_entry_1">āĻĒā§‚āĻ°ā§āĻŦ-āύāĻŋāĻ°ā§āϧāĻžāϰāĻŋāϤ</string>
@@ -1332,8 +1337,8 @@ DeArrow āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇ āĻāĻ–āĻžāύ⧇ āϟ
</patch>
<patch id="misc.links.openLinksExternallyPatch">
<string name="revanced_external_browser_title">āϞāĻŋāĻ‚āĻ• āĻŦā§āϰāĻžāωāϜāĻžāϰ⧇ āϖ⧁āϞ⧁āύ</string>
<string name="revanced_external_browser_summary_on">āϞāĻŋāĻ‚āĻ• āĻŦāĻžāĻšāĻŋāϰ⧇ āϖ⧁āϞ⧁āύ</string>
<string name="revanced_external_browser_summary_off">āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āϞāĻŋāĻ‚āĻ• āϖ⧁āϞāϛ⧇</string>
<string name="revanced_external_browser_summary_on">āĻŦāĻžāĻšā§āϝāĻŋāĻ• āĻŦā§āϰāĻžāωāϜāĻžāϰ⧇ āϞāĻŋāĻ™ā§āĻ• āĻ–ā§‹āϞāĻž āĻšāĻšā§āϛ⧇</string>
<string name="revanced_external_browser_summary_off">āχāύ-āĻ…ā§āϝāĻžāĻĒ āĻŦā§āϰāĻžāωāϜāĻžāϰ⧇ āϞāĻŋāĻ™ā§āĻ• āĻ–ā§‹āϞāĻž āĻšāĻšā§āϛ⧇</string>
</patch>
<patch id="misc.privacy.removeTrackingQueryParameterPatch">
<string name="revanced_remove_tracking_query_parameter_title">āĻŸā§āĻ°ā§āϝāĻžāĻ•āĻŋāĻ‚ āĻ•āϰāĻžāϰ āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāϟāĻžāϰ āĻŽā§āϛ⧁āύ</string>
@@ -1346,10 +1351,11 @@ DeArrow āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇ āĻāĻ–āĻžāύ⧇ āϟ
<string name="revanced_disable_zoom_haptics_summary_off">āĻ•āĻŽā§āĻĒāύ āϏāĻ•ā§āϰāĻŋ⧟ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">āĻŽā§‚āϞ āĻ…āĻĄāĻŋāĻ“ āĻŦāϞāĻĒā§‚āĻ°ā§āĻŦāĻ• āϚāĻžāϞ⧁ āĻ•āϰ⧁āύ</string>
<string name="revanced_force_original_audio_summary_on">āĻŽā§‚āϞ āĻ…āĻĄāĻŋāĻ“ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϛ⧇</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_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 id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

View File

@@ -130,7 +130,7 @@ Second \"item\" text"</string>
<!-- '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. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch">
<patch id="layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch">
</patch>
<patch id="layout.hide.time.hideTimestampPatch">
</patch>
@@ -206,6 +206,7 @@ Second \"item\" text"</string>
<patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<!-- 'Spoof video streams' should be the same translation used for revanced_spoof_video_streams_screen_title -->
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->

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