Compare commits

...

59 Commits

Author SHA1 Message Date
semantic-release-bot
d02a490f36 chore(release): 4.11.0-dev.7 [skip ci]
# [4.11.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.6...v4.11.0-dev.7) (2024-07-10)

### Features

* **Bandcamp:** Add `Remove play limits` patch ([#3366](https://github.com/ReVanced/revanced-patches/issues/3366)) ([f0fb2fa](f0fb2fa3ba))
2024-07-10 22:23:36 +00:00
xob0t
f0fb2fa3ba feat(Bandcamp): Add Remove play limits patch (#3366)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-07-11 00:21:40 +02:00
semantic-release-bot
4885d4ef00 chore(release): 4.11.0-dev.6 [skip ci]
# [4.11.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.5...v4.11.0-dev.6) (2024-07-10)

### Bug Fixes

* **YouTube - SponsorBlock:** Skip segments when casting ([#3331](https://github.com/ReVanced/revanced-patches/issues/3331)) ([a81a6bf](a81a6bf5b2))

### Features

* **Soundcloud:** Add `Hide ads` and `Disable telemetry` patch ([#3386](https://github.com/ReVanced/revanced-patches/issues/3386)) ([a036c1f](a036c1fa0a))
2024-07-10 22:15:51 +00:00
patchink0
a036c1fa0a feat(Soundcloud): Add Hide ads and Disable telemetry patch (#3386)
Co-authored-by: bewzusore <bewzusore>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-07-11 00:13:44 +02:00
Sami Alaoui
a81a6bf5b2 fix(YouTube - SponsorBlock): Skip segments when casting (#3331)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-07-11 00:12:03 +02:00
semantic-release-bot
81836119c0 chore(release): 4.11.0-dev.5 [skip ci]
# [4.11.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.4...v4.11.0-dev.5) (2024-07-05)

### Bug Fixes

* **YouTube - Settings:** Move some settings to different menus, adjust default setting values ([#3415](https://github.com/ReVanced/revanced-patches/issues/3415)) ([def1ec4](def1ec4de6))
2024-07-05 17:40:57 +00:00
LisoUseInAIKyrios
def1ec4de6 fix(YouTube - Settings): Move some settings to different menus, adjust default setting values (#3415) 2024-07-05 21:38:52 +04:00
ReVanced Bot
72f02c8d2f chore: Sync translations (#3400) 2024-07-04 02:05:06 +04:00
semantic-release-bot
5e0dd932cd chore(release): 4.11.0-dev.4 [skip ci]
# [4.11.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.3...v4.11.0-dev.4) (2024-07-01)

### Bug Fixes

* **Windy - Unlock pro:** Revert changing package name ([#3402](https://github.com/ReVanced/revanced-patches/issues/3402)) ([dd6a9f9](dd6a9f977f))
2024-07-01 13:02:34 +00:00
oSumAtrIX
dd6a9f977f fix(Windy - Unlock pro): Revert changing package name (#3402) 2024-07-01 15:00:34 +02:00
semantic-release-bot
7d4c2e820c chore(release): 4.11.0-dev.3 [skip ci]
# [4.11.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.2...v4.11.0-dev.3) (2024-06-30)

### Bug Fixes

* **Windy - Unlock pro:** Use correct package name ([#3397](https://github.com/ReVanced/revanced-patches/issues/3397)) ([e4ae9cc](e4ae9ccd26))
2024-06-30 17:49:56 +00:00
Benedikt Strasser
e4ae9ccd26 fix(Windy - Unlock pro): Use correct package name (#3397) 2024-06-30 19:47:51 +02:00
oSumAtrIX
d469604f0f chore: Add contribution guidelines for translations 2024-06-30 16:16:43 +03:00
semantic-release-bot
1fa7dc83a7 chore(release): 4.11.0-dev.2 [skip ci]
# [4.11.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.1...v4.11.0-dev.2) (2024-06-27)

### Features

* Add translations ([#2963](https://github.com/ReVanced/revanced-patches/issues/2963)) ([f5f0240](f5f024024a))
2024-06-27 20:53:19 +00:00
ReVanced Bot
f5f024024a feat: Add translations (#2963)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2024-06-27 22:51:13 +02:00
oSumAtrIX
c5f0bc41e2 ci: Use correct push path to trigger pushing strings 2024-06-27 22:47:44 +02:00
semantic-release-bot
a80825cbc2 chore(release): 4.11.0-dev.1 [skip ci]
# [4.11.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.10.1-dev.2...v4.11.0-dev.1) (2024-06-27)

### Features

* Add `Remove share targets` patch ([#3334](https://github.com/ReVanced/revanced-patches/issues/3334)) ([70e54f8](70e54f8794))
* **Instagram:** Add `Hide ads` patch ([#3380](https://github.com/ReVanced/revanced-patches/issues/3380)) ([decdff9](decdff9037))
* **RAR:** Add `Hide purchase reminder` patch ([#3321](https://github.com/ReVanced/revanced-patches/issues/3321)) ([55556f3](55556f3efc))
* **Stocard:** Add `Hide offers tab` and `Hide story bubbles` patch ([#3359](https://github.com/ReVanced/revanced-patches/issues/3359)) ([e2f9193](e2f9193aa8))
2024-06-27 20:30:10 +00:00
Twin
55556f3efc feat(RAR): Add Hide purchase reminder patch (#3321)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-27 22:27:59 +02:00
1fexd
70e54f8794 feat: Add Remove share targets patch (#3334)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-27 22:27:29 +02:00
epireyn
e2f9193aa8 feat(Stocard): Add Hide offers tab and Hide story bubbles patch (#3359)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-27 22:26:35 +02:00
Tim
decdff9037 feat(Instagram): Add Hide ads patch (#3380)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-27 22:25:43 +02:00
LisoUseInAIKyrios
8f6519d206 chore: attempt to fix crowdin push 2024-06-27 21:48:46 +03:00
LisoUseInAIKyrios
b0b809d222 chore: Simplify string for localization 2024-06-25 17:06:32 +03:00
semantic-release-bot
979af7ddd0 chore(release): 4.10.1-dev.2 [skip ci]
## [4.10.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.10.1-dev.1...v4.10.1-dev.2) (2024-06-24)

### Bug Fixes

* **Boost for reddit - Fix missing audio in video downloads:** Replace correct strings ([#3379](https://github.com/ReVanced/revanced-patches/issues/3379)) ([0b098a2](0b098a2027))
2024-06-24 17:10:03 +00:00
Yan
0b098a2027 fix(Boost for reddit - Fix missing audio in video downloads): Replace correct strings (#3379) 2024-06-24 19:07:55 +02:00
LisoUseInAIKyrios
2bfd75ce96 chore(YouTube - Spoof client): Update side effects disclaimer 2024-06-24 17:56:16 +03:00
semantic-release-bot
047e6d994b chore(release): 4.10.1-dev.1 [skip ci]
## [4.10.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.10.0...v4.10.1-dev.1) (2024-06-23)

### Bug Fixes

* **YouTube - Hide layout components:** Detect if a keyword filter hides all videos ([#3365](https://github.com/ReVanced/revanced-patches/issues/3365)) ([03eb9c0](03eb9c032a))
2024-06-23 12:49:15 +00:00
LisoUseInAIKyrios
03eb9c032a fix(YouTube - Hide layout components): Detect if a keyword filter hides all videos (#3365) 2024-06-23 15:46:58 +03:00
semantic-release-bot
dfc14c1a26 chore(release): 4.10.0 [skip ci]
# [4.10.0](https://github.com/ReVanced/revanced-patches/compare/v4.9.0...v4.10.0) (2024-06-23)

### Bug Fixes

* Correct invalid string name ([7fa7ef6](7fa7ef6c4f))
* **YouTube - Client spoof:** Correctly play more livestreams using Android VR ([#3316](https://github.com/ReVanced/revanced-patches/issues/3316)) ([d5c0b5e](d5c0b5ed65))
* **YouTube - Hide description components:** Replace `Hide game section` and `Hide music section` with `Hide attributes section` ([#3327](https://github.com/ReVanced/revanced-patches/issues/3327)) ([e5ced63](e5ced6310d))
* **YouTube Music:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3315](https://github.com/ReVanced/revanced-patches/issues/3315)) ([b78b7cf](b78b7cfe6c))
* **YouTube:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3314](https://github.com/ReVanced/revanced-patches/issues/3314)) ([4919cba](4919cba478))

### Features

* Add `Change version code` patch ([#3338](https://github.com/ReVanced/revanced-patches/issues/3338)) ([3cfe745](3cfe745d59))
* **Boost For Reddit:** Add `Fix /s/ links` patch ([#3154](https://github.com/ReVanced/revanced-patches/issues/3154)) ([b575fc6](b575fc68ff))
* **Boost for Reddit:** Add `Fix audio missing in video downloads` patch ([#3287](https://github.com/ReVanced/revanced-patches/issues/3287)) ([2ea583c](2ea583c220))
* **YouTube - Comments:** Add `Hide 'Create a Short' button` option ([#3333](https://github.com/ReVanced/revanced-patches/issues/3333)) ([f0cc415](f0cc415199))
* **YouTube - Comments:** Add `Hide Thanks button` and `Hide 'Comments by members' header` options ([#3317](https://github.com/ReVanced/revanced-patches/issues/3317)) ([e240921](e2409213d4))
* **YouTube - Miniplayer:** Rename `Tablet mini player` and allow selecting the style of the in-app miniplayer ([#3302](https://github.com/ReVanced/revanced-patches/issues/3302)) ([70013d8](70013d813b))
2024-06-23 12:13:53 +00:00
oSumAtrIX
20dedb2605 chore: Merge branch dev to main (#3300) 2024-06-23 14:11:45 +02:00
semantic-release-bot
cb7a283be1 chore(release): 4.10.0-dev.10 [skip ci]
# [4.10.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.9...v4.10.0-dev.10) (2024-06-18)

### Bug Fixes

* Correct invalid string name ([7fa7ef6](7fa7ef6c4f))
2024-06-18 00:47:34 +00:00
oSumAtrIX
7fa7ef6c4f fix: Correct invalid string name 2024-06-18 02:45:42 +02:00
semantic-release-bot
5a93660825 chore(release): 4.10.0-dev.9 [skip ci]
# [4.10.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.8...v4.10.0-dev.9) (2024-06-17)

### Bug Fixes

* **YouTube - Hide description components:** Replace `Hide game section` and `Hide music section` with `Hide attributes section` ([#3327](https://github.com/ReVanced/revanced-patches/issues/3327)) ([e5ced63](e5ced6310d))
2024-06-17 22:55:53 +00:00
ILoveOpenSourceApplications
e5ced6310d fix(YouTube - Hide description components): Replace Hide game section and Hide music section with Hide attributes section (#3327)
Co-authored-by: ILoveOpenSourceApplications <ILoveOpenSourceApplications@users.noreply.github.com>
2024-06-18 00:53:56 +02:00
semantic-release-bot
1147094241 chore(release): 4.10.0-dev.8 [skip ci]
# [4.10.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.7...v4.10.0-dev.8) (2024-06-17)

### Features

* **YouTube - Comments:** Add `Hide 'Create a Short' button` option ([#3333](https://github.com/ReVanced/revanced-patches/issues/3333)) ([f0cc415](f0cc415199))
2024-06-17 22:51:07 +00:00
ILoveOpenSourceApplications
f0cc415199 feat(YouTube - Comments): Add Hide 'Create a Short' button option (#3333)
Co-authored-by: ILoveOpenSourceApplications <ILoveOpenSourceApplications@users.noreply.github.com>
2024-06-18 00:49:08 +02:00
semantic-release-bot
c0f8ddac52 chore(release): 4.10.0-dev.7 [skip ci]
# [4.10.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.6...v4.10.0-dev.7) (2024-06-15)

### Features

* Add `Change version code` patch ([#3338](https://github.com/ReVanced/revanced-patches/issues/3338)) ([3cfe745](3cfe745d59))
2024-06-15 16:58:02 +00:00
1fexd
3cfe745d59 feat: Add Change version code patch (#3338)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-15 19:56:01 +03:00
semantic-release-bot
550ac09e32 chore(release): 4.10.0-dev.6 [skip ci]
# [4.10.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.5...v4.10.0-dev.6) (2024-06-09)

### Features

* **YouTube - Comments:** Add `Hide Thanks button` and `Hide 'Comments by members' header` options ([#3317](https://github.com/ReVanced/revanced-patches/issues/3317)) ([e240921](e2409213d4))
2024-06-09 09:17:27 +00:00
ILoveOpenSourceApplications
e2409213d4 feat(YouTube - Comments): Add Hide Thanks button and Hide 'Comments by members' header options (#3317) 2024-06-09 12:15:08 +03:00
semantic-release-bot
a8b14d560f chore(release): 4.10.0-dev.5 [skip ci]
# [4.10.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.4...v4.10.0-dev.5) (2024-06-09)

### Bug Fixes

* **YouTube - Client spoof:** Correctly play more livestreams using Android VR ([#3316](https://github.com/ReVanced/revanced-patches/issues/3316)) ([d5c0b5e](d5c0b5ed65))
2024-06-09 08:33:19 +00:00
LisoUseInAIKyrios
d5c0b5ed65 fix(YouTube - Client spoof): Correctly play more livestreams using Android VR (#3316) 2024-06-09 11:31:14 +03:00
semantic-release-bot
343fdec796 chore(release): 4.10.0-dev.4 [skip ci]
# [4.10.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.3...v4.10.0-dev.4) (2024-06-08)

### Features

* **Boost for Reddit:** Add `Fix audio missing in video downloads` patch ([#3287](https://github.com/ReVanced/revanced-patches/issues/3287)) ([2ea583c](2ea583c220))
2024-06-08 16:44:45 +00:00
Yan
2ea583c220 feat(Boost for Reddit): Add Fix audio missing in video downloads patch (#3287)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-08 18:42:45 +02:00
semantic-release-bot
8dda8988ef chore(release): 4.10.0-dev.3 [skip ci]
# [4.10.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.2...v4.10.0-dev.3) (2024-06-08)

### Features

* **Boost For Reddit:** Add `Fix /s/ links` patch ([#3154](https://github.com/ReVanced/revanced-patches/issues/3154)) ([b575fc6](b575fc68ff))
2024-06-08 16:33:11 +00:00
Yan
b575fc68ff feat(Boost For Reddit): Add Fix /s/ links patch (#3154)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-08 18:31:18 +02:00
semantic-release-bot
c979e92676 chore(release): 4.10.0-dev.2 [skip ci]
# [4.10.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.1...v4.10.0-dev.2) (2024-06-08)

### Bug Fixes

* **YouTube Music:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3315](https://github.com/ReVanced/revanced-patches/issues/3315)) ([b78b7cf](b78b7cfe6c))
* **YouTube:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3314](https://github.com/ReVanced/revanced-patches/issues/3314)) ([4919cba](4919cba478))
2024-06-08 08:37:57 +00:00
LisoUseInAIKyrios
b78b7cfe6c fix(YouTube Music): Rename Minimized playback to Remove background playback restrictions (#3315) 2024-06-08 11:35:44 +03:00
LisoUseInAIKyrios
4919cba478 fix(YouTube): Rename Minimized playback to Remove background playback restrictions (#3314) 2024-06-08 11:35:20 +03:00
semantic-release-bot
1d46b95698 chore(release): 4.10.0-dev.1 [skip ci]
# [4.10.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.9.0...v4.10.0-dev.1) (2024-06-07)

### Features

* **YouTube - Miniplayer:** Rename `Tablet mini player` and allow selecting the style of the in-app miniplayer ([#3302](https://github.com/ReVanced/revanced-patches/issues/3302)) ([70013d8](70013d813b))
2024-06-07 22:50:16 +00:00
LisoUseInAIKyrios
70013d813b feat(YouTube - Miniplayer): Rename Tablet mini player and allow selecting the style of the in-app miniplayer (#3302)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-08 01:48:12 +03:00
KobeW50
8a33fe9986 docs: Fix broken link (#3313)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-07 22:20:07 +02:00
LisoUseInAIKyrios
ee21a2bb91 chore(YouTube - Spoof client): Update iOS summary disclaimer 2024-06-04 06:43:18 +03:00
semantic-release-bot
ea67466bd5 chore(release): 4.9.0 [skip ci]
# [4.9.0](https://github.com/ReVanced/revanced-patches/compare/v4.8.3...v4.9.0) (2024-06-02)

### Bug Fixes

* **YouTube - Spoof client:** Allow swipe gestures to enter/exit fullscreen when spoofing with `Android VR` client ([#3259](https://github.com/ReVanced/revanced-patches/issues/3259)) ([ea1deb6](ea1deb630e))
* **YouTube - Spoof client:** Restore playback speed menu when spoofing to an iOS client ([61401c9](61401c9ea4))

### Features

* **Messenger:** Add `Hide inbox subtabs` patch ([#3163](https://github.com/ReVanced/revanced-patches/issues/3163)) ([a331f0a](a331f0a30b))
* **YouTube - Hide layout components:** Disable like / subscribe button glow animation ([#3265](https://github.com/ReVanced/revanced-patches/issues/3265)) ([367a83a](367a83accd))
* **YouTube - Playback speed:** Add option to show speed dialog button in video player ([#3197](https://github.com/ReVanced/revanced-patches/issues/3197)) ([8e67cdc](8e67cdca55))
* **YouTube Music:** Support version `7.03` ([#3272](https://github.com/ReVanced/revanced-patches/issues/3272)) ([6c25c95](6c25c95747))
* **YouTube:** Support version `19.12`, `19.13`, `19.14`, `19.15` and `19.16` ([#3239](https://github.com/ReVanced/revanced-patches/issues/3239)) ([868f51d](868f51d992))
2024-06-02 17:43:45 +00:00
oSumAtrIX
320e039a9f chore: Merge branch dev to main (#3266) 2024-06-02 19:41:49 +02:00
semantic-release-bot
6b9c13d92c chore(release): 4.9.0-dev.7 [skip ci]
# [4.9.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.6...v4.9.0-dev.7) (2024-06-02)

### Features

* **YouTube - Playback speed:** Add option to show speed dialog button in video player ([#3197](https://github.com/ReVanced/revanced-patches/issues/3197)) ([8e67cdc](8e67cdca55))
2024-06-02 15:50:45 +00:00
MarcaD
8e67cdca55 feat(YouTube - Playback speed): Add option to show speed dialog button in video player (#3197)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-06-02 17:48:35 +02:00
semantic-release-bot
32c5fa4d04 chore(release): 4.9.0-dev.6 [skip ci]
# [4.9.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.5...v4.9.0-dev.6) (2024-06-02)

### Bug Fixes

* **YouTube - Spoof client:** Restore playback speed menu when spoofing to an iOS client ([61401c9](61401c9ea4))
2024-06-02 15:46:01 +00:00
oSumAtrIX
61401c9ea4 fix(YouTube - Spoof client): Restore playback speed menu when spoofing to an iOS client 2024-06-02 17:43:41 +02:00
170 changed files with 54843 additions and 604 deletions

View File

@@ -20,11 +20,11 @@ jobs:
- name: Open pull request
uses: repo-sync/pull-request@v2
with:
destination_branch: 'main'
pr_title: 'chore: ${{ env.MESSAGE }}'
destination_branch: "main"
pr_title: "chore: ${{ env.MESSAGE }}"
pr_body: |
This pull request will ${{ env.MESSAGE }}.
## Dependencies before merge
- [ ] https://github.com/revanced/revanced-integrations

View File

@@ -1,34 +1,35 @@
name: Pull strings
on:
workflow_dispatch:
schedule:
- cron: 0 0 1 * *
workflow_dispatch:
schedule:
- cron: 0 0 1 * *
jobs:
pull:
name: Pull strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
pull:
name: Pull strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: dev
- name: Pull strings
uses: crowdin/github-action@v1
with:
config: crowdin.yml
download_translations: true
localization_branch_name: feat/translations
create_pull_request: true
pull_request_title: "chore: Sync translations"
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
pull_request_base_branch_name: "dev"
commit_message: "chore: Sync translations"
github_user_name: revanced-bot
github_user_email: github@revanced.app
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: Pull strings
uses: crowdin/github-action@v1
with:
config: crowdin.yml
download_translations: true
localization_branch_name: feat/translations
create_pull_request: true
pull_request_title: "chore: Sync translations"
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
pull_request_base_branch_name: "dev"
commit_message: "chore: Sync translations"
github_user_name: revanced-bot
github_user_email: github@revanced.app
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@@ -1,27 +1,29 @@
name: Push strings
on:
workflow_dispatch:
push:
paths:
- /src/main/resources/addresources/values/strings.xml
workflow_dispatch:
push:
branches:
- dev
paths:
- src/main/resources/addresources/values/strings.xml
jobs:
push:
name: Push strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
push:
name: Push strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Push strings
uses: crowdin/github-action@v1
with:
config: crowdin.yml
upload_sources: true
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: Push strings
uses: crowdin/github-action@v1
with:
config: crowdin.yml
upload_sources: true
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@@ -1,3 +1,197 @@
# [4.11.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.6...v4.11.0-dev.7) (2024-07-10)
### Features
* **Bandcamp:** Add `Remove play limits` patch ([#3366](https://github.com/ReVanced/revanced-patches/issues/3366)) ([ad8d3bb](https://github.com/ReVanced/revanced-patches/commit/ad8d3bb1c86f1324234e890f1171ec4a18e56dd9))
# [4.11.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.5...v4.11.0-dev.6) (2024-07-10)
### Bug Fixes
* **YouTube - SponsorBlock:** Skip segments when casting ([#3331](https://github.com/ReVanced/revanced-patches/issues/3331)) ([d9395fd](https://github.com/ReVanced/revanced-patches/commit/d9395fdbca45cf68fbc63469e228eefbd6c2bf2e))
### Features
* **Soundcloud:** Add `Hide ads` and `Disable telemetry` patch ([#3386](https://github.com/ReVanced/revanced-patches/issues/3386)) ([3c79f3d](https://github.com/ReVanced/revanced-patches/commit/3c79f3d34d978aead60de19e64d05fbebc1bc73a))
# [4.11.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.4...v4.11.0-dev.5) (2024-07-05)
### Bug Fixes
* **YouTube - Settings:** Move some settings to different menus, adjust default setting values ([#3415](https://github.com/ReVanced/revanced-patches/issues/3415)) ([7201ac4](https://github.com/ReVanced/revanced-patches/commit/7201ac45c158682413c8584aac7bb37b770fc924))
# [4.11.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.3...v4.11.0-dev.4) (2024-07-01)
### Bug Fixes
* **Windy - Unlock pro:** Revert changing package name ([#3402](https://github.com/ReVanced/revanced-patches/issues/3402)) ([541f1e7](https://github.com/ReVanced/revanced-patches/commit/541f1e702665630a3737f67a4cc0c4f7b4899f8f))
# [4.11.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.2...v4.11.0-dev.3) (2024-06-30)
### Bug Fixes
* **Windy - Unlock pro:** Use correct package name ([#3397](https://github.com/ReVanced/revanced-patches/issues/3397)) ([1d8459a](https://github.com/ReVanced/revanced-patches/commit/1d8459ac992b12371c41df3c25b6386119770d15))
# [4.11.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.11.0-dev.1...v4.11.0-dev.2) (2024-06-27)
### Features
* Add translations ([#2963](https://github.com/ReVanced/revanced-patches/issues/2963)) ([69ea6f3](https://github.com/ReVanced/revanced-patches/commit/69ea6f3bc2b5f419320f17c150489dcb9eed76ce))
# [4.11.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.10.1-dev.2...v4.11.0-dev.1) (2024-06-27)
### Features
* Add `Remove share targets` patch ([#3334](https://github.com/ReVanced/revanced-patches/issues/3334)) ([9414122](https://github.com/ReVanced/revanced-patches/commit/94141228163aee8d051491db51fc1e4c8b86f0e6))
* **Instagram:** Add `Hide ads` patch ([#3380](https://github.com/ReVanced/revanced-patches/issues/3380)) ([c6b2f8c](https://github.com/ReVanced/revanced-patches/commit/c6b2f8c0172b4fd142927d9448ed855831c86ae4))
* **RAR:** Add `Hide purchase reminder` patch ([#3321](https://github.com/ReVanced/revanced-patches/issues/3321)) ([8fbe7e3](https://github.com/ReVanced/revanced-patches/commit/8fbe7e3d38c43adfa0755bcbe87f8c6b7696da3a))
* **Stocard:** Add `Hide offers tab` and `Hide story bubbles` patch ([#3359](https://github.com/ReVanced/revanced-patches/issues/3359)) ([fbd0507](https://github.com/ReVanced/revanced-patches/commit/fbd0507ce5cdeb93a1f661aa8097139c61e643a0))
## [4.10.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.10.1-dev.1...v4.10.1-dev.2) (2024-06-24)
### Bug Fixes
* **Boost for reddit - Fix missing audio in video downloads:** Replace correct strings ([#3379](https://github.com/ReVanced/revanced-patches/issues/3379)) ([b43db98](https://github.com/ReVanced/revanced-patches/commit/b43db98e8483e2939d8fb9cd02443f24aeaae3c3))
## [4.10.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.10.0...v4.10.1-dev.1) (2024-06-23)
### Bug Fixes
* **YouTube - Hide layout components:** Detect if a keyword filter hides all videos ([#3365](https://github.com/ReVanced/revanced-patches/issues/3365)) ([6aa47ec](https://github.com/ReVanced/revanced-patches/commit/6aa47ec1050cf32158ab608441c0649501184971))
# [4.10.0](https://github.com/ReVanced/revanced-patches/compare/v4.9.0...v4.10.0) (2024-06-23)
### Bug Fixes
* Correct invalid string name ([b84494f](https://github.com/ReVanced/revanced-patches/commit/b84494f4e26e040ada69ed7a516f331f2d47da87))
* **YouTube - Client spoof:** Correctly play more livestreams using Android VR ([#3316](https://github.com/ReVanced/revanced-patches/issues/3316)) ([c05264a](https://github.com/ReVanced/revanced-patches/commit/c05264af3944cbfe8d9aa34fb0e0fddb05a1d42f))
* **YouTube - Hide description components:** Replace `Hide game section` and `Hide music section` with `Hide attributes section` ([#3327](https://github.com/ReVanced/revanced-patches/issues/3327)) ([0198a43](https://github.com/ReVanced/revanced-patches/commit/0198a436f97b127a2a5dd283644254f9a0ae3e43))
* **YouTube Music:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3315](https://github.com/ReVanced/revanced-patches/issues/3315)) ([3c31e55](https://github.com/ReVanced/revanced-patches/commit/3c31e55b13d9495e857f068f8cd2b4320112d763))
* **YouTube:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3314](https://github.com/ReVanced/revanced-patches/issues/3314)) ([37d415b](https://github.com/ReVanced/revanced-patches/commit/37d415b53af4771d9c97a8b1c153be32bf3ac2e0))
### Features
* Add `Change version code` patch ([#3338](https://github.com/ReVanced/revanced-patches/issues/3338)) ([685ef39](https://github.com/ReVanced/revanced-patches/commit/685ef39119daf1033a83262982519531c481c40f))
* **Boost For Reddit:** Add `Fix /s/ links` patch ([#3154](https://github.com/ReVanced/revanced-patches/issues/3154)) ([5fa9fd2](https://github.com/ReVanced/revanced-patches/commit/5fa9fd2dfef43838d7311a967a3e805256a5d116))
* **Boost for Reddit:** Add `Fix audio missing in video downloads` patch ([#3287](https://github.com/ReVanced/revanced-patches/issues/3287)) ([a9258d4](https://github.com/ReVanced/revanced-patches/commit/a9258d48d3ddf8552ab56219677a3b31ee553666))
* **YouTube - Comments:** Add `Hide 'Create a Short' button` option ([#3333](https://github.com/ReVanced/revanced-patches/issues/3333)) ([be9e244](https://github.com/ReVanced/revanced-patches/commit/be9e24420fda80903e44e2e2278ea4904ecac4e1))
* **YouTube - Comments:** Add `Hide Thanks button` and `Hide 'Comments by members' header` options ([#3317](https://github.com/ReVanced/revanced-patches/issues/3317)) ([9c4c4f0](https://github.com/ReVanced/revanced-patches/commit/9c4c4f05a762d745404101bbc3925ab4eba2deb8))
* **YouTube - Miniplayer:** Rename `Tablet mini player` and allow selecting the style of the in-app miniplayer ([#3302](https://github.com/ReVanced/revanced-patches/issues/3302)) ([5511736](https://github.com/ReVanced/revanced-patches/commit/5511736b0c5dd409db6a68db0f85e389bb95be47))
# [4.10.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.9...v4.10.0-dev.10) (2024-06-18)
### Bug Fixes
* Correct invalid string name ([b84494f](https://github.com/ReVanced/revanced-patches/commit/b84494f4e26e040ada69ed7a516f331f2d47da87))
# [4.10.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.8...v4.10.0-dev.9) (2024-06-17)
### Bug Fixes
* **YouTube - Hide description components:** Replace `Hide game section` and `Hide music section` with `Hide attributes section` ([#3327](https://github.com/ReVanced/revanced-patches/issues/3327)) ([0198a43](https://github.com/ReVanced/revanced-patches/commit/0198a436f97b127a2a5dd283644254f9a0ae3e43))
# [4.10.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.7...v4.10.0-dev.8) (2024-06-17)
### Features
* **YouTube - Comments:** Add `Hide 'Create a Short' button` option ([#3333](https://github.com/ReVanced/revanced-patches/issues/3333)) ([be9e244](https://github.com/ReVanced/revanced-patches/commit/be9e24420fda80903e44e2e2278ea4904ecac4e1))
# [4.10.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.6...v4.10.0-dev.7) (2024-06-15)
### Features
* Add `Change version code` patch ([#3338](https://github.com/ReVanced/revanced-patches/issues/3338)) ([685ef39](https://github.com/ReVanced/revanced-patches/commit/685ef39119daf1033a83262982519531c481c40f))
# [4.10.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.5...v4.10.0-dev.6) (2024-06-09)
### Features
* **YouTube - Comments:** Add `Hide Thanks button` and `Hide 'Comments by members' header` options ([#3317](https://github.com/ReVanced/revanced-patches/issues/3317)) ([9c4c4f0](https://github.com/ReVanced/revanced-patches/commit/9c4c4f05a762d745404101bbc3925ab4eba2deb8))
# [4.10.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.4...v4.10.0-dev.5) (2024-06-09)
### Bug Fixes
* **YouTube - Client spoof:** Correctly play more livestreams using Android VR ([#3316](https://github.com/ReVanced/revanced-patches/issues/3316)) ([c05264a](https://github.com/ReVanced/revanced-patches/commit/c05264af3944cbfe8d9aa34fb0e0fddb05a1d42f))
# [4.10.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.3...v4.10.0-dev.4) (2024-06-08)
### Features
* **Boost for Reddit:** Add `Fix audio missing in video downloads` patch ([#3287](https://github.com/ReVanced/revanced-patches/issues/3287)) ([a9258d4](https://github.com/ReVanced/revanced-patches/commit/a9258d48d3ddf8552ab56219677a3b31ee553666))
# [4.10.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.2...v4.10.0-dev.3) (2024-06-08)
### Features
* **Boost For Reddit:** Add `Fix /s/ links` patch ([#3154](https://github.com/ReVanced/revanced-patches/issues/3154)) ([5fa9fd2](https://github.com/ReVanced/revanced-patches/commit/5fa9fd2dfef43838d7311a967a3e805256a5d116))
# [4.10.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.1...v4.10.0-dev.2) (2024-06-08)
### Bug Fixes
* **YouTube Music:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3315](https://github.com/ReVanced/revanced-patches/issues/3315)) ([3c31e55](https://github.com/ReVanced/revanced-patches/commit/3c31e55b13d9495e857f068f8cd2b4320112d763))
* **YouTube:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3314](https://github.com/ReVanced/revanced-patches/issues/3314)) ([37d415b](https://github.com/ReVanced/revanced-patches/commit/37d415b53af4771d9c97a8b1c153be32bf3ac2e0))
# [4.10.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.9.0...v4.10.0-dev.1) (2024-06-07)
### Features
* **YouTube - Miniplayer:** Rename `Tablet mini player` and allow selecting the style of the in-app miniplayer ([#3302](https://github.com/ReVanced/revanced-patches/issues/3302)) ([5511736](https://github.com/ReVanced/revanced-patches/commit/5511736b0c5dd409db6a68db0f85e389bb95be47))
# [4.9.0](https://github.com/ReVanced/revanced-patches/compare/v4.8.3...v4.9.0) (2024-06-02)
### Bug Fixes
* **YouTube - Spoof client:** Allow swipe gestures to enter/exit fullscreen when spoofing with `Android VR` client ([#3259](https://github.com/ReVanced/revanced-patches/issues/3259)) ([5114900](https://github.com/ReVanced/revanced-patches/commit/5114900b1b5572c04ba6759eedab77f0a934b058))
* **YouTube - Spoof client:** Restore playback speed menu when spoofing to an iOS client ([95f290f](https://github.com/ReVanced/revanced-patches/commit/95f290f1139cc8679beecac53c623847668f885e))
### Features
* **Messenger:** Add `Hide inbox subtabs` patch ([#3163](https://github.com/ReVanced/revanced-patches/issues/3163)) ([24e4ebd](https://github.com/ReVanced/revanced-patches/commit/24e4ebd77ad0f349b479926bf3983b72c2683496))
* **YouTube - Hide layout components:** Disable like / subscribe button glow animation ([#3265](https://github.com/ReVanced/revanced-patches/issues/3265)) ([68d35ea](https://github.com/ReVanced/revanced-patches/commit/68d35eafc15513c23cd5220260023e7ec5b7978a))
* **YouTube - Playback speed:** Add option to show speed dialog button in video player ([#3197](https://github.com/ReVanced/revanced-patches/issues/3197)) ([ad00305](https://github.com/ReVanced/revanced-patches/commit/ad00305ff57d5e8041de7375bea7d3ad6f18c4e2))
* **YouTube Music:** Support version `7.03` ([#3272](https://github.com/ReVanced/revanced-patches/issues/3272)) ([d1ceca3](https://github.com/ReVanced/revanced-patches/commit/d1ceca39984f7933b28d81802d04bb3ead327595))
* **YouTube:** Support version `19.12`, `19.13`, `19.14`, `19.15` and `19.16` ([#3239](https://github.com/ReVanced/revanced-patches/issues/3239)) ([99b07e0](https://github.com/ReVanced/revanced-patches/commit/99b07e0e18574668f36bb3c962c8d11222114be4))
# [4.9.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.6...v4.9.0-dev.7) (2024-06-02)
### Features
* **YouTube - Playback speed:** Add option to show speed dialog button in video player ([#3197](https://github.com/ReVanced/revanced-patches/issues/3197)) ([ad00305](https://github.com/ReVanced/revanced-patches/commit/ad00305ff57d5e8041de7375bea7d3ad6f18c4e2))
# [4.9.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.5...v4.9.0-dev.6) (2024-06-02)
### Bug Fixes
* **YouTube - Spoof client:** Restore playback speed menu when spoofing to an iOS client ([95f290f](https://github.com/ReVanced/revanced-patches/commit/95f290f1139cc8679beecac53c623847668f885e))
# [4.9.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.4...v4.9.0-dev.5) (2024-06-01)

View File

@@ -83,6 +83,10 @@ Features can be requested by opening an issue using the
If you encounter a bug while using ReVanced Patches, open an issue using the
[Bug report issue template](https://github.com/ReVanced/revanced-patches/issues/new?assignees=&labels=Bug+report&projects=&template=bug_report.yml&title=bug%3A+).
## 🌐 Submitting translations
You can contribute translations at [translate.revanced.app](https://translate.revanced.app).
## 🧑‍⚖️ Guidelines for requesting or contributing patches
To maintain a high-quality and ethical collection of patches, the following guidelines for requesting
@@ -107,7 +111,6 @@ are unaffected by this change.
* Payment circumvention: We do not accept patches that exist solely to bypass payment for the app or any of its features
* Malicious patches: Patches that are malicious in nature are not allowed
## 📝 How to contribute
1. Before contributing, it is recommended to open an issue to discuss your change
@@ -115,11 +118,11 @@ with the maintainers of ReVanced Patches. This will help you determine whether y
and whether it is worth your time to implement it
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
3. Commit your changes. In case you are contributing a new patch, make sure to follow the conventions for patches
described in the [documentation](https://github.com/ReVanced/revanced-patches/tree/docs/docs)
described in the [ReVanced Patcher documentation](https://github.com/ReVanced/revanced-patcher/tree/main/docs)
4. Submit a pull request to the `dev` branch of the repository and reference issues
that your pull request closes in the description of your pull request
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
it will be merged into the `dev` branch and will be included in the next release of ReVanced Patches
❤️ Thank you for considering contributing to ReVanced Patches,
ReVanced

View File

@@ -105,6 +105,12 @@ public final class app/revanced/patches/all/misc/transformation/IMethodCall$Defa
public static fun replaceInvokeVirtualWithIntegrations (Lapp/revanced/patches/all/misc/transformation/IMethodCall;Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V
}
public final class app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
public static final field INSTANCE Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch;
public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
@@ -149,6 +155,12 @@ public final class app/revanced/patches/all/screenshot/removerestriction/RemoveS
public static fun values ()[Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall;
}
public final class app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/all/telephony/sim/spoof/SpoofSimCountryPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
public static final field INSTANCE Lapp/revanced/patches/all/telephony/sim/spoof/SpoofSimCountryPatch;
public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
@@ -169,6 +181,12 @@ public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatch : app/
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/candylinkvpn/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/candylinkvpn/UnlockProPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -235,6 +253,12 @@ public final class app/revanced/patches/inshorts/ad/HideAdsPatch : app/revanced/
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/instagram/patches/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/instagram/patches/ad/HideAdsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/instagram/patches/ads/timeline/HideTimelineAdsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/instagram/patches/ads/timeline/HideTimelineAdsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -403,6 +427,12 @@ public final class app/revanced/patches/music/misc/androidauto/BypassCertificate
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/misc/gms/Constants {
public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/Constants;
}
@@ -511,6 +541,16 @@ public final class app/revanced/patches/pixiv/ads/HideAdsPatch : app/revanced/pa
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/fingerprints/ShowReminderFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
public static final field INSTANCE Lapp/revanced/patches/rar/misc/annoyances/purchasereminder/fingerprints/ShowReminderFingerprint;
}
public final class app/revanced/patches/reddit/ad/banner/HideBannerPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/ad/banner/HideBannerPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
@@ -529,6 +569,18 @@ public final class app/revanced/patches/reddit/ad/general/HideAdsPatch : app/rev
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public abstract class app/revanced/patches/reddit/customclients/BaseFixSLinksPatch : app/revanced/patcher/patch/BytecodePatch {
public fun <init> (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Ljava/util/Set;)V
public synthetic fun <init> (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
protected abstract fun getIntegrationsClassDescriptor ()Ljava/lang/String;
protected final fun getResolveSLinkMethod ()Ljava/lang/String;
protected final fun getSetAccessTokenMethod ()Ljava/lang/String;
protected abstract fun patchNavigationHandler (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;Lapp/revanced/patcher/data/BytecodeContext;)V
protected abstract fun patchSetAccessToken (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;Lapp/revanced/patcher/data/BytecodeContext;)V
}
public abstract class app/revanced/patches/reddit/customclients/BaseSpoofClientPatch : app/revanced/patcher/patch/BytecodePatch {
public fun <init> (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@@ -564,6 +616,20 @@ public final class app/revanced/patches/reddit/customclients/boostforreddit/api/
public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
}
public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch : app/revanced/patches/reddit/customclients/BaseFixSLinksPatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch;
}
public final class app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch;
}
public final class app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch;
public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -642,10 +708,12 @@ public final class app/revanced/patches/reddit/customclients/syncforreddit/detec
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch : app/revanced/patcher/patch/BytecodePatch {
public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch : app/revanced/patches/reddit/customclients/BaseFixSLinksPatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch;
}
public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch : app/revanced/patcher/patch/BytecodePatch {
@@ -970,6 +1038,18 @@ public final class app/revanced/patches/songpal/badge/RemoveNotificationBadgePat
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/soundcloud/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/soundcloud/ad/HideAdsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/soundcloud/analytics/DisableTelemetryPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/spotify/layout/theme/CustomThemePatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/spotify/layout/theme/CustomThemePatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
@@ -994,6 +1074,18 @@ public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabResourceP
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/stocard/layout/HideOffersTabPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/stocard/layout/HideOffersTabPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/stocard/layout/HideStoryBubblesPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/strava/subscription/UnlockSubscriptionPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -1528,6 +1620,12 @@ public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPa
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -1632,6 +1730,12 @@ public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/misc/debugging/DebuggingPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/debugging/DebuggingPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
@@ -1840,6 +1944,12 @@ public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch :
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@@ -1888,6 +1998,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
public final class app/revanced/util/BytecodeUtilsKt {
public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException;
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I
@@ -1895,6 +2006,7 @@ public final class app/revanced/util/BytecodeUtilsKt {
public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 4.9.0-dev.5
version = 4.11.0-dev.7

View File

@@ -13,7 +13,6 @@ import app.revanced.util.resource.BaseResource
import app.revanced.util.resource.StringResource
import org.w3c.dom.Node
import java.io.Closeable
import java.util.*
/**
* An identifier of an app. For example, `youtube`.
@@ -55,6 +54,95 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
*/
private lateinit var resources: Map<Value, Resources>
/**
* Map of Crowdin locales to Android resource locale names.
*
* Fixme: Instead this patch should detect what locale regions are present in both patches and the target app,
* and automatically merge into the appropriate existing target file.
* So if a target app has only 'es', then the Crowdin file of 'es-rES' should merge into that.
* But if a target app has specific regions (such as 'pt-rBR'),
* then the Crowdin region specific file should merged into that.
*/
private val locales = mapOf(
"af-rZA" to "af",
"am-rET" to "am",
"ar-rSA" to "ar",
"as-rIN" to "as",
"az-rAZ" to "az",
"be-rBY" to "be",
"bg-rBG" to "bg",
"bn-rBD" to "bn",
"bs-rBA" to "bs",
"ca-rES" to "ca",
"cs-rCZ" to "cs",
"da-rDK" to "da",
"de-rDE" to "de",
"el-rGR" to "el",
"es-rES" to "es",
"et-rEE" to "et",
"eu-rES" to "eu",
"fa-rIR" to "fa",
"fi-rFI" to "fi",
"tl-rPH" to "tl",
"fr-rFR" to "fr",
"gl-rES" to "gl",
"gu-rIN" to "gu",
"hi-rIN" to "hi",
"hr-rHR" to "hr",
"hu-rHU" to "hu",
"hy-rAM" to "hy",
"in-rID" to "in",
"is-rIS" to "is",
"it-rIT" to "it",
"iw-rIL" to "iw",
"ja-rJP" to "ja",
"ka-rGE" to "ka",
"kk-rKZ" to "kk",
"km-rKH" to "km",
"kn-rIN" to "kn",
"ko-rKR" to "ko",
"ky-rKG" to "ky",
"lo-rLA" to "lo",
"lt-rLT" to "lt",
"lv-rLV" to "lv",
"mk-rMK" to "mk",
"ml-rIN" to "ml",
"mn-rMN" to "mn",
"mr-rIN" to "mr",
"ms-rMY" to "ms",
"my-rMM" to "my",
"nb-rNO" to "nb",
"ne-rIN" to "ne",
"nl-rNL" to "nl",
"or-rIN" to "or",
"pa-rIN" to "pa",
"pl-rPL" to "pl",
"pt-rBR" to "pt-rBR",
"pt-rPT" to "pt-rPT",
"ro-rRO" to "ro",
"ru-rRU" to "ru",
"si-rLK" to "si",
"sk-rSK" to "sk",
"sl-rSI" to "sl",
"sq-rAL" to "sq",
"sr-rSP" to "sr",
"sv-rSE" to "sv",
"sw-rKE" to "sw",
"ta-rIN" to "ta",
"te-rIN" to "te",
"th-rTH" to "th",
"tl-rPH" to "tl",
"tr-rTR" to "tr",
"uk-rUA" to "uk",
"ur-rIN" to "ur",
"uz-rUZ" to "uz",
"vi-rVN" to "vi",
"zh-rCN" to "zh-rCN",
"zh-rHK" to "zh-rHK",
"zh-rTW" to "zh-rTW",
"zu-rZA" to "zu",
)
/*
The strategy of this patch is to stage resources present in `/resources/addresources`.
These resources are organized by their respective value and patch.
@@ -75,23 +163,25 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
/**
* Puts resources under `/resources/addresources/<value>/<resourceKind>.xml` into the map.
*
* @param value The value of the resource. For example, `values` or `values-de`.
* @param sourceValue The source value of the resource. For example, `values` or `values-de-rDE`.
* @param destValue The destination value of the resource. For example, 'values' or 'values-de'.
* @param resourceKind The kind of the resource. For example, `strings` or `arrays`.
* @param transform A function that transforms the [Node]s from the XML files to a [BaseResource].
*/
fun addResources(
value: Value,
sourceValue: Value,
destValue: Value = sourceValue,
resourceKind: String,
transform: (Node) -> BaseResource,
) {
inputStreamFromBundledResource(
"addresources",
"$value/$resourceKind.xml",
"$sourceValue/$resourceKind.xml",
)?.let { stream ->
// Add the resources associated with the given value to the map,
// instead of overwriting it.
// This covers the example case such as adding strings and arrays of the same value.
getOrPut(value, ::mutableMapOf).apply {
getOrPut(destValue, ::mutableMapOf).apply {
context.xmlEditor[stream].use { editor ->
val document = editor.file
@@ -121,13 +211,13 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
// Staged resources consumed by AddResourcesPatch#invoke(PatchClass)
// are later used in AddResourcesPatch#close.
try {
val addStringResources = { value: Value ->
addResources(value, "strings", StringResource::fromNode)
val addStringResources = { source: Value, dest: Value ->
addResources(source, dest, "strings", StringResource::fromNode)
}
Locale.getISOLanguages().asSequence().map { "values-$it" }.forEach { addStringResources(it) }
addStringResources("values")
locales.forEach { (source, dest) -> addStringResources("values-$source", "values-$dest") }
addStringResources("values", "values")
addResources("values", "arrays", ArrayResource::fromNode)
addResources("values", "values", "arrays", ArrayResource::fromNode)
} catch (e: Exception) {
throw PatchException("Failed to read resources", e)
}

View File

@@ -0,0 +1,39 @@
package app.revanced.patches.all.misc.versioncode
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.intPatchOption
import app.revanced.util.getNode
import org.w3c.dom.Element
@Patch(
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.",
use = false,
)
@Suppress("unused")
object ChangeVersionCodePatch : ResourcePatch() {
private val versionCode by intPatchOption(
key = "versionCode",
default = Int.MAX_VALUE,
values = mapOf(
"Lowest" to 1,
"Highest" to Int.MAX_VALUE,
),
title = "Version code",
description = "The version code to use",
required = true,
) {
it!! >= 1
}
override fun execute(context: ResourceContext) {
context.document["AndroidManifest.xml"].use { document ->
val manifestElement = document.getNode("manifest") as Element
manifestElement.setAttribute("android:versionCode", "$versionCode")
}
}
}

View File

@@ -0,0 +1,32 @@
package app.revanced.patches.all.shortcut.sharetargets
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.util.asSequence
import app.revanced.util.getNode
import org.w3c.dom.Element
import java.io.FileNotFoundException
import java.util.logging.Logger
@Patch(
name = "Remove share targets",
description = "Removes share targets like directly sharing to a frequent contact.",
use = false,
)
@Suppress("unused")
object RemoveShareTargetsPatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
try {
context.document["res/xml/shortcuts.xml"]
} catch (_: FileNotFoundException) {
return Logger.getLogger(this::class.java.name).warning("The app has no shortcuts")
}.use { document ->
val rootNode = document.getNode("shortcuts") as? Element ?: return@use
document.getElementsByTagName("share-target").asSequence().forEach {
rootNode.removeChild(it)
}
}
}
}

View File

@@ -0,0 +1,23 @@
package app.revanced.patches.bandcamp.limitations
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.bandcamp.limitations.fingerprints.HandlePlaybackLimitsPatch
import app.revanced.util.exception
@Patch(
name = "Remove play limits",
description = "Disables purchase nagging and playback limits of not purchased tracks.",
compatiblePackages = [CompatiblePackage("com.bandcamp.android")],
)
@Suppress("unused")
object RemovePlayLimitsPatch : BytecodePatch(
setOf(HandlePlaybackLimitsPatch),
) {
override fun execute(context: BytecodeContext) =
HandlePlaybackLimitsPatch.result?.mutableMethod?.addInstructions(0, "return-void")
?: throw HandlePlaybackLimitsPatch.exception
}

View File

@@ -0,0 +1,7 @@
package app.revanced.patches.bandcamp.limitations.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object HandlePlaybackLimitsPatch : MethodFingerprint(
strings = listOf("play limits processing track", "found play_count"),
)

View File

@@ -0,0 +1,29 @@
package app.revanced.patches.instagram.patches.ad
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.instagram.patches.ad.fingerprints.AdInjectorFingerprint
import app.revanced.util.exception
@Patch(
name = "Hide ads",
description = "Hides ads in stories, discover, profile, etc." +
"An ad can still appear once when refreshing the home feed.",
compatiblePackages = [CompatiblePackage("com.instagram.android")],
)
@Suppress("unused")
object HideAdsPatch : BytecodePatch(
setOf(AdInjectorFingerprint),
) {
override fun execute(context: BytecodeContext) =
AdInjectorFingerprint.result?.mutableMethod?.addInstructions(
0,
"""
const/4 v0, 0x0
return v0
""",
) ?: throw AdInjectorFingerprint.exception
}

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.instagram.patches.ad.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object AdInjectorFingerprint : MethodFingerprint(
returnType = "Z",
accessFlags = AccessFlags.PRIVATE.value,
parameters = listOf("L", "L"),
opcodes = listOf(
Opcode.IGET,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
),
strings = listOf("SponsoredContentController::Delivery"),
)

View File

@@ -1,50 +1,13 @@
package app.revanced.patches.music.layout.minimizedplayback
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.BackgroundPlaybackDisableFingerprint
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint
import app.revanced.util.exception
import app.revanced.patches.music.misc.backgroundplayback.BackgroundPlaybackPatch
@Patch(
name = "Minimized playback",
description = "Unlocks options for picture-in-picture and background playback.",
compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
]
)
@Suppress("unused")
@Deprecated("This patch has been merged into BackgroundPlaybackPatch.")
object MinimizedPlaybackPatch : BytecodePatch(
setOf(
KidsMinimizedPlaybackPolicyControllerFingerprint,
BackgroundPlaybackDisableFingerprint,
),
dependencies = setOf(BackgroundPlaybackPatch::class),
) {
override fun execute(context: BytecodeContext) {
KidsMinimizedPlaybackPolicyControllerFingerprint.result?.mutableMethod?.addInstruction(
0,
"return-void",
) ?: throw KidsMinimizedPlaybackPolicyControllerFingerprint.exception
BackgroundPlaybackDisableFingerprint.result?.mutableMethod?.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
) ?: throw BackgroundPlaybackDisableFingerprint.exception
}
}
}

View File

@@ -0,0 +1,50 @@
package app.revanced.patches.music.misc.backgroundplayback
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.music.misc.backgroundplayback.fingerprints.BackgroundPlaybackDisableFingerprint
import app.revanced.patches.music.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint
import app.revanced.util.resultOrThrow
@Patch(
name = "Remove background playback restrictions",
description = "Removes restrictions on background playback, including playing kids videos in the background.",
compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
]
)
@Suppress("unused")
object BackgroundPlaybackPatch : BytecodePatch(
setOf(
KidsBackgroundPlaybackPolicyControllerFingerprint,
BackgroundPlaybackDisableFingerprint,
),
) {
override fun execute(context: BytecodeContext) {
KidsBackgroundPlaybackPolicyControllerFingerprint.resultOrThrow().mutableMethod.addInstruction(
0,
"return-void",
)
BackgroundPlaybackDisableFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
)
}
}

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.music.layout.minimizedplayback.fingerprints
package app.revanced.patches.music.misc.backgroundplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -1,11 +1,11 @@
package app.revanced.patches.music.layout.minimizedplayback.fingerprints
package app.revanced.patches.music.misc.backgroundplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerprint(
internal object KidsBackgroundPlaybackPolicyControllerFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("I", "L", "Z"),

View File

@@ -2,10 +2,11 @@ package app.revanced.patches.music.premium.backgroundplay
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patches.music.layout.minimizedplayback.MinimizedPlaybackPatch
@Deprecated("This patch has been merged into MinimizedPlaybackPatch.")
import app.revanced.patches.music.misc.backgroundplayback.BackgroundPlaybackPatch
@Deprecated("This patch has been merged into BackgroundPlaybackPatch.")
object BackgroundPlayPatch : BytecodePatch(
dependencies = setOf(MinimizedPlaybackPatch::class),
dependencies = setOf(BackgroundPlaybackPatch::class),
) {
override fun execute(context: BytecodeContext) {
}

View File

@@ -0,0 +1,24 @@
package app.revanced.patches.rar.misc.annoyances.purchasereminder
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.rar.misc.annoyances.purchasereminder.fingerprints.ShowReminderFingerprint
import app.revanced.util.exception
@Patch(
name = "Hide purchase reminder",
description = "Hides the popup that reminds you to purchase the app.",
compatiblePackages = [CompatiblePackage("com.rarlab.rar")],
)
@Suppress("unused")
object HidePurchaseReminderPatch : BytecodePatch(
setOf(ShowReminderFingerprint),
) {
override fun execute(context: BytecodeContext) {
ShowReminderFingerprint.result?.mutableMethod?.addInstruction(0, "return-void")
?: throw ShowReminderFingerprint.exception
}
}

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.rar.misc.annoyances.purchasereminder.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
object ShowReminderFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
customFingerprint = { methodDef, _ ->
methodDef.definingClass.endsWith("AdsNotify;") && methodDef.name == "show"
},
)

View File

@@ -0,0 +1,49 @@
package app.revanced.patches.reddit.customclients
import app.revanced.patcher.PatchClass
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.fingerprint.MethodFingerprintResult
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.util.resultOrThrow
abstract class BaseFixSLinksPatch(
private val handleNavigationFingerprint: MethodFingerprint,
private val setAccessTokenFingerprint: MethodFingerprint,
compatiblePackages: Set<CompatiblePackage>,
dependencies: Set<PatchClass> = emptySet(),
) : BytecodePatch(
name = "Fix /s/ links",
fingerprints = setOf(handleNavigationFingerprint, setAccessTokenFingerprint),
compatiblePackages = compatiblePackages,
dependencies = dependencies,
) {
protected abstract val integrationsClassDescriptor: String
protected val resolveSLinkMethod =
"patchResolveSLink(Ljava/lang/String;)Z"
protected val setAccessTokenMethod =
"patchSetAccessToken(Ljava/lang/String;)V"
override fun execute(context: BytecodeContext) {
handleNavigationFingerprint.resultOrThrow().patchNavigationHandler(context)
setAccessTokenFingerprint.resultOrThrow().patchSetAccessToken(context)
}
/**
* Patch app's navigation handler to resolve /s/ links.
*
* @param context The current [BytecodeContext].
*
*/
protected abstract fun MethodFingerprintResult.patchNavigationHandler(context: BytecodeContext)
/**
* Patch access token setup in app to resolve /s/ links with an access token
* in order to bypass API bans when making unauthorized requests.
*
* @param context The current [BytecodeContext].
*/
protected abstract fun MethodFingerprintResult.patchSetAccessToken(context: BytecodeContext)
}

View File

@@ -0,0 +1,38 @@
package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads.fingerprints.DownloadAudioFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Fix missing audio in video downloads",
description = "Fixes audio missing in videos downloaded from v.redd.it.",
compatiblePackages = [CompatiblePackage("com.rubenmayayo.reddit")],
)
@Suppress("unused")
object FixAudioMissingInDownloadsPatch : BytecodePatch(
setOf(DownloadAudioFingerprint),
) {
private val endpointReplacements = mapOf(
"/DASH_audio.mp4" to "/DASH_AUDIO_128.mp4",
"/audio" to "/DASH_AUDIO_64.mp4",
)
override fun execute(context: BytecodeContext) {
DownloadAudioFingerprint.resultOrThrow().let { result ->
result.scanResult.stringsScanResult!!.matches.forEach { match ->
result.mutableMethod.apply {
val replacement = endpointReplacements[match.string]
val register = getInstruction<OneRegisterInstruction>(match.index).registerA
replaceInstruction(match.index, "const-string v$register, \"$replacement\"")
}
}
}
}
}

View File

@@ -0,0 +1,7 @@
package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object DownloadAudioFingerprint : MethodFingerprint(
strings = setOf("/DASH_audio.mp4", "/audio"),
)

View File

@@ -0,0 +1,44 @@
package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.fingerprint.MethodFingerprintResult
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.reddit.customclients.BaseFixSLinksPatch
import app.revanced.patches.reddit.customclients.boostforreddit.fix.slink.fingerprints.GetOAuthAccessTokenFingerprint
import app.revanced.patches.reddit.customclients.boostforreddit.fix.slink.fingerprints.HandleNavigationFingerprint
import app.revanced.patches.reddit.customclients.boostforreddit.misc.integrations.IntegrationsPatch
@Suppress("unused")
object FixSLinksPatch : BaseFixSLinksPatch(
handleNavigationFingerprint = HandleNavigationFingerprint,
setAccessTokenFingerprint = GetOAuthAccessTokenFingerprint,
compatiblePackages = setOf(CompatiblePackage("com.rubenmayayo.reddit")),
dependencies = setOf(IntegrationsPatch::class),
) {
override val integrationsClassDescriptor = "Lapp/revanced/integrations/boostforreddit/FixSLinksPatch;"
override fun MethodFingerprintResult.patchNavigationHandler(context: BytecodeContext) {
mutableMethod.apply {
val urlRegister = "p1"
val tempRegister = "v1"
addInstructionsWithLabels(
0,
"""
invoke-static { $urlRegister }, $integrationsClassDescriptor->$resolveSLinkMethod
move-result $tempRegister
if-eqz $tempRegister, :continue
return $tempRegister
""",
ExternalLabel("continue", getInstruction(0)),
)
}
}
override fun MethodFingerprintResult.patchSetAccessToken(context: BytecodeContext) = mutableMethod.addInstruction(
3,
"invoke-static { v0 }, $integrationsClassDescriptor->$setAccessTokenMethod",
)
}

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object GetOAuthAccessTokenFingerprint : MethodFingerprint(
strings = listOf("access_token"),
accessFlags = AccessFlags.PUBLIC.value,
returnType = "Ljava/lang/String",
customFingerprint = { _, classDef -> classDef.type == "Lnet/dean/jraw/http/oauth/OAuthData;" },
)

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object HandleNavigationFingerprint : MethodFingerprint(
strings = listOf(
"android.intent.action.SEARCH",
"subscription",
"sort",
"period",
"boostforreddit.com/themes",
),
)

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.reddit.customclients.boostforreddit.misc.integrations
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
import app.revanced.patches.reddit.customclients.boostforreddit.misc.integrations.fingerprints.InitFingerprint
@Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch(
setOf(InitFingerprint)
)

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.reddit.customclients.boostforreddit.misc.integrations.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object InitFingerprint : IntegrationsFingerprint(
customFingerprint = { methodDef, _ -> methodDef.definingClass == "Lcom/rubenmayayo/reddit/MyApplication;" && methodDef.name == "onCreate" },
insertIndexResolver = { 1 } // Insert after call to super class.
)

View File

@@ -1,32 +1,49 @@
package app.revanced.patches.reddit.customclients.syncforreddit.fix.slink
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.fingerprint.MethodFingerprintResult
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.reddit.customclients.BaseFixSLinksPatch
import app.revanced.patches.reddit.customclients.syncforreddit.fix.slink.fingerprints.LinkHelperOpenLinkFingerprint
import app.revanced.util.exception
import app.revanced.patches.reddit.customclients.syncforreddit.fix.slink.fingerprints.SetAuthorizationHeaderFingerprint
import app.revanced.patches.reddit.customclients.syncforreddit.misc.integrations.IntegrationsPatch
@Patch(
name = "Fix /s/ links",
description = "Fixes the issue where /s/ links do not work.",
compatiblePackages = [
@Suppress("unused")
object FixSLinksPatch : BaseFixSLinksPatch(
handleNavigationFingerprint = LinkHelperOpenLinkFingerprint,
setAccessTokenFingerprint = SetAuthorizationHeaderFingerprint,
compatiblePackages = setOf(
CompatiblePackage("com.laurencedawson.reddit_sync"),
CompatiblePackage("com.laurencedawson.reddit_sync.pro"),
CompatiblePackage("com.laurencedawson.reddit_sync.dev")
],
requiresIntegrations = true
)
object FixSLinksPatch : BytecodePatch(
setOf(LinkHelperOpenLinkFingerprint)
CompatiblePackage("com.laurencedawson.reddit_sync.dev"),
),
dependencies = setOf(IntegrationsPatch::class),
) {
override fun execute(context: BytecodeContext) =
LinkHelperOpenLinkFingerprint.result?.mutableMethod?.addInstructions(
1,
"""
invoke-static { p3 }, Lapp/revanced/integrations/syncforreddit/FixSLinksPatch;->resolveSLink(Ljava/lang/String;)Ljava/lang/String;
move-result-object p3
"""
) ?: throw LinkHelperOpenLinkFingerprint.exception
override val integrationsClassDescriptor = "Lapp/revanced/integrations/syncforreddit/FixSLinksPatch;"
override fun MethodFingerprintResult.patchNavigationHandler(context: BytecodeContext) {
mutableMethod.apply {
val urlRegister = "p3"
val tempRegister = "v2"
addInstructionsWithLabels(
0,
"""
invoke-static { $urlRegister }, $integrationsClassDescriptor->$resolveSLinkMethod
move-result $tempRegister
if-eqz $tempRegister, :continue
return $tempRegister
""",
ExternalLabel("continue", getInstruction(0)),
)
}
}
override fun MethodFingerprintResult.patchSetAccessToken(context: BytecodeContext) = mutableMethod.addInstruction(
0,
"invoke-static { p0 }, $integrationsClassDescriptor->$setAccessTokenMethod",
)
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.reddit.customclients.syncforreddit.fix.slink.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object SetAuthorizationHeaderFingerprint : MethodFingerprint(
strings = listOf("Authorization", "bearer "),
returnType = "Ljava/util/HashMap;",
customFingerprint = { methodDef, _ -> methodDef.definingClass == "Lcom/laurencedawson/reddit_sync/singleton/a;" },
)

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.reddit.customclients.syncforreddit.misc.integrations
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
import app.revanced.patches.reddit.customclients.syncforreddit.misc.integrations.fingerprints.InitFingerprint
@Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch(
setOf(InitFingerprint)
)

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.reddit.customclients.syncforreddit.misc.integrations.fingerprints
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
internal object InitFingerprint : IntegrationsFingerprint(
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.type == "Lcom/laurencedawson/reddit_sync/RedditApplication;"
},
insertIndexResolver = { 1 }, // Insert after call to super class.
)

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.shared.misc.mapping
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatch
import org.w3c.dom.Element
import java.util.*
@@ -51,9 +52,10 @@ object ResourceMappingPatch : ResourcePatch() {
threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
}
operator fun get(type: String, name: String) = resourceMappings.first {
it.type == type && it.name == name
}.id
operator fun get(type: String, name: String) =
resourceMappings.firstOrNull {
it.type == type && it.name == name
}?.id ?: throw PatchException("Could not find resource type: $type name: $name")
data class ResourceElement(val type: String, val name: String, val id: Long)
}

View File

@@ -0,0 +1,72 @@
package app.revanced.patches.soundcloud.ad
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
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.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.soundcloud.ad.fingerprints.InterceptFingerprint
import app.revanced.patches.soundcloud.ad.fingerprints.FeatureConstructorFingerprint
import app.revanced.patches.soundcloud.ad.fingerprints.UserConsumerPlanConstructorFingerprint
import app.revanced.util.resultOrThrow
@Patch(
name = "Hide ads",
compatiblePackages = [CompatiblePackage("com.soundcloud.android")],
)
@Suppress("unused")
object HideAdsPatch : BytecodePatch(
setOf(FeatureConstructorFingerprint, UserConsumerPlanConstructorFingerprint, InterceptFingerprint),
) {
override fun execute(context: BytecodeContext) {
// Enable a preset feature to disable audio ads by modifying the JSON server response.
// This method is the constructor of a class representing a "Feature" object parsed from JSON data.
// p1 is the name of the feature.
// p2 is true if the feature is enabled, false otherwise.
FeatureConstructorFingerprint.resultOrThrow().mutableMethod.apply {
val afterCheckNotNullIndex = 2
addInstructionsWithLabels(
afterCheckNotNullIndex,
"""
const-string v0, "no_audio_ads"
invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :skip
const/4 p2, 0x1
""",
ExternalLabel("skip", getInstruction(afterCheckNotNullIndex)),
)
}
// Overwrite the JSON response from the server to a paid plan, which hides all ads in the app.
// This does not enable paid features, as they are all checked for on the backend.
// This method is the constructor of a class representing a "UserConsumerPlan" object parsed from JSON data.
// p1 is the "currentTier" value, dictating which features to enable in the app.
// p4 is the "consumerPlanUpsells" value, a list of plans to try to sell to the user.
// p5 is the "currentConsumerPlan" value, the type of plan currently subscribed to.
// p6 is the "currentConsumerPlanTitle" value, the name of the plan currently subscribed to, shown to the user.
UserConsumerPlanConstructorFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"""
const-string p1, "high_tier"
new-instance p4, Ljava/util/ArrayList;
invoke-direct {p4}, Ljava/util/ArrayList;-><init>()V
const-string p5, "go-plus"
const-string p6, "SoundCloud Go+"
""",
)
// Prevent verification of an HTTP header containing the user's current plan, which would contradict the previous patch.
InterceptFingerprint.resultOrThrow().let { result ->
val conditionIndex = result.scanResult.patternScanResult!!.endIndex
result.mutableMethod.addInstruction(
conditionIndex,
"return-object p1",
)
}
}
}

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.soundcloud.ad.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object FeatureConstructorFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("Ljava/lang/String;", "Z", "Ljava/util/List;"),
customFingerprint = { _, classDef ->
classDef.sourceFile == "Feature.kt"
},
)

View File

@@ -0,0 +1,22 @@
package app.revanced.patches.soundcloud.ad.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object InterceptFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC.value,
parameters = listOf("L"),
opcodes = listOf(
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
),
strings = listOf("SC-Mob-UserPlan", "Configuration"),
customFingerprint = { _, classDef ->
classDef.sourceFile == "ApiUserPlanInterceptor.java"
},
)

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.soundcloud.ad.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object UserConsumerPlanConstructorFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("Ljava/lang/String;", "Z", "Ljava/lang/String;", "Ljava/util/List;", "Ljava/lang/String;", "Ljava/lang/String;"),
customFingerprint = { _, classDef ->
classDef.sourceFile == "UserConsumerPlan.kt"
},
)

View File

@@ -0,0 +1,24 @@
package app.revanced.patches.soundcloud.analytics
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.soundcloud.analytics.fingerprints.CreateTrackingApiFingerprint
import app.revanced.util.resultOrThrow
@Patch(
name = "Disable telemetry",
description = "Disables SoundCloud's telemetry system.",
compatiblePackages = [CompatiblePackage("com.soundcloud.android")],
)
@Suppress("unused")
object DisableTelemetryPatch : BytecodePatch(
setOf(CreateTrackingApiFingerprint),
) {
override fun execute(context: BytecodeContext) =
// Empty the "backend" argument to abort the initializer.
CreateTrackingApiFingerprint.resultOrThrow()
.mutableMethod.addInstruction(0, "const-string p1, \"\"")
}

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.soundcloud.analytics.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object CreateTrackingApiFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC.value,
customFingerprint = { methodDef, classDef ->
classDef.sourceFile == "DefaultTrackingApiFactory.kt" && methodDef.name == "create"
},
)

View File

@@ -0,0 +1,27 @@
package app.revanced.patches.stocard.layout
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.util.childElementsSequence
import app.revanced.util.getNode
@Patch(
name = "Hide offers tab",
compatiblePackages = [CompatiblePackage("de.stocard.stocard")],
)
@Suppress("unused")
object HideOffersTabPatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
context.document["res/menu/bottom_navigation_menu.xml"].use { document ->
document.getNode("menu").apply {
removeChild(
childElementsSequence().first {
it.attributes.getNamedItem("android:id")?.nodeValue?.contains("offer") ?: false
},
)
}
}
}
}

View File

@@ -0,0 +1,27 @@
package app.revanced.patches.stocard.layout
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.util.getNode
@Patch(
name = "Hide story bubbles",
compatiblePackages = [CompatiblePackage("de.stocard.stocard")],
)
@Suppress("unused")
object HideStoryBubblesPatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
context.document["res/layout/rv_story_bubbles_list.xml"].use { document ->
document.getNode("androidx.recyclerview.widget.RecyclerView").apply {
arrayOf(
"android:layout_width",
"android:layout_height",
).forEach {
attributes.getNamedItem(it).nodeValue = "0dp"
}
}
}
}
}

View File

@@ -13,7 +13,6 @@ import app.revanced.patches.tiktok.interaction.speed.fingerprints.OnRenderFirstF
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SetSpeedFingerprint
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x
import com.android.tools.smali.dexlib2.iface.reference.MethodReference

View File

@@ -62,9 +62,12 @@ object CommentsPatch : ResourcePatch() {
PreferenceScreen(
"revanced_comments_screen",
preferences = setOf(
SwitchPreference("revanced_hide_preview_comment"),
SwitchPreference("revanced_hide_comments_by_members_header"),
SwitchPreference("revanced_hide_comments_section"),
SwitchPreference("revanced_hide_comment_timestamp_and_emoji_buttons")
SwitchPreference("revanced_hide_comments_create_a_short_button"),
SwitchPreference("revanced_hide_comments_preview_comment"),
SwitchPreference("revanced_hide_comments_thanks_button"),
SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons")
),
sorting = PreferenceScreen.Sorting.UNSORTED
)

View File

@@ -94,16 +94,14 @@ object HideLayoutComponentsPatch : BytecodePatch(
PreferenceScreen(
key = "revanced_hide_description_components_screen",
preferences = setOf(
SwitchPreference("revanced_hide_chapters"),
SwitchPreference("revanced_hide_attributes_section"),
SwitchPreference("revanced_hide_chapters_section"),
SwitchPreference("revanced_hide_info_cards_section"),
SwitchPreference("revanced_hide_game_section"),
SwitchPreference("revanced_hide_music_section"),
SwitchPreference("revanced_hide_podcast_section"),
SwitchPreference("revanced_hide_transcript_section"),
),
),
SwitchPreference("revanced_hide_emergency_box"),
SwitchPreference("revanced_hide_expandable_chip"),
SwitchPreference("revanced_hide_info_panels"),
SwitchPreference("revanced_hide_join_membership_button"),
SwitchPreference("revanced_disable_like_subscribe_glow"),
@@ -144,6 +142,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
)
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_hide_expandable_chip"),
SwitchPreference("revanced_hide_gray_separator"),
PreferenceScreen(
key = "revanced_custom_filter_screen",
@@ -156,10 +155,6 @@ object HideLayoutComponentsPatch : BytecodePatch(
),
)
SettingsPatch.PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_hide_video_quality_menu_footer"),
)
LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR)
LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME)
LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME)

View File

@@ -74,6 +74,7 @@ object HidePlayerFlyoutMenuPatch : ResourcePatch() {
SwitchPreference("revanced_hide_player_flyout_more_info"),
SwitchPreference("revanced_hide_player_flyout_audio_track"),
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
SwitchPreference("revanced_hide_video_quality_menu_footer"),
)
)
)

View File

@@ -0,0 +1,370 @@
package app.revanced.patches.youtube.layout.miniplayer
import app.revanced.patcher.data.BytecodeContext
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.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerClose
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerExpand
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerForwardButton
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerRewindButton
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.scrimOverlay
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlineXWhite24
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerDimensionsCalculatorParentFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernAddViewListenerFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernCloseButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernConstructorFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernExpandButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernExpandCloseDrawablesFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernForwardButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernOverlayViewFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernRewindButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernViewParentFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerOverrideFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerOverrideNoContextFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerResponseModelSizeCheckFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint.YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.findOpcodeIndicesReversed
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import app.revanced.util.patch.LiteralValueFingerprint
import app.revanced.util.resultOrThrow
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.Method
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
// YT uses "Miniplayer" without a space between 'mini' and 'player: https://support.google.com/youtube/answer/9162927.
@Patch(
name = "Miniplayer",
description = "Adds options to change the in app minimized player, " +
"and if patching target 19.16+ adds options to use modern miniplayers.",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
MiniplayerResourcePatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
// 19.14 is left out, as it has incomplete miniplayer code and missing some UI resources.
// It's simpler to not bother with supporting this single old version.
// 19.15 has a different code for handling sub title texts,
// and also probably not worth making changes just to support this single old version.
"19.16.39" // Earliest supported version with modern miniplayers.
]
)
]
)
@Suppress("unused")
object MiniplayerPatch : BytecodePatch(
setOf(GetFormFactorFingerprint,
MiniplayerDimensionsCalculatorParentFingerprint,
MiniplayerResponseModelSizeCheckFingerprint,
MiniplayerOverrideFingerprint,
MiniplayerModernConstructorFingerprint,
MiniplayerModernViewParentFingerprint,
YouTubePlayerOverlaysLayoutFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/MiniplayerPatch;"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
// Modern mini player is only present and functional in 19.15+.
// Resource is not present in older versions. Using it to determine, if patching an old version.
val isPatchingOldVersion = ytOutlinePictureInPictureWhite24 < 0
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen(
key = "revanced_miniplayer_screen",
sorting = Sorting.UNSORTED,
preferences =
if (isPatchingOldVersion) {
setOf(
ListPreference(
"revanced_miniplayer_type",
summaryKey = null,
entriesKey = "revanced_miniplayer_type_legacy_entries",
entryValuesKey = "revanced_miniplayer_type_legacy_entry_values"
)
)
} else {
setOf(
ListPreference(
"revanced_miniplayer_type",
summaryKey = null,
entriesKey = "revanced_miniplayer_type_19_15_entries",
entryValuesKey = "revanced_miniplayer_type_19_15_entry_values"
),
SwitchPreference("revanced_miniplayer_hide_expand_close"),
SwitchPreference("revanced_miniplayer_hide_subtext"),
SwitchPreference("revanced_miniplayer_hide_rewind_forward"),
TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
)
}
)
)
// region Enable tablet miniplayer.
MiniplayerOverrideNoContextFingerprint.resolve(
context,
MiniplayerDimensionsCalculatorParentFingerprint.resultOrThrow().classDef
)
MiniplayerOverrideNoContextFingerprint.resultOrThrow().mutableMethod.apply {
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
}
// endregion
// region Legacy tablet Miniplayer hooks.
MiniplayerOverrideFingerprint.resultOrThrow().let {
val appNameStringIndex = it.scanResult.stringsScanResult!!.matches.first().index + 2
it.mutableMethod.apply {
val walkerMethod = context.toMethodWalker(this)
.nextMethod(appNameStringIndex, true)
.getMethod() as MutableMethod
walkerMethod.apply {
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
}
}
}
MiniplayerResponseModelSizeCheckFingerprint.resultOrThrow().let {
it.mutableMethod.insertLegacyTabletMiniplayerOverride(it.scanResult.patternScanResult!!.endIndex)
}
if (isPatchingOldVersion) {
// Return here, as patch below is only intended for new versions of the app.
return
}
// endregion
// region Enable modern miniplayer.
MiniplayerModernConstructorFingerprint.resultOrThrow().mutableClass.methods.forEach {
it.apply {
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
val iPutIndex = indexOfFirstInstructionOrThrow {
this.opcode == Opcode.IPUT && this.getReference<FieldReference>()?.type == "I"
}
insertModernMiniplayerTypeOverride(iPutIndex)
} else {
findReturnIndicesReversed().forEach { index -> insertModernMiniplayerOverride(index) }
}
}
}
// endregion
// region Fix 19.16 using mixed up drawables for tablet modern.
// YT fixed this mistake in 19.17.
// Fix this, by swapping the drawable resource values with each other.
MiniplayerModernExpandCloseDrawablesFingerprint.apply {
resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)
}.resultOrThrow().mutableMethod.apply {
listOf(
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
).forEach { (originalResource, replacementResource) ->
val imageResourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(originalResource)
val register = getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA
replaceInstruction(imageResourceIndex, "const v$register, $replacementResource")
}
}
// endregion
// region Add hooks to hide tablet modern miniplayer buttons.
listOf(
Triple(MiniplayerModernExpandButtonFingerprint, modernMiniplayerExpand,"hideMiniplayerExpandClose"),
Triple(MiniplayerModernCloseButtonFingerprint, modernMiniplayerClose, "hideMiniplayerExpandClose"),
Triple(MiniplayerModernRewindButtonFingerprint, modernMiniplayerRewindButton, "hideMiniplayerRewindForward"),
Triple(MiniplayerModernForwardButtonFingerprint, modernMiniplayerForwardButton, "hideMiniplayerRewindForward"),
Triple(MiniplayerModernOverlayViewFingerprint, scrimOverlay, "adjustMiniplayerOpacity")
).forEach { (fingerprint, literalValue, methodName) ->
fingerprint.resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)
fingerprint.hookInflatedView(
literalValue,
"Landroid/widget/ImageView;",
"$INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V"
)
}
MiniplayerModernAddViewListenerFingerprint.apply {
resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)
}.resultOrThrow().mutableMethod.addInstruction(
0,
"invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->" +
"hideMiniplayerSubTexts(Landroid/view/View;)V"
)
// Modern 2 has a broken overlay subtitle view that is always present.
// Modern 2 uses the same overlay controls as the regular video player,
// and the overlay views are added at runtime.
// Add a hook to the overlay class, and pass the added views to integrations.
YouTubePlayerOverlaysLayoutFingerprint.resultOrThrow().mutableClass.methods.add(
ImmutableMethod(
YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME,
"addView",
listOf(
ImmutableMethodParameter("Landroid/view/View;", null, null),
ImmutableMethodParameter("I", null, null),
ImmutableMethodParameter("Landroid/view/ViewGroup\$LayoutParams;", null, null),
),
"V",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(4),
).toMutable().apply {
addInstructions(
"""
invoke-super { p0, p1, p2, p3 }, Landroid/view/ViewGroup;->addView(Landroid/view/View;ILandroid/view/ViewGroup${'$'}LayoutParams;)V
invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->playerOverlayGroupCreated(Landroid/view/View;)V
return-void
""",
)
}
)
// endregion
}
private fun Method.findReturnIndicesReversed() = findOpcodeIndicesReversed(Opcode.RETURN)
/**
* Adds an override to force legacy tablet miniplayer to be used or not used.
*/
private fun MutableMethod.insertLegacyTabletMiniplayerOverride(index: Int) {
insertBooleanOverride(index, "getLegacyTabletMiniplayerOverride")
}
/**
* Adds an override to force modern miniplayer to be used or not used.
*/
private fun MutableMethod.insertModernMiniplayerOverride(index: Int) {
insertBooleanOverride(index, "getModernMiniplayerOverride")
}
private fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) {
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Z)Z
move-result v$register
"""
)
}
/**
* Adds an override to specify which modern miniplayer is used.
*/
private fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
val targetReference = (targetInstruction as ReferenceInstruction).reference
addInstructions(
iPutIndex + 1, """
invoke-static { v${targetInstruction.registerA} }, $INTEGRATIONS_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
move-result v${targetInstruction.registerA}
# Original instruction
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference
"""
)
removeInstruction(iPutIndex)
}
private fun LiteralValueFingerprint.hookInflatedView(
literalValue: Long,
hookedClassType: String,
integrationsMethodName: String,
) {
resultOrThrow().mutableMethod.apply {
val imageViewIndex = indexOfFirstInstructionOrThrow(
indexOfFirstWideLiteralInstructionValueOrThrow(literalValue)
) {
opcode == Opcode.CHECK_CAST && getReference<TypeReference>()?.type == hookedClassType
}
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
addInstruction(
imageViewIndex + 1,
"invoke-static { v$register }, $integrationsMethodName"
)
}
}
}

View File

@@ -0,0 +1,81 @@
package app.revanced.patches.youtube.layout.miniplayer
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
@Patch(dependencies = [ResourceMappingPatch::class])
internal object MiniplayerResourcePatch : ResourcePatch() {
var floatyBarButtonTopMargin = -1L
// Only available in 19.15 and upwards.
var ytOutlineXWhite24 = -1L
var ytOutlinePictureInPictureWhite24 = -1L
var scrimOverlay = -1L
var modernMiniplayerClose = -1L
var modernMiniplayerExpand = -1L
var modernMiniplayerRewindButton = -1L
var modernMiniplayerForwardButton = -1L
var playerOverlays = -1L
override fun execute(context: ResourceContext) {
floatyBarButtonTopMargin = ResourceMappingPatch[
"dimen",
"floaty_bar_button_top_margin"
]
try {
ytOutlinePictureInPictureWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_picture_in_picture_white_24"
]
} catch (exception: PatchException) {
// Ignore, and assume the app is 19.14 or earlier.
return
}
ytOutlineXWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_x_white_24"
]
scrimOverlay = ResourceMappingPatch[
"id",
"scrim_overlay"
]
modernMiniplayerClose = ResourceMappingPatch[
"id",
"modern_miniplayer_close"
]
modernMiniplayerExpand = ResourceMappingPatch[
"id",
"modern_miniplayer_expand"
]
modernMiniplayerRewindButton = ResourceMappingPatch[
"id",
"modern_miniplayer_rewind_button"
]
modernMiniplayerForwardButton = ResourceMappingPatch[
"id",
"modern_miniplayer_forward_button"
]
playerOverlays = ResourceMappingPatch[
"layout",
"player_overlays"
]
// Resource id is not used during patching, but is used by integrations.
// Verify the resource is present while patching.
ResourceMappingPatch[
"id",
"modern_miniplayer_subtitle_text"
]
}
}

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object MiniplayerDimensionsCalculatorParentFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("L"),
literalSupplier = { MiniplayerResourcePatch.floatyBarButtonTopMargin }
)

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
*/
internal object MiniplayerModernAddViewListenerFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("Landroid/view/View;")
)

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
*/
internal object MiniplayerModernCloseButtonFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/widget/ImageView;",
parameters = listOf(),
literalSupplier = { MiniplayerResourcePatch.modernMiniplayerClose }
)

View File

@@ -0,0 +1,11 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object MiniplayerModernConstructorFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("L"),
literalSupplier = { 45623000L } // Magic number found in the constructor.
)

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
*/
internal object MiniplayerModernExpandButtonFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/widget/ImageView;",
parameters = listOf(),
literalSupplier = { MiniplayerResourcePatch.modernMiniplayerExpand }
)

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
*/
internal object MiniplayerModernExpandCloseDrawablesFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("L"),
literalSupplier = { MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24 }
)

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
*/
internal object MiniplayerModernForwardButtonFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/widget/ImageView;",
parameters = listOf(),
literalSupplier = { MiniplayerResourcePatch.modernMiniplayerForwardButton }
)

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
*/
internal object MiniplayerModernOverlayViewFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf(),
literalSupplier = { MiniplayerResourcePatch.scrimOverlay }
)

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Resolves using the class found in [MiniplayerModernViewParentFingerprint].
*/
internal object MiniplayerModernRewindButtonFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/widget/ImageView;",
parameters = listOf(),
literalSupplier = { MiniplayerResourcePatch.modernMiniplayerRewindButton }
)

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object MiniplayerModernViewParentFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Ljava/lang/String;",
parameters = listOf(),
strings = listOf("player_overlay_modern_mini_player_controls")
)

View File

@@ -1,10 +1,10 @@
package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object MiniPlayerOverrideFingerprint : MethodFingerprint(
internal object MiniplayerOverrideFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object MiniplayerOverrideNoContextFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
returnType = "Z",
opcodes = listOf(Opcode.IGET_BOOLEAN), // anchor to insert the instruction
)

View File

@@ -1,15 +1,15 @@
package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object MiniPlayerResponseModelSizeCheckFingerprint : MethodFingerprint(
"L",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
listOf(
internal object MiniplayerResponseModelSizeCheckFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "L",
parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
opcodes = listOf(
Opcode.RETURN_OBJECT,
Opcode.CHECK_CAST,
Opcode.CHECK_CAST,

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint.YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
internal object YouTubePlayerOverlaysLayoutFingerprint : MethodFingerprint(
customFingerprint = { _, classDef ->
classDef.type == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
}
) {
const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"
}

View File

@@ -1,7 +1,5 @@
package app.revanced.patches.youtube.layout.player.overlay
import app.revanced.util.exception
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
@@ -9,6 +7,8 @@ import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.layout.player.overlay.fingerprints.CreatePlayerOverviewFingerprint
import app.revanced.util.exception
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
@@ -27,7 +27,7 @@ object CustomPlayerOverlayOpacityPatch : BytecodePatch(setOf(CreatePlayerOvervie
CreatePlayerOverviewFingerprint.result?.let { result ->
result.mutableMethod.apply {
val viewRegisterIndex =
indexOfFirstWideLiteralInstructionValue(CustomPlayerOverlayOpacityResourcePatch.scrimOverlayId) + 3
indexOfFirstWideLiteralInstructionValueOrThrow(CustomPlayerOverlayOpacityResourcePatch.scrimOverlayId) + 3
val viewRegister =
getInstruction<OneRegisterInstruction>(viewRegisterIndex).registerA

View File

@@ -1,7 +1,5 @@
package app.revanced.patches.youtube.layout.seekbar
import app.revanced.util.exception
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
@@ -15,6 +13,8 @@ import app.revanced.patches.youtube.layout.seekbar.fingerprints.ShortsSeekbarCol
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch.lithoColorOverrideHook
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.util.exception
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@@ -30,7 +30,7 @@ internal object SeekbarColorBytecodePatch : BytecodePatch(
override fun execute(context: BytecodeContext) {
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
val registerIndex = indexOfFirstWideLiteralInstructionValue(resourceId) + 2
val registerIndex = indexOfFirstWideLiteralInstructionValueOrThrow(resourceId) + 2
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
addInstructions(
registerIndex + 1,

View File

@@ -13,18 +13,51 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.resultOrThrow
@Patch(
name = "Enable tablet layout",
description = "Adds an option to spoof the device form factor to a tablet which enables the tablet layout.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
compatiblePackages = [CompatiblePackage("com.google.android.youtube")]
description = "Adds an option to enable tablet layout",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", arrayOf(
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39"
)
)
]
)
@Suppress("unused")
object EnableTabletLayoutPatch : BytecodePatch(
setOf(GetFormFactorFingerprint)
) {
object EnableTabletLayoutPatch : BytecodePatch(setOf(GetFormFactorFingerprint)) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/TabletLayoutPatch;"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@@ -32,7 +65,7 @@ object EnableTabletLayoutPatch : BytecodePatch(
SwitchPreference("revanced_tablet_layout")
)
GetFormFactorFingerprint.result?.let {
GetFormFactorFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val returnIsLargeFormFactorIndex = getInstructions().lastIndex - 4
val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex)
@@ -40,8 +73,8 @@ object EnableTabletLayoutPatch : BytecodePatch(
addInstructionsWithLabels(
0,
"""
invoke-static { }, Lapp/revanced/integrations/youtube/patches/EnableTabletLayoutPatch;->enableTabletLayout()Z
move-result v0 # Free register
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->getTabletLayoutEnabled()Z
move-result v0
if-nez v0, :is_large_form_factor
""",
ExternalLabel(
@@ -50,6 +83,6 @@ object EnableTabletLayoutPatch : BytecodePatch(
)
)
}
} ?: GetFormFactorFingerprint.exception
}
}
}

View File

@@ -21,5 +21,6 @@ internal object GetFormFactorFingerprint : MethodFingerprint(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT
)
),
strings = listOf("")
)

View File

@@ -1,152 +1,11 @@
package app.revanced.patches.youtube.layout.tabletminiplayer
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerDimensionsCalculatorParentFingerprint
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideFingerprint
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideNoContextFingerprint
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerResponseModelSizeCheckFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerPatch
@Patch(
name = "Tablet mini player",
description = "Adds an option to enable the tablet mini player layout.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", arrayOf(
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
)
)
]
)
@Suppress("unused")
object TabletMiniPlayerPatch : BytecodePatch(
setOf(
MiniPlayerDimensionsCalculatorParentFingerprint,
MiniPlayerResponseModelSizeCheckFingerprint,
MiniPlayerOverrideFingerprint
)
) {
@Deprecated("This patch class has been renamed to Miniplayer.")
object TabletMiniPlayerPatch : BytecodePatch(dependencies = setOf(MiniplayerPatch::class)) {
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_tablet_miniplayer")
)
// First resolve the fingerprints via the parent fingerprint.
MiniPlayerDimensionsCalculatorParentFingerprint.result
?: throw MiniPlayerDimensionsCalculatorParentFingerprint.exception
val miniPlayerClass = MiniPlayerDimensionsCalculatorParentFingerprint.result!!.classDef
/*
* No context parameter method.
*/
MiniPlayerOverrideNoContextFingerprint.resolve(context, miniPlayerClass)
val (method, _, parameterRegister) = MiniPlayerOverrideNoContextFingerprint.addProxyCall()
// Insert right before the return instruction.
val secondInsertIndex = method.implementation!!.instructions.size - 1
method.insertOverride(
secondInsertIndex, parameterRegister
/** same register used to return **/
)
/*
* Override every return instruction with the proxy call.
*/
MiniPlayerOverrideFingerprint.result?.let { result ->
result.mutableMethod.let { method ->
val appNameStringIndex = result.scanResult.stringsScanResult!!.matches.first().index + 2
context.toMethodWalker(method).nextMethod(appNameStringIndex, true)
.getMethod() as MutableMethod
}.apply {
implementation!!.let { implementation ->
val returnIndices = implementation.instructions
.withIndex()
.filter { (_, instruction) -> instruction.opcode == Opcode.RETURN }
.map { (index, _) -> index }
if (returnIndices.isEmpty()) throw PatchException("No return instructions found.")
// This method clobbers register p0 to return the value, calculate to override.
val returnedRegister = implementation.registerCount - parameters.size
// Hook the returned register on every return instruction.
returnIndices.forEach { index -> insertOverride(index, returnedRegister) }
}
}
return@let
} ?: throw MiniPlayerOverrideFingerprint.exception
/*
* Size check return value override.
*/
MiniPlayerResponseModelSizeCheckFingerprint.addProxyCall()
}
// Helper methods.
private fun MethodFingerprint.addProxyCall(): Triple<MutableMethod, Int, Int> {
val (method, scanIndex, parameterRegister) = this.unwrap()
method.insertOverride(scanIndex, parameterRegister)
return Triple(method, scanIndex, parameterRegister)
}
private fun MutableMethod.insertOverride(index: Int, overrideRegister: Int) {
this.addInstructions(
index,
"""
invoke-static {v$overrideRegister}, Lapp/revanced/integrations/youtube/patches/TabletMiniPlayerOverridePatch;->getTabletMiniPlayerOverride(Z)Z
move-result v$overrideRegister
"""
)
}
private fun MethodFingerprint.unwrap(): Triple<MutableMethod, Int, Int> {
val result = this.result!!
val scanIndex = result.scanResult.patternScanResult!!.endIndex
val method = result.mutableMethod
val instructions = method.implementation!!.instructions
val parameterRegister = (instructions[scanIndex] as OneRegisterInstruction).registerA
return Triple(method, scanIndex, parameterRegister)
}
}

View File

@@ -1,23 +0,0 @@
package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object MiniPlayerDimensionsCalculatorParentFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("F"),
opcodes = listOf(
Opcode.CONST_HIGH16,
Opcode.ADD_FLOAT_2ADDR,
null, // Opcode.MUL_FLOAT or Opcode.MUL_FLOAT_2ADDR
Opcode.CONST_4,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.FLOAT_TO_INT,
Opcode.INVOKE_INTERFACE,
Opcode.RETURN_VOID,
)
)

View File

@@ -1,11 +0,0 @@
package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object MiniPlayerOverrideNoContextFingerprint : MethodFingerprint(
"Z", AccessFlags.FINAL or AccessFlags.PRIVATE,
opcodes = listOf(Opcode.RETURN), // anchor to insert the instruction
)

View File

@@ -16,7 +16,7 @@ import app.revanced.patches.youtube.layout.theme.fingerprints.UseGradientLoading
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@@ -123,7 +123,7 @@ object ThemeBytecodePatch : BytecodePatch(
)
UseGradientLoadingScreenFingerprint.result?.mutableMethod?.apply {
val isEnabledIndex = indexOfFirstWideLiteralInstructionValue(GRADIENT_LOADING_SCREEN_AB_CONSTANT) + 3
val isEnabledIndex = indexOfFirstWideLiteralInstructionValueOrThrow(GRADIENT_LOADING_SCREEN_AB_CONSTANT) + 3
val isEnabledRegister = getInstruction<OneRegisterInstruction>(isEnabledIndex - 1).registerA
addInstructions(

View File

@@ -0,0 +1,103 @@
package app.revanced.patches.youtube.misc.backgroundplayback
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackManagerFingerprint
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackSettingsFingerprint
import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Remove background playback restrictions",
description = "Removes restrictions on background playback, including playing kids videos in the background.",
dependencies = [
BackgroundPlaybackResourcePatch::class,
IntegrationsPatch::class,
PlayerTypeHookPatch::class,
VideoInformationPatch::class,
SettingsPatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
]
)
]
)
@Suppress("unused")
object BackgroundPlaybackPatch : BytecodePatch(
setOf(
BackgroundPlaybackManagerFingerprint,
BackgroundPlaybackSettingsFingerprint,
KidsBackgroundPlaybackPolicyControllerFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/BackgroundPlaybackPatch;"
override fun execute(context: BytecodeContext) {
BackgroundPlaybackManagerFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"""
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z
move-result v0
return v0
"""
)
// Enable background playback option in YouTube settings
BackgroundPlaybackSettingsFingerprint.resultOrThrow().mutableMethod.apply {
val booleanCalls = implementation!!.instructions.withIndex()
.filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" }
val settingsBooleanIndex = booleanCalls.elementAt(1).index
val settingsBooleanMethod =
context.toMethodWalker(this).nextMethod(settingsBooleanIndex, true).getMethod() as MutableMethod
settingsBooleanMethod.addInstructions(
0,
"""
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideBackgroundPlaybackAvailable()Z
move-result v0
return v0
"""
)
}
// Force allowing background play for videos labeled for kids.
KidsBackgroundPlaybackPolicyControllerFingerprint.resultOrThrow().mutableMethod.addInstruction(
0,
"return-void"
)
}
}

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.misc.minimizedplayback
package app.revanced.patches.youtube.misc.backgroundplayback
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
@@ -8,7 +8,7 @@ import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
@Patch(
dependencies = [ResourceMappingPatch::class],
)
internal object MinimizedPlaybackResourcePatch : ResourcePatch() {
internal object BackgroundPlaybackResourcePatch : ResourcePatch() {
internal var prefBackgroundAndOfflineCategoryId: Long = -1
override fun execute(context: ResourceContext) {

View File

@@ -1,11 +1,11 @@
package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints
package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
internal object BackgroundPlaybackManagerFingerprint : MethodFingerprint(
"Z",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L"),

View File

@@ -1,12 +1,12 @@
package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints
package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.misc.minimizedplayback.MinimizedPlaybackResourcePatch
import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object MinimizedPlaybackSettingsFingerprint : LiteralValueFingerprint(
internal object BackgroundPlaybackSettingsFingerprint : LiteralValueFingerprint(
returnType = "Ljava/lang/String;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf(),
@@ -19,5 +19,5 @@ internal object MinimizedPlaybackSettingsFingerprint : LiteralValueFingerprint(
Opcode.IF_NEZ,
Opcode.GOTO
),
literalSupplier = { MinimizedPlaybackResourcePatch.prefBackgroundAndOfflineCategoryId }
literalSupplier = { BackgroundPlaybackResourcePatch.prefBackgroundAndOfflineCategoryId }
)

View File

@@ -1,12 +1,11 @@
package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints
package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerprint(
internal object KidsBackgroundPlaybackPolicyControllerFingerprint : LiteralValueFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("I", "L", "L"),
@@ -26,9 +25,5 @@ internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerp
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID
),
customFingerprint = { methodDef, _ ->
methodDef.implementation!!.instructions.any {
((it as? NarrowLiteralInstruction)?.narrowLiteral == 5)
}
}
literalSupplier = { 5 },
)

View File

@@ -14,14 +14,8 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildInitPlaybackRequestFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildPlayerRequestURIFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyWithModelFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.PlayerGestureConfigSyntheticFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.SetPlayerRequestClientTypeFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.*
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
@@ -86,6 +80,9 @@ object SpoofClientPatch : BytecodePatch(
// Player gesture config.
PlayerGestureConfigSyntheticFingerprint,
// Player speed menu item.
CreatePlaybackSpeedMenuItemFingerprint,
),
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
@@ -99,7 +96,7 @@ object SpoofClientPatch : BytecodePatch(
SettingsPatch.PreferenceScreen.MISC.addPreferences(
PreferenceScreen(
key = "revanced_spoof_client_screen",
sorting = Sorting.UNSORTED,
sorting = PreferenceScreen.Sorting.UNSORTED,
preferences = setOf(
SwitchPreference("revanced_spoof_client"),
SwitchPreference("revanced_spoof_client_use_ios"),
@@ -127,33 +124,6 @@ object SpoofClientPatch : BytecodePatch(
// endregion
// region fix player gesture.
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
val endIndex = it.scanResult.patternScanResult!!.endIndex
arrayOf(3, 9).forEach { offSet ->
(context.toMethodWalker(it.mutableMethod)
.nextMethod(endIndex - offSet, true)
.getMethod() as MutableMethod)
.apply {
val index = implementation!!.instructions.lastIndex
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
move-result v$register
"""
)
}
}
}
// endregion
// region Block /get_watch requests to fall back to /player requests.
BuildPlayerRequestURIFingerprint.resultOrThrow().let {
@@ -281,5 +251,59 @@ object SpoofClientPatch : BytecodePatch(
// endregion
// region Fix player gesture if spoofing to iOS.
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
val endIndex = it.scanResult.patternScanResult!!.endIndex
val downAndOutLandscapeAllowedIndex = endIndex - 3
val downAndOutPortraitAllowedIndex = endIndex - 9
arrayOf(
downAndOutLandscapeAllowedIndex,
downAndOutPortraitAllowedIndex,
).forEach { index ->
val gestureAllowedMethod = context.toMethodWalker(it.mutableMethod)
.nextMethod(index, true)
.getMethod() as MutableMethod
gestureAllowedMethod.apply {
val isAllowedIndex = getInstructions().lastIndex
val isAllowed = getInstruction<OneRegisterInstruction>(isAllowedIndex).registerA
addInstructions(
isAllowedIndex,
"""
invoke-static { v$isAllowed }, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
move-result v$isAllowed
""",
)
}
}
}
// endregion
// Fix playback speed menu item if spoofing to iOS.
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
val scanResult = it.scanResult.patternScanResult!!
if (scanResult.startIndex != 0) throw PatchException("Unexpected start index: ${scanResult.startIndex}")
it.mutableMethod.apply {
// Find the conditional check if the playback speed menu item is not created.
val shouldCreateMenuIndex = indexOfFirstInstructionOrThrow(scanResult.endIndex) { opcode == Opcode.IF_EQZ }
val shouldCreateMenuRegister = getInstruction<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
addInstructions(
shouldCreateMenuIndex,
"""
invoke-static { v$shouldCreateMenuRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenu(Z)Z
move-result v$shouldCreateMenuRegister
""",
)
}
}
// endregion
}
}

View File

@@ -8,11 +8,14 @@ import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.Instruction35cInfo
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
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.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch<Instruction35cInfo>() {
private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube"
@@ -42,20 +45,31 @@ object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch<Instruction35c
as? OneRegisterInstruction ?: return
).registerA
// IndexOutOfBoundsException is not possible here,
// IndexOutOfBoundsException is possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
// This can technically also match non-user agent string builder append methods,
// but no such occurrences are present in the app.
// Only replace string builder usage.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return
}
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
// Changing these package names will result in playback limitations,
// particularly Android VR background audio only playback.
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
opcode == Opcode.CONST_STRING &&
(reference?.string == "android.resource://" || reference?.string == "gcore_")
}
if (resourceOrGmsStringInstructionIndex >= 0) {
return
}
// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"${ORIGINAL_PACKAGE_NAME}\"",
"const-string v$targetRegister, \"$ORIGINAL_PACKAGE_NAME\"",
)
}
}

View File

@@ -0,0 +1,34 @@
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
opcodes = listOf(
Opcode.IGET_OBJECT, // First instruction of the method
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
null // MOVE_RESULT or MOVE_RESULT_OBJECT, Return value controls the creation of the playback speed menu item.
),
// 19.01 and earlier is missing the second parameter.
// Since this fingerprint is somewhat weak, work around by checking for both method parameter signatures.
customFingerprint = custom@{ methodDef, _ ->
// 19.01 and earlier parameters are: "[L"
// 19.02+ parameters are "[L", "F"
val parameterTypes = methodDef.parameterTypes
val firstParameter = parameterTypes.firstOrNull()
if (firstParameter == null || !firstParameter.startsWith("[L")) {
return@custom false
}
parameterTypes.size == 1 || (parameterTypes.size == 2 && parameterTypes[1] == "F")
}
)

View File

@@ -1,9 +1,7 @@
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.PlayerGestureConfigSyntheticFingerprint.indexOfDownAndOutAllowedInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
@@ -24,28 +22,28 @@ internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint(
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed.
Opcode.MOVE_RESULT,
Opcode.CHECK_CAST,
Opcode.IPUT_BOOLEAN,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed.
Opcode.MOVE_RESULT,
Opcode.IPUT_BOOLEAN,
Opcode.RETURN_VOID,
),
customFingerprint = { methodDef, classDef ->
// This method is always called "a" because this kind of class always has a single method.
methodDef.name == "a" && classDef.methods.count() == 2 &&
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
}
) {
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
reference.parameterTypes.isEmpty() &&
reference.returnType == "Z"
}
}
}
// This method is always called "a" because this kind of class always has a single method.
methodDef.name == "a" && classDef.methods.count() == 2 &&
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
},
)

View File

@@ -1,117 +1,11 @@
package app.revanced.patches.youtube.misc.minimizedplayback
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsFingerprint
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Minimized playback",
description = "Unlocks options for picture-in-picture and background playback.",
dependencies = [
MinimizedPlaybackResourcePatch::class,
IntegrationsPatch::class,
PlayerTypeHookPatch::class,
VideoInformationPatch::class,
SettingsPatch::class,
AddResourcesPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
]
)
]
)
@Suppress("unused")
object MinimizedPlaybackPatch : BytecodePatch(
setOf(
MinimizedPlaybackManagerFingerprint,
MinimizedPlaybackSettingsFingerprint,
KidsMinimizedPlaybackPolicyControllerFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/MinimizedPlaybackPatch;"
import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackPatch
@Deprecated("This patch class has been renamed to BackgroundPlaybackPatch.")
object MinimizedPlaybackPatch : BytecodePatch(dependencies = setOf(BackgroundPlaybackPatch::class)) {
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.MISC.addPreferences(
NonInteractivePreference("revanced_minimized_playback")
)
MinimizedPlaybackManagerFingerprint.result?.apply {
mutableMethod.addInstructions(
0,
"""
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z
move-result v0
return v0
"""
)
} ?: throw MinimizedPlaybackManagerFingerprint.exception
// Enable minimized playback option in YouTube settings
MinimizedPlaybackSettingsFingerprint.result?.apply {
val booleanCalls = method.implementation!!.instructions.withIndex()
.filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" }
val settingsBooleanIndex = booleanCalls.elementAt(1).index
val settingsBooleanMethod =
context.toMethodWalker(method).nextMethod(settingsBooleanIndex, true).getMethod() as MutableMethod
settingsBooleanMethod.addInstructions(
0,
"""
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideMinimizedPlaybackAvailable()Z
move-result v0
return v0
"""
)
} ?: throw MinimizedPlaybackSettingsFingerprint.exception
// Force allowing background play for videos labeled for kids.
// Some regions and YouTube accounts do not require this patch.
KidsMinimizedPlaybackPolicyControllerFingerprint.result?.apply {
mutableMethod.addInstruction(
0,
"return-void"
)
} ?: throw KidsMinimizedPlaybackPolicyControllerFingerprint.exception
}
}
}

View File

@@ -14,6 +14,9 @@ import app.revanced.patches.youtube.video.information.fingerprints.*
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
@@ -24,6 +27,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import com.android.tools.smali.dexlib2.util.MethodUtil
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
description = "Hooks YouTube to get information about the current playing video.",
@@ -32,6 +38,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
object VideoInformationPatch : BytecodePatch(
setOf(
PlayerInitFingerprint,
MdxPlayerDirectorSetVideoStageFingerprint,
CreateVideoPlayerSeekbarFingerprint,
PlayerControllerSetTimeReferenceFingerprint,
OnPlaybackSpeedItemClickFingerprint
@@ -42,12 +49,16 @@ object VideoInformationPatch : BytecodePatch(
private lateinit var playerInitMethod: MutableMethod
private var playerInitInsertIndex = 4
private lateinit var mdxInitMethod: MutableMethod
private var mdxInitInsertIndex = -1
private var mdxInitInsertRegister = -1
private lateinit var timeMethod: MutableMethod
private var timeInitInsertIndex = 2
private lateinit var speedSelectionInsertMethod: MutableMethod
private var speedSelectionInsertIndex = 0
private var speedSelectionValueRegister = 0
private var speedSelectionInsertIndex = -1
private var speedSelectionValueRegister = -1
// Used by other patches.
internal lateinit var setPlaybackSpeedContainerClassFieldReference: String
@@ -55,44 +66,48 @@ object VideoInformationPatch : BytecodePatch(
internal lateinit var setPlaybackSpeedMethodReference: String
override fun execute(context: BytecodeContext) {
with(PlayerInitFingerprint.result!!) {
with(PlayerInitFingerprint.resultOrThrow()) {
playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
// hook the player controller for use through integrations
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
// seek method
val seekFingerprintResultMethod = SeekFingerprint.also { it.resolve(context, classDef) }.result!!.method
val seekFingerprintResultMethod =
SeekFingerprint.also { it.resolve(context, classDef) }.resultOrThrow().method
// create helper method
val seekHelperMethod = ImmutableMethod(
seekFingerprintResultMethod.definingClass,
"seekTo",
listOf(ImmutableMethodParameter("J", null, "time")),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
null, null,
MutableMethodImplementation(4)
).toMutable()
// get enum type for the seek helper method
val seekSourceEnumType = seekFingerprintResultMethod.parameterTypes[1].toString()
// insert helper method instructions
seekHelperMethod.addInstructions(
0,
"""
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
invoke-virtual {p0, p1, p2, v0}, ${seekFingerprintResultMethod.definingClass}->${seekFingerprintResultMethod.name}(J$seekSourceEnumType)Z
move-result p1
return p1
"""
)
val seekHelperMethod = generateSeekMethodHelper(seekFingerprintResultMethod)
// add the seekTo method to the class for the integrations to call
mutableClass.methods.add(seekHelperMethod)
}
with(MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow()) {
mdxInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
// find the location of the first invoke-direct call and extract the register storing the 'this' object reference
val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
}
mdxInitInsertRegister = mdxInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
mdxInitInsertIndex = initThisIndex + 1
// hook the MDX director for use through integrations
onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx")
// MDX seek method
val mdxSeekFingerprintResultMethod =
MdxSeekFingerprint.apply { resolve(context, classDef) }.resultOrThrow().method
// create helper method
val mdxSeekHelperMethod = generateSeekMethodHelper(mdxSeekFingerprintResultMethod)
// add the seekTo method to the class for the integrations to call
mutableClass.methods.add(mdxSeekHelperMethod)
}
with(CreateVideoPlayerSeekbarFingerprint.result!!) {
val videoLengthMethodResult = VideoLengthFingerprint.also { it.resolve(context, classDef) }.result!!
@@ -103,7 +118,7 @@ object VideoInformationPatch : BytecodePatch(
addInstruction(
videoLengthMethodResult.scanResult.patternScanResult!!.endIndex,
"invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V"
"invoke-static { v$videoLengthRegister, v$dummyRegisterForLong }, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V"
)
}
}
@@ -158,6 +173,35 @@ object VideoInformationPatch : BytecodePatch(
userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
}
private fun generateSeekMethodHelper(seekMethod: Method): MutableMethod {
// create helper method
val generatedMethod = ImmutableMethod(
seekMethod.definingClass,
"seekTo",
listOf(ImmutableMethodParameter("J", null, "time")),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
null, null,
MutableMethodImplementation(4)
).toMutable()
// get enum type for the seek helper method
val seekSourceEnumType = seekMethod.parameterTypes[1].toString()
// insert helper method instructions
generatedMethod.addInstructions(
0,
"""
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
invoke-virtual { p0, p1, p2, v0 }, $seekMethod
move-result p1
return p1
"""
)
return generatedMethod
}
private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) =
addInstruction(insertIndex, "invoke-static { $register }, $descriptor")
@@ -180,6 +224,19 @@ object VideoInformationPatch : BytecodePatch(
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
)
/**
* Hook the MDX player director. Called when playing videos while casting to a big screen device.
*
* @param targetMethodClass The descriptor for the class to invoke when the player controller is created.
* @param targetMethodName The name of the static method to invoke when the player controller is created.
*/
internal fun onCreateHookMdx(targetMethodClass: String, targetMethodName: String) =
mdxInitMethod.insert(
mdxInitInsertIndex++,
"v$mdxInitInsertRegister",
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
)
/**
* Hook the video time.
* The hook is usually called once per second.

View File

@@ -0,0 +1,8 @@
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object MdxPlayerDirectorSetVideoStageFingerprint : MethodFingerprint(
strings = listOf("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state ")
)

View File

@@ -0,0 +1,23 @@
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object MdxSeekFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf("J", "L"),
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.RETURN,
),
customFingerprint = { methodDef, _ ->
// The instruction count is necessary here to avoid matching the relative version
// of the seek method we're after, which has the same function signature as the
// regular one, is in the same class, and even has the exact same 3 opcodes pattern.
methodDef.implementation!!.instructions.count() == 3
}
)

View File

@@ -4,13 +4,19 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
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
@Patch(
name = "Playback speed",
description = "Adds options to customize available playback speeds and to remember the last playback speed selected.",
dependencies = [CustomPlaybackSpeedPatch::class, RememberPlaybackSpeedPatch::class],
description = "Adds options to customize available playback speeds, remember the last playback speed selected " +
"and show a speed dialog button to the video player.",
dependencies = [
PlaybackSpeedButtonPatch::class,
CustomPlaybackSpeedPatch::class,
RememberPlaybackSpeedPatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
@@ -42,4 +48,4 @@ object PlaybackSpeedPatch : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) {
// All patches this patch depends on succeed.
}
}
}

View File

@@ -0,0 +1,38 @@
package app.revanced.patches.youtube.video.speed.button
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsBytecodePatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedPatch
@Patch(
description = "Adds the option to display playback speed dialog button in the video player.",
dependencies = [
PlaybackSpeedButtonResourcePatch::class,
CustomPlaybackSpeedPatch::class,
PlayerControlsBytecodePatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
],
)
@Suppress("unused")
object PlaybackSpeedButtonPatch : BytecodePatch(emptySet()) {
private const val SPEED_BUTTON_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/videoplayer/PlaybackSpeedDialogButton;"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_playback_speed_dialog_button"),
)
PlayerControlsBytecodePatch.initializeControl("$SPEED_BUTTON_CLASS_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$SPEED_BUTTON_CLASS_DESCRIPTOR->changeVisibility(Z)V")
}
}

View File

@@ -0,0 +1,25 @@
package app.revanced.patches.youtube.video.speed.button
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
@Patch(
dependencies = [BottomControlsResourcePatch::class],
)
internal object PlaybackSpeedButtonResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
context.copyResources(
"speedbutton",
ResourceGroup(
"drawable",
"revanced_playback_speed_dialog_button.xml",
),
)
BottomControlsResourcePatch.addControls("speedbutton")
}
}

View File

@@ -8,6 +8,7 @@ import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
@@ -99,6 +100,7 @@ fun Method.indexOfIdResourceOrThrow(resourceName: String): Int {
* Find the index of the first wide literal instruction with the given value.
*
* @return the first literal instruction with the value, or -1 if not found.
* @see indexOfFirstWideLiteralInstructionValueOrThrow
*/
fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let {
it.instructions.indexOfFirst { instruction ->
@@ -106,6 +108,18 @@ fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementati
}
} ?: -1
/**
* Find the index of the first wide literal instruction with the given value,
* or throw an exception if not found.
*
* @return the first literal instruction with the value, or throws [PatchException] if not found.
*/
fun Method.indexOfFirstWideLiteralInstructionValueOrThrow(literal: Long) : Int {
val index = indexOfFirstWideLiteralInstructionValue(literal)
if (index < 0) throw PatchException("Could not find literal value: $literal")
return index
}
/**
* Check if the method contains a literal with the given value.
*
@@ -144,7 +158,9 @@ inline fun <reified T : Reference> Instruction.getReference() = (this as? Refere
* @return The index of the first [Instruction] that matches the predicate.
*/
// TODO: delete this on next major release, the overloaded method with an optional start index serves the same purposes.
@Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)"))
// Method is deprecated, but annotation is commented out otherwise during compilation usage of the replacement is
// incorrectly flagged as deprecated.
//@Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)"))
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = indexOfFirstInstruction(0, predicate)
/**
@@ -179,6 +195,21 @@ fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, predicate: Instru
return index
}
/**
* @return The list of indices of the opcode in reverse order.
*/
fun Method.findOpcodeIndicesReversed(opcode: Opcode): List<Int> {
val indexes = implementation!!.instructions
.withIndex()
.filter { (_, instruction) -> instruction.opcode == opcode }
.map { (index, _) -> index }
.reversed()
if (indexes.isEmpty()) throw PatchException("No ${opcode.name} instructions found in: $this")
return indexes
}
/**
* Return the resolved methods of [MethodFingerprint]s early.
*/

View File

@@ -0,0 +1,263 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
</app>
<app id="youtube">
<patch id="misc.settings.SettingsResourcePatch">
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
<patch id="misc.debugging.DebuggingPatch">
</patch>
<patch id="layout.hide.general.HideLayoutComponentsPatch">
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.getpremium.HideGetPremiumPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="interaction.copyvideourl.CopyVideoUrlResourcePatch">
</patch>
<patch id="interaction.dialog.RemoveViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.downloads.DownloadsResourcePatch">
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
</patch>
<patch id="interaction.seekbar.DisablePreciseSeekingGesturePatch">
</patch>
<patch id="interaction.seekbar.EnableSeekbarTappingPatch">
</patch>
<patch id="interaction.swipecontrols.SwipeControlsResourcePatch">
</patch>
<patch id="layout.autocaptions.AutoCaptionsPatch">
</patch>
<patch id="layout.buttons.action.HideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="layout.buttons.autoplay.HideAutoplayButtonPatch">
</patch>
<patch id="layout.buttons.captions.HideCaptionsButtonPatch">
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
</patch>
<patch id="layout.buttons.cast.HideCastButtonPatch">
</patch>
<patch id="layout.buttons.navigation.NavigationButtonsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<!-- The Create button has no display name. Translate normally. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
</patch>
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'More info' should be translated using the same localized wording YouTube displays for the menu item.
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
</patch>
<patch id="layout.buttons.player.hide.HidePlayerButtonsPatch">
</patch>
<patch id="layout.hide.albumcards.AlbumCardsResourcePatch">
</patch>
<patch id="layout.hide.comments.CommentsPatch">
</patch>
<patch id="layout.hide.crowdfundingbox.CrowdfundingBoxResourcePatch">
</patch>
<patch id="layout.hide.endscreencards.HideEndscreenCardsResourcePatch">
</patch>
<patch id="layout.hide.filterbar.HideFilterBarResourcePatch">
</patch>
<patch id="layout.hide.floatingmicrophone.HideFloatingMicrophoneButtonResourcePatch">
</patch>
<patch id="layout.hide.fullscreenambientmode.DisableFullscreenAmbientModePatch">
</patch>
<patch id="layout.hide.infocards.HideInfocardsResourcePatch">
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.HideSeekbarPatch">
</patch>
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.DisableSuggestedVideoEndScreenResourcePatch">
</patch>
<patch id="layout.hide.time.HideTimestampPatch">
</patch>
<patch id="layout.panels.popup.PlayerPopupPanelsPatch">
</patch>
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.WideSearchbarPatch">
</patch>
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<!-- 'RYD' is 'Return YouTube Dislike' -->
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- 'History' should be translated using the same localized wording YouTube displays for the 'history' section in the 'You' tab. -->
</patch>
<patch id="layout.startupshortsreset.DisableResumingShortsOnStartupPatch">
</patch>
<patch id="layout.tablet.EnableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.MiniplayerPatch">
</patch>
<patch id="layout.theme.ThemeBytecodePatch">
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.AnnouncementsPatch">
</patch>
<patch id="misc.autorepeat.AutoRepeatPatch">
</patch>
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
</patch>
<patch id="misc.gms.GmsCoreSupportResourcePatch">
</patch>
<patch id="misc.links.BypassURLRedirectsPatch">
</patch>
<patch id="misc.links.OpenLinksExternallyPatch">
</patch>
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
</patch>
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
</patch>
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
</patch>
<patch id="video.quality.RememberVideoQualityPatch">
</patch>
<patch id="video.speed.button.PlaybackSpeedButtonPatch">
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.RememberPlaybackSpeedPatch">
</patch>
<patch id="video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.EnableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.SpoofClientPatch">
</patch>
<!-- This patch is no longer used, these strings are not in use, and these strings will be deleted in the future. -->
<patch id="misc.fix.playback.SpoofSignaturePatch">
</patch>
<!-- This patch is no longer used and these strings will soon be deleted. -->
<patch id="video.hdrbrightness.HDRBrightnessPatch">
</patch>
</app>
<app id="twitch">
<patch id="ad.audio.AudioAdsPatch">
</patch>
<patch id="ad.embedded.EmbeddedAdsPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="chat.antidelete.ShowDeletedMessagesPatch">
</patch>
<patch id="chat.autoclaim.AutoClaimChannelPointsPatch">
</patch>
<patch id="debug.DebugModePatch">
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
</app>
</resources>

View File

@@ -0,0 +1,263 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
</app>
<app id="youtube">
<patch id="misc.settings.SettingsResourcePatch">
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
<patch id="misc.debugging.DebuggingPatch">
</patch>
<patch id="layout.hide.general.HideLayoutComponentsPatch">
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.getpremium.HideGetPremiumPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="interaction.copyvideourl.CopyVideoUrlResourcePatch">
</patch>
<patch id="interaction.dialog.RemoveViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.downloads.DownloadsResourcePatch">
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
</patch>
<patch id="interaction.seekbar.DisablePreciseSeekingGesturePatch">
</patch>
<patch id="interaction.seekbar.EnableSeekbarTappingPatch">
</patch>
<patch id="interaction.swipecontrols.SwipeControlsResourcePatch">
</patch>
<patch id="layout.autocaptions.AutoCaptionsPatch">
</patch>
<patch id="layout.buttons.action.HideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="layout.buttons.autoplay.HideAutoplayButtonPatch">
</patch>
<patch id="layout.buttons.captions.HideCaptionsButtonPatch">
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
</patch>
<patch id="layout.buttons.cast.HideCastButtonPatch">
</patch>
<patch id="layout.buttons.navigation.NavigationButtonsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<!-- The Create button has no display name. Translate normally. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
</patch>
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'More info' should be translated using the same localized wording YouTube displays for the menu item.
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
</patch>
<patch id="layout.buttons.player.hide.HidePlayerButtonsPatch">
</patch>
<patch id="layout.hide.albumcards.AlbumCardsResourcePatch">
</patch>
<patch id="layout.hide.comments.CommentsPatch">
</patch>
<patch id="layout.hide.crowdfundingbox.CrowdfundingBoxResourcePatch">
</patch>
<patch id="layout.hide.endscreencards.HideEndscreenCardsResourcePatch">
</patch>
<patch id="layout.hide.filterbar.HideFilterBarResourcePatch">
</patch>
<patch id="layout.hide.floatingmicrophone.HideFloatingMicrophoneButtonResourcePatch">
</patch>
<patch id="layout.hide.fullscreenambientmode.DisableFullscreenAmbientModePatch">
</patch>
<patch id="layout.hide.infocards.HideInfocardsResourcePatch">
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.HideSeekbarPatch">
</patch>
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.DisableSuggestedVideoEndScreenResourcePatch">
</patch>
<patch id="layout.hide.time.HideTimestampPatch">
</patch>
<patch id="layout.panels.popup.PlayerPopupPanelsPatch">
</patch>
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.WideSearchbarPatch">
</patch>
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<!-- 'RYD' is 'Return YouTube Dislike' -->
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- 'History' should be translated using the same localized wording YouTube displays for the 'history' section in the 'You' tab. -->
</patch>
<patch id="layout.startupshortsreset.DisableResumingShortsOnStartupPatch">
</patch>
<patch id="layout.tablet.EnableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.MiniplayerPatch">
</patch>
<patch id="layout.theme.ThemeBytecodePatch">
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.AnnouncementsPatch">
</patch>
<patch id="misc.autorepeat.AutoRepeatPatch">
</patch>
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
</patch>
<patch id="misc.gms.GmsCoreSupportResourcePatch">
</patch>
<patch id="misc.links.BypassURLRedirectsPatch">
</patch>
<patch id="misc.links.OpenLinksExternallyPatch">
</patch>
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
</patch>
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
</patch>
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
</patch>
<patch id="video.quality.RememberVideoQualityPatch">
</patch>
<patch id="video.speed.button.PlaybackSpeedButtonPatch">
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.RememberPlaybackSpeedPatch">
</patch>
<patch id="video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.EnableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.SpoofClientPatch">
</patch>
<!-- This patch is no longer used, these strings are not in use, and these strings will be deleted in the future. -->
<patch id="misc.fix.playback.SpoofSignaturePatch">
</patch>
<!-- This patch is no longer used and these strings will soon be deleted. -->
<patch id="video.hdrbrightness.HDRBrightnessPatch">
</patch>
</app>
<app id="twitch">
<patch id="ad.audio.AudioAdsPatch">
</patch>
<patch id="ad.embedded.EmbeddedAdsPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="chat.antidelete.ShowDeletedMessagesPatch">
</patch>
<patch id="chat.autoclaim.AutoClaimChannelPointsPatch">
</patch>
<patch id="debug.DebugModePatch">
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
</app>
</resources>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,263 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
</app>
<app id="youtube">
<patch id="misc.settings.SettingsResourcePatch">
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
<patch id="misc.debugging.DebuggingPatch">
</patch>
<patch id="layout.hide.general.HideLayoutComponentsPatch">
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.getpremium.HideGetPremiumPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="interaction.copyvideourl.CopyVideoUrlResourcePatch">
</patch>
<patch id="interaction.dialog.RemoveViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.downloads.DownloadsResourcePatch">
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
</patch>
<patch id="interaction.seekbar.DisablePreciseSeekingGesturePatch">
</patch>
<patch id="interaction.seekbar.EnableSeekbarTappingPatch">
</patch>
<patch id="interaction.swipecontrols.SwipeControlsResourcePatch">
</patch>
<patch id="layout.autocaptions.AutoCaptionsPatch">
</patch>
<patch id="layout.buttons.action.HideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="layout.buttons.autoplay.HideAutoplayButtonPatch">
</patch>
<patch id="layout.buttons.captions.HideCaptionsButtonPatch">
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
</patch>
<patch id="layout.buttons.cast.HideCastButtonPatch">
</patch>
<patch id="layout.buttons.navigation.NavigationButtonsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<!-- The Create button has no display name. Translate normally. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
</patch>
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'More info' should be translated using the same localized wording YouTube displays for the menu item.
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
</patch>
<patch id="layout.buttons.player.hide.HidePlayerButtonsPatch">
</patch>
<patch id="layout.hide.albumcards.AlbumCardsResourcePatch">
</patch>
<patch id="layout.hide.comments.CommentsPatch">
</patch>
<patch id="layout.hide.crowdfundingbox.CrowdfundingBoxResourcePatch">
</patch>
<patch id="layout.hide.endscreencards.HideEndscreenCardsResourcePatch">
</patch>
<patch id="layout.hide.filterbar.HideFilterBarResourcePatch">
</patch>
<patch id="layout.hide.floatingmicrophone.HideFloatingMicrophoneButtonResourcePatch">
</patch>
<patch id="layout.hide.fullscreenambientmode.DisableFullscreenAmbientModePatch">
</patch>
<patch id="layout.hide.infocards.HideInfocardsResourcePatch">
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.HideSeekbarPatch">
</patch>
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.DisableSuggestedVideoEndScreenResourcePatch">
</patch>
<patch id="layout.hide.time.HideTimestampPatch">
</patch>
<patch id="layout.panels.popup.PlayerPopupPanelsPatch">
</patch>
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.WideSearchbarPatch">
</patch>
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<!-- 'RYD' is 'Return YouTube Dislike' -->
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- 'History' should be translated using the same localized wording YouTube displays for the 'history' section in the 'You' tab. -->
</patch>
<patch id="layout.startupshortsreset.DisableResumingShortsOnStartupPatch">
</patch>
<patch id="layout.tablet.EnableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.MiniplayerPatch">
</patch>
<patch id="layout.theme.ThemeBytecodePatch">
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.AnnouncementsPatch">
</patch>
<patch id="misc.autorepeat.AutoRepeatPatch">
</patch>
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
</patch>
<patch id="misc.gms.GmsCoreSupportResourcePatch">
</patch>
<patch id="misc.links.BypassURLRedirectsPatch">
</patch>
<patch id="misc.links.OpenLinksExternallyPatch">
</patch>
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
</patch>
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
</patch>
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
</patch>
<patch id="video.quality.RememberVideoQualityPatch">
</patch>
<patch id="video.speed.button.PlaybackSpeedButtonPatch">
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.RememberPlaybackSpeedPatch">
</patch>
<patch id="video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.EnableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.SpoofClientPatch">
</patch>
<!-- This patch is no longer used, these strings are not in use, and these strings will be deleted in the future. -->
<patch id="misc.fix.playback.SpoofSignaturePatch">
</patch>
<!-- This patch is no longer used and these strings will soon be deleted. -->
<patch id="video.hdrbrightness.HDRBrightnessPatch">
</patch>
</app>
<app id="twitch">
<patch id="ad.audio.AudioAdsPatch">
</patch>
<patch id="ad.embedded.EmbeddedAdsPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="chat.antidelete.ShowDeletedMessagesPatch">
</patch>
<patch id="chat.autoclaim.AutoClaimChannelPointsPatch">
</patch>
<patch id="debug.DebugModePatch">
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
</app>
</resources>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,537 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
<string name="revanced_settings_confirm_user_dialog_title">Желаете ли да продължите?</string>
<string name="revanced_settings_reset">Нулиране</string>
<string name="revanced_settings_restart_title">Опреснете и рестартирайте</string>
<string name="revanced_settings_restart">Рестартиране</string>
<string name="revanced_settings_import">Импортиране</string>
<string name="revanced_settings_import_copy">Копиране</string>
<string name="revanced_settings_import_reset">Настройките на ReVanced бяха нулирани</string>
<string name="revanced_settings_import_success">Следните настройки бяха импортирани успешно: %d</string>
<string name="revanced_settings_import_failure_parse">Импортирането беше неуспешно: %s</string>
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="gms_core_dialog_open_website_text">Отвори сайта</string>
</patch>
</app>
<app id="youtube">
<patch id="misc.settings.SettingsResourcePatch">
<string name="revanced_settings_about_links_dev_header">Забележка</string>
<string name="revanced_pref_import_export_title">Импортиране / Експортиране</string>
<string name="revanced_pref_import_export_summary">Импортиране / Експортиране на ReVanced настройките</string>
</patch>
<patch id="misc.settings.SettingsPatch">
<string name="revanced_settings_screen_00_about_title">Относно</string>
<string name="revanced_settings_screen_01_ads_title">Реклами</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">Алтернативни миниатюри</string>
<string name="revanced_settings_screen_03_feed_title">Поток</string>
<string name="revanced_settings_screen_04_player_title">Плейър</string>
<string name="revanced_settings_screen_05_general_title">Общо оформление</string>
<string name="revanced_settings_screen_06_shorts_title">Shorts</string>
<string name="revanced_settings_screen_07_seekbar_title">Seekbar</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Контроли с плъзгане</string>
<string name="revanced_settings_screen_11_misc_title">Разни</string>
<string name="revanced_settings_screen_12_video_title">Видео</string>
</patch>
<patch id="misc.debugging.DebuggingPatch">
<string name="revanced_debug_screen_title">Отстраняване на грешки</string>
<string name="revanced_debug_screen_summary">Активиране или деактивиране на отстраняването на грешки</string>
<string name="revanced_debug_title">Дневник на отстраняването на грешки</string>
<string name="revanced_debug_summary_on">Дневникът за остраняване на грешки е активиран</string>
<string name="revanced_debug_summary_off">Дневникът за остраняване на грешки е деактивиран</string>
<string name="revanced_debug_protobuffer_title">Буфер на протокола за дневника</string>
<string name="revanced_debug_protobuffer_summary_on">Файлове с дневници за грешки включват буфера</string>
<string name="revanced_debug_protobuffer_summary_off">Файлове с дневници за грешки не включват буфера</string>
<string name="revanced_debug_stacktrace_title">Следи от стека на дневника</string>
<string name="revanced_debug_stacktrace_summary_on">Дневникът за отстраняване на грешки съдържа следи от стека</string>
<string name="revanced_debug_stacktrace_summary_off">Дневникът за отстраняване на грешки не съдържа следи от стека</string>
<string name="revanced_debug_toast_on_error_title">Покажи системно съобщение при ReVanced грешка</string>
<string name="revanced_debug_toast_on_error_summary_on">Системно съобщение ще бъде показано, ако се появи грешка</string>
<string name="revanced_debug_toast_on_error_summary_off">Системно съобщение няма да бъде показано, ако се появи грешка</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">Ако изключите системните съобщения, ще скриете всички уведомления за ReVanced грешки. \n\nНяма да бъдете уведомени, ако настъпят неочаквани събития.</string>
</patch>
<patch id="layout.hide.general.HideLayoutComponentsPatch">
<string name="revanced_hide_gray_separator_title">Скриване на сивия разделител</string>
<string name="revanced_hide_gray_separator_summary_on">Сивите разделители са скрити</string>
<string name="revanced_hide_gray_separator_summary_off">Сивите разделители са показани</string>
<string name="revanced_hide_channel_watermark_title">Скриване на водния знак на канала</string>
<string name="revanced_hide_channel_watermark_summary_on">Водният знак е скрит</string>
<string name="revanced_hide_channel_watermark_summary_off">Водният знак е показан</string>
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
<string name="revanced_hide_join_membership_button_summary_on">Бутонът е скрит</string>
<string name="revanced_hide_join_membership_button_summary_off">Бутонът е показан</string>
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<string name="revanced_hide_for_you_shelf_summary_on">Рафтът е скрит</string>
<string name="revanced_hide_for_you_shelf_summary_off">Рафтът е показан</string>
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<string name="revanced_hide_notify_me_button_summary_on">Бутонът е скрит</string>
<string name="revanced_hide_notify_me_button_summary_off">Бутонът е показан</string>
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
<string name="revanced_hide_search_result_recommendations_summary_on">Препоръките са скрити</string>
<string name="revanced_hide_search_result_recommendations_summary_off">Препоръките са показани</string>
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<string name="revanced_hide_show_more_button_summary_on">Бутонът е скрит</string>
<string name="revanced_hide_show_more_button_summary_off">Бутонът е показан</string>
<string name="revanced_hide_timed_reactions_title">Скриване на времевите реакции</string>
<string name="revanced_hide_timed_reactions_summary_on">Времевите реакции са скрити</string>
<string name="revanced_hide_timed_reactions_summary_off">Времевите реакции са показани</string>
<string name="revanced_hide_search_result_shelf_header_title">Скриване на заглавието на рафта с резултатите от търсенето</string>
<string name="revanced_hide_search_result_shelf_header_summary_on">Заглавието на рафта е скрито</string>
<string name="revanced_hide_search_result_shelf_header_summary_off">Заглавието е показано</string>
<string name="revanced_hide_channel_guidelines_title">Скриване на насоките на канала</string>
<string name="revanced_hide_channel_guidelines_summary_on">Насоките на канала са скрити</string>
<string name="revanced_hide_channel_guidelines_summary_off">Насоките на канала са показани</string>
<string name="revanced_hide_expandable_chip_title">Скриване на разширяемия чип под видеоклиповете</string>
<string name="revanced_hide_expandable_chip_summary_on">Разширяващите се чипове са скрити</string>
<string name="revanced_hide_expandable_chip_summary_off">Разширяващите се чипове са показани</string>
<string name="revanced_hide_video_quality_menu_footer_title">Скриване на футъра на менюто за качество на видеото</string>
<string name="revanced_hide_video_quality_menu_footer_summary_on">Футърът на менюто за качество на видеото е скрит</string>
<string name="revanced_hide_video_quality_menu_footer_summary_off">Футърът на менюто за качество на видеото е показан</string>
<string name="revanced_hide_community_posts_title">Скриване на публикациите от общността</string>
<string name="revanced_hide_community_posts_summary_on">Публикациите от общността са скрити</string>
<string name="revanced_hide_community_posts_summary_off">Публикациите от общността са показани</string>
<string name="revanced_hide_compact_banner_title">Скриване на компактните банери</string>
<string name="revanced_hide_compact_banner_summary_on">Компактните банери са скрити</string>
<string name="revanced_hide_compact_banner_summary_off">Компактните банери са показани</string>
<string name="revanced_hide_movies_section_title">Скриване на раздела за филми</string>
<string name="revanced_hide_movies_section_summary_on">Разделът за филми е скрит</string>
<string name="revanced_hide_movies_section_summary_off">Разделът за филми е показан</string>
<string name="revanced_hide_feed_survey_title">Скриване на анкетите в новинарския поток</string>
<string name="revanced_hide_feed_survey_summary_on">Aнкетите в новинарския поток са скрити</string>
<string name="revanced_hide_feed_survey_summary_off">Aнкетите в новинарския поток са показани</string>
<string name="revanced_hide_community_guidelines_title">Скриване на насоките на общността</string>
<string name="revanced_hide_community_guidelines_summary_on">Насоките на общността са скрити</string>
<string name="revanced_hide_community_guidelines_summary_off">Насоките на общността са показани</string>
<string name="revanced_hide_subscribers_community_guidelines_title">Скриване на насоките за общността на абонатите</string>
<string name="revanced_hide_subscribers_community_guidelines_summary_on">Насоките за общността на абонатите са скрити</string>
<string name="revanced_hide_subscribers_community_guidelines_summary_off">Насоките за общността на абонатите са показани</string>
<string name="revanced_hide_channel_member_shelf_title">Скриване на рафта на канала</string>
<string name="revanced_hide_channel_member_shelf_summary_on">Рафтът на канала е скрит</string>
<string name="revanced_hide_channel_member_shelf_summary_off">Рафтът на канала е показан</string>
<string name="revanced_hide_emergency_box_title">Скриване аварийните кутии</string>
<string name="revanced_hide_emergency_box_summary_on">Аварийните кутии са скрити</string>
<string name="revanced_hide_emergency_box_summary_off">Аварийните кутии са показани</string>
<string name="revanced_hide_info_panels_title">Скриване на информационните панели</string>
<string name="revanced_hide_info_panels_summary_on">Информационните панели са скрити</string>
<string name="revanced_hide_info_panels_summary_off">Информационните панели са показани</string>
<string name="revanced_hide_medical_panels_title">Скриване на медицинските панели</string>
<string name="revanced_hide_medical_panels_summary_on">Медицинските панели са скрити</string>
<string name="revanced_hide_medical_panels_summary_off">Медицинските панели са показани</string>
<string name="revanced_hide_channel_bar_title">Скриване на лентата на канала</string>
<string name="revanced_hide_channel_bar_summary_on">Лентата на канала е скрита</string>
<string name="revanced_hide_channel_bar_summary_off">Лентата на канала е показана</string>
<string name="revanced_hide_quick_actions_title">Скриване на бързите действия в режим на цял екран</string>
<string name="revanced_hide_quick_actions_summary_on">Бързите действия в режим на цял екран са скрити</string>
<string name="revanced_hide_quick_actions_summary_off">Бързите действия в режим на цял екран са показани</string>
<string name="revanced_hide_related_videos_title">Скриване на свързаните видеоклипове в бързите действия</string>
<string name="revanced_hide_related_videos_summary_on">Свързаните видеоклипове в бързите действия са скирти</string>
<string name="revanced_hide_related_videos_summary_off">Свързаните видеоклипове в бързите действия са показани</string>
<string name="revanced_hide_image_shelf_title">Скриване на рафта с изображения в резултатите от търсенето</string>
<string name="revanced_hide_image_shelf_summary_on">Рафтът с изображения в резултатите от търсенето е скрит</string>
<string name="revanced_hide_image_shelf_summary_off">Рафтът с изображения в резултатите от търсенето е показан</string>
<string name="revanced_hide_latest_posts_ads_title">Скриване на последните публикации</string>
<string name="revanced_hide_latest_posts_ads_summary_on">Последните публикации са скрити</string>
<string name="revanced_hide_latest_posts_ads_summary_off">Последните публикации са показани</string>
<string name="revanced_hide_mix_playlists_title">Скриване на разбърканите плейлисти</string>
<string name="revanced_hide_mix_playlists_summary_on">Разбърканите плейлисти са скрити</string>
<string name="revanced_hide_mix_playlists_summary_off">Разбърканите плейлисти са показани</string>
<string name="revanced_hide_artist_cards_title">Скриване на картите на артистите</string>
<string name="revanced_hide_artist_cards_summary_on">Картите на артистите са скрити</string>
<string name="revanced_hide_artist_cards_summary_off">Картите на артистите са показани</string>
<string name="revanced_hide_chips_shelf_title">Скриване на рафта за чипове</string>
<string name="revanced_hide_chips_shelf_summary_on">Рафтът за чипове е скрит</string>
<string name="revanced_hide_chips_shelf_summary_off">Рафтът за чипове е показан</string>
<string name="revanced_hide_info_cards_section_title">Скриване на раздела за информационни карти</string>
<string name="revanced_hide_info_cards_section_summary_on">Разделът за информационни карти е скрит</string>
<string name="revanced_hide_info_cards_section_summary_off">Разделът за информационни карти е показан</string>
<string name="revanced_hide_transcript_section_summary_on">Разделът за транскрипция е скрит</string>
<string name="revanced_hide_transcript_section_summary_off">Разделът за транскрипция е показан</string>
<string name="revanced_hide_description_components_screen_title">Описание на видеото</string>
<string name="revanced_hide_description_components_screen_summary">Скриване или показване на компонентите за описание на видеоклиповете</string>
<string name="revanced_custom_filter_screen_title">Потребителски филтър</string>
<string name="revanced_custom_filter_screen_summary">Скриване на компоненти с помощта на потребителски филтри</string>
<string name="revanced_custom_filter_title">Активиране на потребителските филтри</string>
<string name="revanced_custom_filter_summary_on">Потребителският филтър е активиран</string>
<string name="revanced_custom_filter_summary_off">Потребителският филтър е деактивиран</string>
<string name="revanced_custom_filter_strings_title">Потребителски филтър</string>
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<string name="revanced_custom_filter_strings_summary">Списък с низове за изграждане на пътя на компонента, които да се филтрират, разделени с нов ред</string>
<string name="revanced_custom_filter_toast_invalid_syntax">Невалиден потребителски филтър: %s</string>
<string name="revanced_hide_keyword_content_screen_title">Скриване на съдържанието с ключови думи</string>
<string name="revanced_hide_keyword_content_screen_summary">Скриване на видеоклипове в резултатите от търсенуя и в новинарския поток с помощта на филтри с ключови думи</string>
<string name="revanced_hide_keyword_content_home_title">Скриване на видеоклипове на началната страница с ключови думи</string>
<string name="revanced_hide_keyword_content_home_summary_on">Видеоклиповете на началния екран са филтрирани с ключови думи</string>
<string name="revanced_hide_keyword_content_home_summary_off">Видеоклиповете на началния екран не са филтрирани с ключови думи</string>
<string name="revanced_hide_keyword_content_subscriptions_title">Скриване на видеоклипове от абонаменти с ключови думи</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">Видеоклиповете в раздела за абонаменти са са филтрирани с ключови думи</string>
<string name="revanced_hide_keyword_content_search_title">Скриване на резултати от търсения с ключови думи</string>
<string name="revanced_hide_keyword_content_search_summary_on">Резултатите от търсения са филтрирани с ключови думи</string>
<string name="revanced_hide_keyword_content_search_summary_off">Резултатите от търсения не са филтрирани с ключови думи</string>
<string name="revanced_hide_keyword_content_phrases_title">Ключови думи, които да бъдат скрити</string>
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<string name="revanced_hide_keyword_content_phrases_summary">Ключови думи и фрази, които да бъдат скрити, разделени с нови редове\n\nДуми с главни букви в средата трябва да бъдат въведени с големи букви (например: iPhone, TikTok, LeBlanc)</string>
<string name="revanced_hide_keyword_content_about_title">За филтриране с ключови думи</string>
<string name="revanced_hide_keyword_content_about_summary">Начало/Абонамент/Резултатите от търсенето се филтрират, за да се скрие съдържанието, което съответства на ключовите фрази\n\nОграничения\n• Някои кратки видеоклипове може да не са скрити\n• Някои компоненти на потребителския интерфейс може да не са скрити\n• Търсенето на ключова дума може да не покаже резултати</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Скриване на общите реклами</string>
<string name="revanced_hide_general_ads_summary_on">Общите реклами са скрити</string>
<string name="revanced_hide_general_ads_summary_off">Общите реклами са показани</string>
<string name="revanced_hide_fullscreen_ads_title">Скриване на рекламите в режим на цял екран</string>
<string name="revanced_hide_fullscreen_ads_summary_off">Рекламите в режим на цял екран са показани</string>
<string name="revanced_hide_buttoned_ads_summary_on">Рекламите като бутон са скрити</string>
<string name="revanced_hide_buttoned_ads_summary_off">Рекламите като бутон са показани</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_summary_on">Банерът е скрит</string>
<string name="revanced_hide_products_banner_summary_off">Банерът е показан</string>
<string name="revanced_hide_shopping_links_title">Скриване на връзките за пазаруване в описанието на видеоклипа</string>
<string name="revanced_hide_shopping_links_summary_on">Връзките за пазаруване са скрити</string>
<string name="revanced_hide_shopping_links_summary_off">Връзките за пазаруване са показани</string>
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_visit_store_button_summary_on">Бутонът е скрит</string>
<string name="revanced_hide_visit_store_button_summary_off">Бутонът е показан</string>
<string name="revanced_hide_web_search_results_title">Скриване на резултатите от уеб търсенето</string>
<string name="revanced_hide_web_search_results_summary_on">Резултатите от уеб търсенето са скрити</string>
<string name="revanced_hide_web_search_results_summary_off">Резултатите от уеб търсенето са показани</string>
<string name="revanced_hide_merchandise_banners_title">Скриване на банерите за стоки</string>
<string name="revanced_hide_merchandise_banners_summary_on">Банерите за стоки са скрити</string>
<string name="revanced_hide_merchandise_banners_summary_off">Банерите за стоки са показани</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.getpremium.HideGetPremiumPatch">
<string name="revanced_hide_get_premium_title">Скриване на YouTube Premium промоции</string>
<string name="revanced_hide_get_premium_summary_on">YouTube Premium промоциите под видео плейъра са скрити</string>
<string name="revanced_hide_get_premium_summary_off">YouTube Premium промоциите под видео плейъра са показани</string>
</patch>
<patch id="ad.video.VideoAdsPatch">
<string name="revanced_hide_video_ads_title">Скриване на видео рекламите</string>
<string name="revanced_hide_video_ads_summary_on">Видео рекламите са скрити</string>
<string name="revanced_hide_video_ads_summary_off">Видео рекламите са показани</string>
</patch>
<patch id="interaction.copyvideourl.CopyVideoUrlResourcePatch">
<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_off">Бутонът не е показан</string>
<string name="revanced_copy_video_url_timestamp_title">Показване на бутона за копиране на URL адреса на времевия отпечатък на видеоклипа</string>
<string name="revanced_copy_video_url_timestamp_summary_off">Бутонът не е показан</string>
</patch>
<patch id="interaction.dialog.RemoveViewerDiscretionDialogPatch">
<string name="revanced_remove_viewer_discretion_dialog_title">Скриване на прозореца за възрастово ограничение</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_on">Диалоговият прозорец ще бъде премахнат</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">Диалоговият прозорец ще бъде показан</string>
</patch>
<patch id="interaction.downloads.DownloadsResourcePatch">
<string name="revanced_external_downloader_screen_title">Външни изтегляния</string>
<string name="revanced_external_downloader_screen_summary">Настройки за използване на външно приложение за изтегляне</string>
<string name="revanced_external_downloader_title">Показване на бутона за изтегляне чрез външно приложение</string>
<string name="revanced_external_downloader_summary_on">Бутонът за изтегляне е показан в плейъра</string>
<string name="revanced_external_downloader_summary_off">Бутонът за изтегляне не е показан в плейъра</string>
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
<string name="revanced_external_downloader_action_button_title">Отмяна на бутона за изтегляне</string>
<string name="revanced_external_downloader_action_button_summary_on">Бутонът за изтегляне отваря избраното от Вас външно приложение за изтегляне</string>
<string name="revanced_external_downloader_action_button_summary_off">Бутонът за изтегляне отваря вграденото приложение за изтегляне</string>
<string name="revanced_external_downloader_name_title">Име на пакета на изтеглящото приложение</string>
<string name="revanced_external_downloader_name_summary">Име на пакета на приложението за изтегляне, като NewPipe или Seal</string>
</patch>
<patch id="interaction.seekbar.DisablePreciseSeekingGesturePatch">
<string name="revanced_disable_precise_seeking_gesture_title">Деактивиране на жеста за точно търсене</string>
<string name="revanced_disable_precise_seeking_gesture_summary_on">Жестът е деактивиран</string>
<string name="revanced_disable_precise_seeking_gesture_summary_off">Жестът е активиран</string>
</patch>
<patch id="interaction.seekbar.EnableSeekbarTappingPatch">
<string name="revanced_seekbar_tapping_title">Активиране на докосването на лентата за време</string>
<string name="revanced_seekbar_tapping_summary_on">Докосването на лентата за време е включено</string>
<string name="revanced_seekbar_tapping_summary_off">Докосването на лентата за време е изключено</string>
</patch>
<patch id="interaction.swipecontrols.SwipeControlsResourcePatch">
<string name="revanced_swipe_brightness_title">Задаване на яркост чрез плъзгане</string>
<string name="revanced_swipe_brightness_summary_on">Задаването на яркост чрез плъзгане е включено</string>
<string name="revanced_swipe_brightness_summary_off">Задаването на яркост чрез плъзгане е изключено</string>
<string name="revanced_swipe_volume_title">Настройване на звука чрез плъзгане</string>
<string name="revanced_swipe_volume_summary_on">Настройването на звука чрез плъзгане е включено</string>
<string name="revanced_swipe_volume_summary_off">Настройването на звука чрез плъзгане е изключено</string>
<string name="revanced_swipe_press_to_engage_title">Активиране на жеста натискане за преместване</string>
<string name="revanced_swipe_press_to_engage_summary_on">Натискането при плъзгане е включено</string>
<string name="revanced_swipe_press_to_engage_summary_off">Натискането при плъзгане е изключено</string>
<string name="revanced_swipe_haptic_feedback_title">Активиране на хептичната обратна връзка</string>
<string name="revanced_swipe_haptic_feedback_summary_on">Хептичната обратна връзка е активирана</string>
<string name="revanced_swipe_haptic_feedback_summary_off">Хептичната обратна връзка е деактивирана</string>
<string name="revanced_swipe_save_and_restore_brightness_title">Запазване и възстановяване на яркостта</string>
<string name="revanced_swipe_save_and_restore_brightness_summary_on">Запазване и възстаовяване яркостта при включване или изключване на цял екран</string>
<string name="revanced_swipe_save_and_restore_brightness_summary_off">Не се запазва или възстаовява яркостта при включване или изключване на цял екран</string>
<string name="revanced_swipe_overlay_timeout_title">Задръжка на плъзгащата контрола</string>
<string name="revanced_swipe_overlay_timeout_summary">Време за което плъзгащата контрола е видима</string>
<string name="revanced_swipe_text_overlay_size_title">Размер на текста при плъзгане</string>
<string name="revanced_swipe_text_overlay_size_summary">Размера на текста на плъзгащите контроли</string>
<string name="revanced_swipe_overlay_background_alpha_title">Видимост на фона на плъзгащите контроли</string>
<string name="revanced_swipe_overlay_background_alpha_summary">Видимостта на фона на плъзгащите контроли</string>
<string name="revanced_swipe_threshold_title">Праг на величината на плъзгане</string>
<string name="revanced_swipe_threshold_summary">Праг преди да се осъществи плъзгането</string>
</patch>
<patch id="layout.autocaptions.AutoCaptionsPatch">
</patch>
<patch id="layout.buttons.action.HideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="layout.buttons.autoplay.HideAutoplayButtonPatch">
</patch>
<patch id="layout.buttons.captions.HideCaptionsButtonPatch">
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
</patch>
<patch id="layout.buttons.cast.HideCastButtonPatch">
</patch>
<patch id="layout.buttons.navigation.NavigationButtonsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<!-- The Create button has no display name. Translate normally. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
</patch>
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'More info' should be translated using the same localized wording YouTube displays for the menu item.
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
</patch>
<patch id="layout.buttons.player.hide.HidePlayerButtonsPatch">
</patch>
<patch id="layout.hide.albumcards.AlbumCardsResourcePatch">
</patch>
<patch id="layout.hide.comments.CommentsPatch">
</patch>
<patch id="layout.hide.crowdfundingbox.CrowdfundingBoxResourcePatch">
</patch>
<patch id="layout.hide.endscreencards.HideEndscreenCardsResourcePatch">
</patch>
<patch id="layout.hide.filterbar.HideFilterBarResourcePatch">
</patch>
<patch id="layout.hide.floatingmicrophone.HideFloatingMicrophoneButtonResourcePatch">
</patch>
<patch id="layout.hide.fullscreenambientmode.DisableFullscreenAmbientModePatch">
</patch>
<patch id="layout.hide.infocards.HideInfocardsResourcePatch">
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.HideSeekbarPatch">
</patch>
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_channel_bar_title">Скриване на лентата на канала</string>
<string name="revanced_hide_shorts_channel_bar_summary_on">Лентата на канала е скрита</string>
<string name="revanced_hide_shorts_channel_bar_summary_off">Лентата на канала е показана</string>
</patch>
<patch id="layout.hide.suggestedvideoendscreen.DisableSuggestedVideoEndScreenResourcePatch">
</patch>
<patch id="layout.hide.time.HideTimestampPatch">
</patch>
<patch id="layout.panels.popup.PlayerPopupPanelsPatch">
</patch>
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Скрито</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<string name="revanced_ryd_about">Относно</string>
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.WideSearchbarPatch">
</patch>
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<string name="revanced_sb_enable_sb">Включване на SponsorBlock</string>
<string name="revanced_sb_appearance_category">Облик</string>
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<string name="revanced_sb_guidelines_preference_title">Преглед на ръководните линии</string>
<string name="revanced_sb_guidelines_popup_already_read">Вече прочетох</string>
<string name="revanced_sb_general">Общи</string>
<string name="revanced_sb_general_uuid_sum">Това трябва да се държи тайно. То е като парола и не трябва да се споделя с никого. Ако някой го има, то той може да се представи вместо вас</string>
<string name="revanced_sb_api_url_reset">Нулиране URL адреса на API</string>
<string name="revanced_sb_api_url_changed">URL адресът на API е променен</string>
<string name="revanced_sb_settings_ie">Настройки за внасяне и изнасяне</string>
<string name="revanced_sb_settings_copy">Копиране</string>
<string name="revanced_sb_segments_sponsor">Спонсор</string>
<string name="revanced_sb_skip_automatically">Да се прескача от само себе си</string>
<string name="revanced_sb_skip_ignore">Изключване</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<string name="revanced_sb_vote_upvote">Положителен вот</string>
<string name="revanced_sb_vote_downvote">Отрицателен вот</string>
<string name="revanced_sb_vote_category">Промяна на категорията</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_mark_start">начало</string>
<string name="revanced_sb_new_segment_mark_end">край</string>
<string name="revanced_sb_new_segment_now">сега</string>
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<string name="revanced_sb_new_segment_edit_by_hand_content">Желаете ли да редактирате времената за начало или край на частта?</string>
<string name="revanced_sb_stats">Статистика</string>
<!-- Shown in the settings preferences, and translations can be any text length. -->
<string name="revanced_sb_stats_loading">Зареждане...</string>
<string name="revanced_sb_stats_sb_disabled">SponsorBlock е изключено</string>
<string name="revanced_sb_stats_username_changed">Потребителското име е успешно променено</string>
<string name="revanced_sb_color_changed">Цветът е променен</string>
<string name="revanced_sb_reset">Нулиране</string>
<string name="revanced_sb_about">Относно</string>
</patch>
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<!-- 'RYD' is 'Return YouTube Dislike' -->
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<string name="revanced_start_page_entry_0">По подразбиране</string>
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<string name="revanced_start_page_entry_5">Shorts</string>
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- 'History' should be translated using the same localized wording YouTube displays for the 'history' section in the 'You' tab. -->
</patch>
<patch id="layout.startupshortsreset.DisableResumingShortsOnStartupPatch">
</patch>
<patch id="layout.tablet.EnableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.MiniplayerPatch">
</patch>
<patch id="layout.theme.ThemeBytecodePatch">
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.AnnouncementsPatch">
<string name="revanced_announcements_dialog_dismiss">Отхвърли</string>
</patch>
<patch id="misc.autorepeat.AutoRepeatPatch">
</patch>
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
</patch>
<patch id="misc.gms.GmsCoreSupportResourcePatch">
</patch>
<patch id="misc.links.BypassURLRedirectsPatch">
</patch>
<patch id="misc.links.OpenLinksExternallyPatch">
</patch>
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
</patch>
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
</patch>
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
</patch>
<patch id="video.quality.RememberVideoQualityPatch">
</patch>
<patch id="video.speed.button.PlaybackSpeedButtonPatch">
<string name="revanced_playback_speed_dialog_button_summary_on">Бутонът е показан</string>
<string name="revanced_playback_speed_dialog_button_summary_off">Бутонът не е показан</string>
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.RememberPlaybackSpeedPatch">
</patch>
<patch id="video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.EnableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.SpoofClientPatch">
</patch>
<!-- This patch is no longer used, these strings are not in use, and these strings will be deleted in the future. -->
<patch id="misc.fix.playback.SpoofSignaturePatch">
</patch>
<!-- This patch is no longer used and these strings will soon be deleted. -->
<patch id="video.hdrbrightness.HDRBrightnessPatch">
</patch>
</app>
<app id="twitch">
<patch id="ad.audio.AudioAdsPatch">
</patch>
<patch id="ad.embedded.EmbeddedAdsPatch">
<string name="revanced_block_embedded_ads_entry_1">Деактивирано</string>
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="chat.antidelete.ShowDeletedMessagesPatch">
</patch>
<patch id="chat.autoclaim.AutoClaimChannelPointsPatch">
</patch>
<patch id="debug.DebugModePatch">
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
</patch>
<patch id="misc.settings.SettingsPatch">
<string name="revanced_ads_screen_title">Реклами</string>
<string name="revanced_misc_screen_title">Разни</string>
<string name="revanced_twitch_debug_title">Дневник на отстраняването на грешки</string>
<string name="revanced_twitch_debug_summary_on">Дневникът за остраняване на грешки е активиран</string>
<string name="revanced_twitch_debug_summary_off">Дневникът за остраняване на грешки е деактивиран</string>
</patch>
</app>
</resources>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,263 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
</app>
<app id="youtube">
<patch id="misc.settings.SettingsResourcePatch">
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
<patch id="misc.debugging.DebuggingPatch">
</patch>
<patch id="layout.hide.general.HideLayoutComponentsPatch">
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.getpremium.HideGetPremiumPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="interaction.copyvideourl.CopyVideoUrlResourcePatch">
</patch>
<patch id="interaction.dialog.RemoveViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.downloads.DownloadsResourcePatch">
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
</patch>
<patch id="interaction.seekbar.DisablePreciseSeekingGesturePatch">
</patch>
<patch id="interaction.seekbar.EnableSeekbarTappingPatch">
</patch>
<patch id="interaction.swipecontrols.SwipeControlsResourcePatch">
</patch>
<patch id="layout.autocaptions.AutoCaptionsPatch">
</patch>
<patch id="layout.buttons.action.HideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="layout.buttons.autoplay.HideAutoplayButtonPatch">
</patch>
<patch id="layout.buttons.captions.HideCaptionsButtonPatch">
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
</patch>
<patch id="layout.buttons.cast.HideCastButtonPatch">
</patch>
<patch id="layout.buttons.navigation.NavigationButtonsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<!-- The Create button has no display name. Translate normally. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
</patch>
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'More info' should be translated using the same localized wording YouTube displays for the menu item.
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
</patch>
<patch id="layout.buttons.player.hide.HidePlayerButtonsPatch">
</patch>
<patch id="layout.hide.albumcards.AlbumCardsResourcePatch">
</patch>
<patch id="layout.hide.comments.CommentsPatch">
</patch>
<patch id="layout.hide.crowdfundingbox.CrowdfundingBoxResourcePatch">
</patch>
<patch id="layout.hide.endscreencards.HideEndscreenCardsResourcePatch">
</patch>
<patch id="layout.hide.filterbar.HideFilterBarResourcePatch">
</patch>
<patch id="layout.hide.floatingmicrophone.HideFloatingMicrophoneButtonResourcePatch">
</patch>
<patch id="layout.hide.fullscreenambientmode.DisableFullscreenAmbientModePatch">
</patch>
<patch id="layout.hide.infocards.HideInfocardsResourcePatch">
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.HideSeekbarPatch">
</patch>
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.DisableSuggestedVideoEndScreenResourcePatch">
</patch>
<patch id="layout.hide.time.HideTimestampPatch">
</patch>
<patch id="layout.panels.popup.PlayerPopupPanelsPatch">
</patch>
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.WideSearchbarPatch">
</patch>
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<!-- 'RYD' is 'Return YouTube Dislike' -->
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- 'History' should be translated using the same localized wording YouTube displays for the 'history' section in the 'You' tab. -->
</patch>
<patch id="layout.startupshortsreset.DisableResumingShortsOnStartupPatch">
</patch>
<patch id="layout.tablet.EnableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.MiniplayerPatch">
</patch>
<patch id="layout.theme.ThemeBytecodePatch">
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.AnnouncementsPatch">
</patch>
<patch id="misc.autorepeat.AutoRepeatPatch">
</patch>
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
</patch>
<patch id="misc.gms.GmsCoreSupportResourcePatch">
</patch>
<patch id="misc.links.BypassURLRedirectsPatch">
</patch>
<patch id="misc.links.OpenLinksExternallyPatch">
</patch>
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
</patch>
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
</patch>
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
</patch>
<patch id="video.quality.RememberVideoQualityPatch">
</patch>
<patch id="video.speed.button.PlaybackSpeedButtonPatch">
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.RememberPlaybackSpeedPatch">
</patch>
<patch id="video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.EnableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.SpoofClientPatch">
</patch>
<!-- This patch is no longer used, these strings are not in use, and these strings will be deleted in the future. -->
<patch id="misc.fix.playback.SpoofSignaturePatch">
</patch>
<!-- This patch is no longer used and these strings will soon be deleted. -->
<patch id="video.hdrbrightness.HDRBrightnessPatch">
</patch>
</app>
<app id="twitch">
<patch id="ad.audio.AudioAdsPatch">
</patch>
<patch id="ad.embedded.EmbeddedAdsPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="chat.antidelete.ShowDeletedMessagesPatch">
</patch>
<patch id="chat.autoclaim.AutoClaimChannelPointsPatch">
</patch>
<patch id="debug.DebugModePatch">
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
</app>
</resources>

View File

@@ -0,0 +1,272 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
<patch id="misc.settings.BaseSettingsResourcePatch">
<string name="revanced_settings_reset">Restablir</string>
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
</app>
<app id="youtube">
<patch id="misc.settings.SettingsResourcePatch">
</patch>
<patch id="misc.settings.SettingsPatch">
<string name="revanced_settings_screen_00_about_title">Quant a</string>
</patch>
<patch id="misc.debugging.DebuggingPatch">
</patch>
<patch id="layout.hide.general.HideLayoutComponentsPatch">
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<!-- 'Component path builder strings' is the technical name for identifying the Litho UI layout items to hide. This is an advanced feature and most users will never use this. -->
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="ad.getpremium.HideGetPremiumPatch">
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="interaction.copyvideourl.CopyVideoUrlResourcePatch">
</patch>
<patch id="interaction.dialog.RemoveViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.downloads.DownloadsResourcePatch">
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
</patch>
<patch id="interaction.seekbar.DisablePreciseSeekingGesturePatch">
</patch>
<patch id="interaction.seekbar.EnableSeekbarTappingPatch">
</patch>
<patch id="interaction.swipecontrols.SwipeControlsResourcePatch">
</patch>
<patch id="layout.autocaptions.AutoCaptionsPatch">
</patch>
<patch id="layout.buttons.action.HideButtonsPatch">
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="layout.buttons.autoplay.HideAutoplayButtonPatch">
</patch>
<patch id="layout.buttons.captions.HideCaptionsButtonPatch">
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
</patch>
<patch id="layout.buttons.cast.HideCastButtonPatch">
</patch>
<patch id="layout.buttons.navigation.NavigationButtonsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<!-- The Create button has no display name. Translate normally. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
</patch>
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<!-- 'Captions' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Additional settings' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Ambient mode' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Playback speed' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'More info' should be translated using the same localized wording YouTube displays for the menu item.
This menu only appears for some videos. Translate the name normally if the menu cannot be found. -->
<!-- 'Lock screen' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Audio track' should be translated using the same localized wording YouTube displays for the menu item. -->
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
</patch>
<patch id="layout.buttons.player.hide.HidePlayerButtonsPatch">
</patch>
<patch id="layout.hide.albumcards.AlbumCardsResourcePatch">
</patch>
<patch id="layout.hide.comments.CommentsPatch">
</patch>
<patch id="layout.hide.crowdfundingbox.CrowdfundingBoxResourcePatch">
</patch>
<patch id="layout.hide.endscreencards.HideEndscreenCardsResourcePatch">
</patch>
<patch id="layout.hide.filterbar.HideFilterBarResourcePatch">
</patch>
<patch id="layout.hide.floatingmicrophone.HideFloatingMicrophoneButtonResourcePatch">
</patch>
<patch id="layout.hide.fullscreenambientmode.DisableFullscreenAmbientModePatch">
</patch>
<patch id="layout.hide.infocards.HideInfocardsResourcePatch">
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.HideSeekbarPatch">
</patch>
<patch id="layout.hide.shorts.HideShortsComponentsResourcePatch">
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
</patch>
<patch id="layout.hide.suggestedvideoendscreen.DisableSuggestedVideoEndScreenResourcePatch">
</patch>
<patch id="layout.hide.time.HideTimestampPatch">
</patch>
<patch id="layout.panels.popup.PlayerPopupPanelsPatch">
</patch>
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<string name="revanced_ryd_about">Quant a</string>
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these. -->
</patch>
<patch id="layout.searchbar.WideSearchbarPatch">
</patch>
<patch id="layout.seekbar.RestoreOldSeekbarThumbnailsPatch">
</patch>
<patch id="layout.sponsorblock.SponsorBlockResourcePatch">
<string name="revanced_sb_appearance_category">Aparença</string>
<!-- Translations should use language similar to 'revanced_ryd_compact_layout_title' -->
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Do not rearrange the (hour):(minute):second) time format operators here.
YT shows the same seekbar time format for all languages, and this string is confirming the segment time as it appears in the seekbar. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
<string name="revanced_sb_stats_loading">S\'està carregant...</string>
<string name="revanced_sb_reset">Restablir</string>
<string name="revanced_sb_about">Quant a</string>
</patch>
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<!-- 'RYD' is 'Return YouTube Dislike' -->
</patch>
<patch id="layout.startpage.ChangeStartPagePatch">
<string name="revanced_start_page_entry_0">Per defecte</string>
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the subscriptions tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- 'History' should be translated using the same localized wording YouTube displays for the 'history' section in the 'You' tab. -->
</patch>
<patch id="layout.startupshortsreset.DisableResumingShortsOnStartupPatch">
</patch>
<patch id="layout.tablet.EnableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.MiniplayerPatch">
</patch>
<patch id="layout.theme.ThemeBytecodePatch">
</patch>
<patch id="layout.theme.ThemeResourcePatch">
</patch>
<patch id="layout.thumbnails.AlternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
<!-- 'Subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<!-- 'You' should be translated using the same localized wording YouTube displays for the You (library) tab. -->
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.AnnouncementsPatch">
</patch>
<patch id="misc.autorepeat.AutoRepeatPatch">
</patch>
<patch id="misc.dimensions.spoof.SpoofDeviceDimensionsPatch">
</patch>
<patch id="misc.gms.GmsCoreSupportResourcePatch">
</patch>
<patch id="misc.links.BypassURLRedirectsPatch">
</patch>
<patch id="misc.links.OpenLinksExternallyPatch">
</patch>
<patch id="misc.backgroundplayback.BackgroundPlaybackPatch">
</patch>
<patch id="misc.privacy.RemoveTrackingQueryParameterPatch">
</patch>
<patch id="misc.zoomhaptics.ZoomHapticsPatch">
</patch>
<patch id="video.quality.RememberVideoQualityPatch">
</patch>
<patch id="video.speed.button.PlaybackSpeedButtonPatch">
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.RememberPlaybackSpeedPatch">
</patch>
<patch id="video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.EnableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.SpoofClientPatch">
</patch>
<!-- This patch is no longer used, these strings are not in use, and these strings will be deleted in the future. -->
<patch id="misc.fix.playback.SpoofSignaturePatch">
</patch>
<!-- This patch is no longer used and these strings will soon be deleted. -->
<patch id="video.hdrbrightness.HDRBrightnessPatch">
</patch>
</app>
<app id="twitch">
<patch id="ad.audio.AudioAdsPatch">
</patch>
<patch id="ad.embedded.EmbeddedAdsPatch">
<string name="revanced_block_embedded_ads_entry_1">Desactivat</string>
</patch>
<patch id="ad.video.VideoAdsPatch">
</patch>
<patch id="chat.antidelete.ShowDeletedMessagesPatch">
</patch>
<patch id="chat.autoclaim.AutoClaimChannelPointsPatch">
</patch>
<patch id="debug.DebugModePatch">
<!-- Twitch specific internal debug mode, and not the same as 'revanced_debug_title' -->
</patch>
<patch id="misc.settings.SettingsPatch">
</patch>
</app>
</resources>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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