diff --git a/README.ko.md b/README.ko.md new file mode 100644 index 000000000..bb6bd653b --- /dev/null +++ b/README.ko.md @@ -0,0 +1,144 @@ +

+

NewPipe

+

A libre lightweight streaming frontend for Android.

+

+ +

+ + + + + + +

+
+

ScreenshotsDescriptionFeaturesUpdatesContributionDonateLicense

+

WebsiteBlogFAQPress

+
+ +*Read this in other languages: [English](README.md), [한국어](README.ko.md).* + +경고: 이 버전은 베타 버전이므로, 버그가 발생할 수도 있습니다. 만약 버그가 발생하였다면, 우리의 GITHUB 저장소에서 ISSUE를 열람하여 주십시오. + +NEWPIPE 또는 이것의 FORK을 구글 플레이스토어에 올리는 것은 그들의 이용약관을 위반합니다. + +## Screenshots + +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png) +[](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png) +[](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png) + +## Description + +NewPipe는 어떤 구글 프레임워크 라이브러리나, 유튜브 API를 사용하지 않습니다. 웹사이트는 단지 필요한 정보를 가져오기 위해 구문 분석 됩니다. 따라서 이 앱은 구글 서비스의 설치 없이 기기에서 사용될 수 있습니다. 또한, 카피레프트 자유 소프트웨어인 NewPipe를 사용하기 위해 유튜브 계정이 필요하지 않습니다. + +### Features + +* 영상 검색 +* 영상의 일반적인 정보 표시 +* 유튜브 영상 보기 +* 유튜브 영상 듣기 +* 팝업 모드 (floating player) +* 영상 공유 +* 영상 다운로드 +* 음성만 다운로드 +* Kodi에서 영상 열람 +* 다음/관련된 영상 표시 +* 특정 언어로 유튜브 검색 +* 연령 제한 컨텐츠 시청/차단 +* 채널에 대한 일반적인 정보 표시 +* 채널 검색 +* 채널에서 영상 시청 +* Orbot/Tor 지원 (아직 직접적이지 않음) +* 1080p/2K/4K 지원 +* 기록 보기 +* 채널 구독 +* 기록 검색 +* 재생목록 검색/시청 +* 추가된 재생목록 시청 +* 영상 추가 +* 지역 재생목록 +* 자막 +* 실시간 방송 지원 +* 댓글 표시 + +### Supported Services + +NewPipe는 여러가지 서비스를 지원합니다. 우리의 [문서](https://teamnewpipe.github.io/documentation/)는 새로운 서비스가 앱과 추출기에 어떻게 추가될 수 있는지에 대한 더 많은 정보를 제공합니다. 만약 새로운 서비스를 추가하고자 한다면, 우리에게 연락해 주시기 바랍니다. 현재 지원되는 서비스: + +* YouTube +* SoundCloud \[beta\] +* media.ccc.de \[beta\] +* PeerTube instances \[beta\] + +## Updates +NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로 인해), 결국 릴리즈가 발생할 것입니다. 이것들의 형식은 x.xx.x 입니다. +이 새로운 버전을 얻기 위해서, 당신은: + 1. 직접 디버그 APK를 생성할 수 있습니다. 이 방법은 당신의 기기에서 새로운 기능을 얻을 수 있는 가장 빠른 방법이지만, 꽤 많이 복잡합니다. + 따라서 우리는 다른 방법들 중 하나를 사용하는 것을 추천합니다. + 2. 우리의 커스텀 저장소를 F-Droid에 추가하고 우리가 릴리즈를 게시하는 대로 저곳에서 릴리즈를 설치할 수 있습니다. + 이에 대한 설명서는 이곳에서 확인할 수 있습니다: https://newpipe.schabi.org/FAQ/tutorials/install-add-fdroid-repo/ + 3. 우리가 릴리즈를 게시하는 대로 [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases)에서 APK를 다운받고 이것을 설치할 수 있습니다. + 4. F-Droid를 통해 업데이트 할 수 있습니다. F-Droid는 변화를 인식하고, 스스로 APK를 생성하고, 이것에 서명하고, 사용자들에서 업데이트를 전달해야만 하기 때문에, + 이것은 업데이트를 받는 가장 느린 방법입니다. + +우리는 대부분의 사용자에게 2번쨰 방법을 추천합니다. 방법 2 또는 3을 사용하여 설치된 APK는 서로 호환되지만, 방법 4를 사용하여 설치된 것들과는 호환되지 않습니다. 이것은 방법 2 또는 3에서는 같은 (우리의)서명 키가 사용되지만, 방법 4에서는 다른 (F-Droid의)서명 키가 사용되기 때문입니다. 방법 1을 사용하여 디버그 APK를 생성하는 것에서는 키가 완전히 제외됩니다. 서명 키는 사용자가 앱에 악의적인 업데이트를 설치하는 것에 대해 속지 않도록 보장하는 것을 도와줍니다. + +한편, 만약 어떠한 이유(예. NewPipe의 핵심 기능이 손상되었고 F-Droid가 아직 업데이트를 가지지 않는 경우) 때문에 소스를 바꾸길 원한다면, +우리는 다음과 같은 절차를 따르는 것을 권장합니다: +1. 당신의 기록, 구독, 그리고 재생목록을 유지할 수 있도록 Settings > Content > Export Database 를 통해 데이터를 백업하십시오. +2. NewPipe를 삭제하십시오. +3. 새로운 소스에서 APK를 다운로드하고 이것을 설치하십시오. +4. Step 1의 Settings > Content > Export Database 을 통해 데이터를 불러오십시오. + +## Contribution +당신이 아이디어, 번역, 디자인 변경, 코드 정리, 또는 정말 큰 코드 수정에 대한 의견이 있다면, 도움은 항상 환영합니다. +더 많이 수행될수록 더 많이 발전할 수 있습니다! + +만약 참여하고 싶다면, 우리의 [컨트리뷰션 공지](.github/CONTRIBUTING.md)를 참고하십시오. + + +Translation status + + +## Donate +만약 NewPipe가 마음에 들었다면, 우리는 기부에 대해 기꺼이 환영합니다. bitcoin을 보내거나, Bountysource 또는 Liberapay를 통해 기부할 수 있습니다. NewPipe에 기부하는 것에 대한 자세한 정보를 원한다면, 우리의 [웹사이트](https://newpipe.schabi.org/donate)를 방문하여 주십시오. + + + + + + + + + + + + + + + + + +
BitcoinBitcoin QR code16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh
LiberapayVisit NewPipe at liberapay.comDonate via Liberapay
BountysourceVisit NewPipe at bountysource.comCheck out how many bounties you can earn.
+ +## Privacy Policy + +NewPipe 프로젝트는 미디어 웹 서비스를 사용하는 것에 대한 사적의, 익명의 경험을 제공하는 것을 목표로 하고 있습니다. +그러므로, 앱은 당신의 동의 없이 어떤 데이터도 수집하지 않습니다. NewPipe의 개인정보보호정책은 당신이 충돌 리포트를 보내거나, 또는 우리의 블로그에 글을 남길 때 어떤 데이터가 보내지고 저장되는지에 대해 상세히 설명합니다. 이 문서는 [여기](https://newpipe.schabi.org/legal/privacy/)에서 확인할 수 있습니다. + +## License +[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) + +NewPipe는 자유 소프트웨어입니다: 당신의 마음대로 이것을 사용하고, 연구하고, 공유하고, 개선할 수 있습니다. +구체적으로 당신은 자유 소프트웨어 재단에서 발행되는, 버전 3 또는 (당신의 선택에 따라)이후 버전의, +[GNU General Public License](https://www.gnu.org/licenses/gpl.html) 하에서 이것을 재배포 및/또는 수정할 수 있습니다. diff --git a/README.md b/README.md index e2ef680d8..f11262da1 100644 --- a/README.md +++ b/README.md @@ -16,22 +16,24 @@

WebsiteBlogFAQPress


+*Read this in other languages: [English](README.md), [한국어](README.ko.md).* + WARNING: THIS IS A BETA VERSION, THEREFORE YOU MAY ENCOUNTER BUGS. IF YOU DO, OPEN AN ISSUE VIA OUR GITHUB REPOSITORY. PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS. ## Screenshots -[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png) -[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png) -[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png) [](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png) [](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png) @@ -80,17 +82,18 @@ NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/doc ## Updates When a change to the NewPipe code occurs (due to either adding features or bug fixing), eventually a release will occur. These are in the format x.xx.x . In order to get this new version, you can: - * Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods. - * Download the APK from [releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it. - * Update via F-droid. This is the slowest method of getting updates, as F-Droid must recognize changes, build the APK itself, sign it, then push the update to users. + 1. Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods. + 2. Add our custom repo to F-Droid and install it from there as soon as we publish a release. The instructions are here: https://newpipe.schabi.org/FAQ/tutorials/install-add-fdroid-repo/ + 3. Download the APK from [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it as soon as we publish a release. + 4. Update via F-droid. This is the slowest method of getting updates, as F-Droid must recognize changes, build the APK itself, sign it, then push the update to users. -When you install an APK from one of these options, it will be incompatible with an APK from one of the other options. This is due to different signing keys being used. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app, and are independent. F-Droid and GitHub use different signing keys, and building an APK debug excludes a key. The signing key issue is being discussed in issue [#1981](https://github.com/TeamNewPipe/NewPipe/issues/1981), and may be fixed by setting up our own repository on F-Droid. +We recommend method 2 for most users. APKs installed using method 2 or 3 are compatible with each other, but not with those installed using method 4. This is due to the same signing key (ours) being using for 2 and 3, but a different signing key (F-Droid's) being used for 4. Building a debug APK using method 1 excludes a key entirely. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app. In the meanwhile, if you want to switch sources for some reason (e.g. NewPipe's core functionality was broken and F-Droid doesn't have the update yet), we recommend following this procedure: -1. Back up your data via "Settings>Content>Export Database" so you keep your history, subscriptions, and playlists +1. Back up your data via Settings > Content > Export Database so you keep your history, subscriptions, and playlists 2. Uninstall NewPipe 3. Download the APK from the new source and install it -4. Import the data from step 1 via "Settings>Content>Import Database" +4. Import the data from step 1 via Settings > Content > Import Database ## Contribution Whether you have ideas, translations, design changes, code cleaning, or real heavy code changes, help is always welcome. diff --git a/app/build.gradle b/app/build.gradle index f45722de8..2050cd8ef 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { resValue "string", "app_name", "NewPipe" minSdkVersion 19 targetSdkVersion 29 - versionCode 956 - versionName "0.20.2" + versionCode 957 + versionName "0.20.3" multiDexEnabled true @@ -65,6 +65,9 @@ android { } compileOptions { + // Flag to enable support for the new language APIs + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 encoding 'utf-8' @@ -144,6 +147,8 @@ afterEvaluate { } dependencies { + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "frankiesardo:icepick:${icepickVersion}" @@ -171,7 +176,7 @@ dependencies { // NewPipe dependencies // You can use a local version by uncommenting a few lines in settings.gradle - implementation 'com.github.TeamNewPipe:NewPipeExtractor:62912ee8349f5d26617c039f337297628ff52ead' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:6701b0fe718f6bdc385221341fa473e8aaab560e' implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" implementation "org.jsoup:jsoup:1.13.1" diff --git a/app/src/main/assets/gpl_2.html b/app/src/main/assets/gpl_2.html deleted file mode 100644 index 0e1b8827e..000000000 --- a/app/src/main/assets/gpl_2.html +++ /dev/null @@ -1,400 +0,0 @@ - - - - - - GNU General Public License v2.0 - GNU Project - Free Software Foundation (FSF) - - - -

GNU GENERAL PUBLIC LICENSE

-

-Version 2, June 1991 -

- -
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-
-Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -
- -

Preamble

- -

- The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. -

- -

- When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. -

- -

- To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. -

- -

- For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. -

- -

- We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. -

- -

- Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. -

- -

- Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. -

- -

- The precise terms and conditions for copying, distribution and -modification follow. -

- - -

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

- - -

-0. - This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". -

- -

-Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. -

- -

-1. - You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. -

- -

-You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. -

- -

-2. - You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: -

- -
-
-
- a) - You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. -
-
-
- b) - You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. -
-
-
- c) - If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) -
-
- -

-These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. -

- -

-Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. -

- -

-In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. -

- -

-3. - You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: -

- - - - -
-
-
- a) - Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, -
-
-
- b) - Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, -
-
-
- c) - Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) -
-
- -

-The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major softwareComponents (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. -

- -

-If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. -

- -

-4. - You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. -

- -

-5. - You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. -

- -

-6. - Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. -

- -

-7. - If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. -

- -

-If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. -

- -

-It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. -

- -

-This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. -

- -

-8. - If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. -

- -

-9. - The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. -

- -

-Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. -

- -

-10. - If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. -

- -

NO WARRANTY

- -

-11. - BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. -

- -

-12. - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. -

- diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index a94acda8e..5fdc1058a 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -36,6 +36,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import io.reactivex.disposables.Disposable; import io.reactivex.exceptions.CompositeException; import io.reactivex.exceptions.MissingBackpressureException; import io.reactivex.exceptions.OnErrorNotImplementedException; @@ -65,6 +66,9 @@ public class App extends MultiDexApplication { protected static final String TAG = App.class.toString(); private static App app; + private Disposable disposable = null; + + @NonNull public static App getApp() { return app; } @@ -100,7 +104,15 @@ public class App extends MultiDexApplication { configureRxJavaErrorHandler(); // Check for new version - new CheckForNewAppVersionTask().execute(); + disposable = CheckForNewAppVersion.checkNewVersion(this); + } + + @Override + public void onTerminate() { + if (disposable != null) { + disposable.dispose(); + } + super.onTerminate(); } protected Downloader getDownloader() { diff --git a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java similarity index 58% rename from app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java rename to app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java index 10a6a73d7..a193149e2 100644 --- a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java +++ b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java @@ -9,9 +9,9 @@ import android.content.pm.PackageManager; import android.content.pm.Signature; import android.net.ConnectivityManager; import android.net.Uri; -import android.os.AsyncTask; import android.util.Log; +import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; @@ -35,16 +35,18 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -/** - * AsyncTask to check if there is a newer version of the NewPipe github apk available or not. - * If there is a newer version we show a notification, informing the user. On tapping - * the notification, the user will be directed to the download link. - */ -public class CheckForNewAppVersionTask extends AsyncTask { - private static final boolean DEBUG = MainActivity.DEBUG; - private static final String TAG = CheckForNewAppVersionTask.class.getSimpleName(); +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.disposables.Disposables; +import io.reactivex.schedulers.Schedulers; + +public final class CheckForNewAppVersion { + private CheckForNewAppVersion() { } + + private static final boolean DEBUG = MainActivity.DEBUG; + private static final String TAG = CheckForNewAppVersion.class.getSimpleName(); - private static final Application APP = App.getApp(); private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15"; private static final String NEWPIPE_API_URL = "https://newpipe.schabi.org/api/data.json"; @@ -52,18 +54,19 @@ public class CheckForNewAppVersionTask extends AsyncTask { /** * Method to get the apk's SHA1 key. See https://stackoverflow.com/questions/9293019/#22506133. * + * @param application The application * @return String with the apk's SHA1 fingeprint in hexadecimal */ - private static String getCertificateSHA1Fingerprint() { - final PackageManager pm = APP.getPackageManager(); - final String packageName = APP.getPackageName(); + private static String getCertificateSHA1Fingerprint(@NonNull final Application application) { + final PackageManager pm = application.getPackageManager(); + final String packageName = application.getPackageName(); final int flags = PackageManager.GET_SIGNATURES; PackageInfo packageInfo = null; try { packageInfo = pm.getPackageInfo(packageName, flags); } catch (final PackageManager.NameNotFoundException e) { - ErrorActivity.reportError(APP, e, null, null, + ErrorActivity.reportError(application, e, null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Could not find package info", R.string.app_ui_crash)); } @@ -78,7 +81,7 @@ public class CheckForNewAppVersionTask extends AsyncTask { final CertificateFactory cf = CertificateFactory.getInstance("X509"); c = (X509Certificate) cf.generateCertificate(input); } catch (final CertificateException e) { - ErrorActivity.reportError(APP, e, null, null, + ErrorActivity.reportError(application, e, null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Certificate error", R.string.app_ui_crash)); } @@ -90,7 +93,7 @@ public class CheckForNewAppVersionTask extends AsyncTask { final byte[] publicKey = md.digest(c.getEncoded()); hexString = byte2HexFormatted(publicKey); } catch (NoSuchAlgorithmException | CertificateEncodingException e) { - ErrorActivity.reportError(APP, e, null, null, + ErrorActivity.reportError(application, e, null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Could not retrieve SHA1 key", R.string.app_ui_crash)); } @@ -118,104 +121,108 @@ public class CheckForNewAppVersionTask extends AsyncTask { return str.toString(); } - public static boolean isGithubApk() { - return getCertificateSHA1Fingerprint().equals(GITHUB_APK_SHA1); + /** + * Method to compare the current and latest available app version. + * If a newer version is available, we show the update notification. + * + * @param application The application + * @param versionName Name of new version + * @param apkLocationUrl Url with the new apk + * @param versionCode Code of new version + */ + private static void compareAppVersionAndShowNotification(@NonNull final Application application, + final String versionName, + final String apkLocationUrl, + final int versionCode) { + final int notificationId = 2000; + + if (BuildConfig.VERSION_CODE < versionCode) { + // A pending intent to open the apk location url in the browser. + final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl)); + final PendingIntent pendingIntent + = PendingIntent.getActivity(application, 0, intent, 0); + + final String channelId = application + .getString(R.string.app_update_notification_channel_id); + final NotificationCompat.Builder notificationBuilder + = new NotificationCompat.Builder(application, channelId) + .setSmallIcon(R.drawable.ic_newpipe_update) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + .setContentTitle(application + .getString(R.string.app_update_notification_content_title)) + .setContentText(application + .getString(R.string.app_update_notification_content_text) + + " " + versionName); + + final NotificationManagerCompat notificationManager + = NotificationManagerCompat.from(application); + notificationManager.notify(notificationId, notificationBuilder.build()); + } } - @Override - protected void onPreExecute() { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(APP); + private static boolean isConnected(@NonNull final App app) { + final ConnectivityManager cm = ContextCompat.getSystemService(app, + ConnectivityManager.class); + return cm.getActiveNetworkInfo() != null + && cm.getActiveNetworkInfo().isConnected(); + } + + public static boolean isGithubApk(@NonNull final App app) { + return getCertificateSHA1Fingerprint(app).equals(GITHUB_APK_SHA1); + } + + @NonNull + public static Disposable checkNewVersion(@NonNull final App app) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app); // Check if user has enabled/disabled update checking // and if the current apk is a github one or not. - if (!prefs.getBoolean(APP.getString(R.string.update_app_key), true) || !isGithubApk()) { - this.cancel(true); - } - } - - @Override - protected String doInBackground(final Void... voids) { - if (isCancelled() || !isConnected()) { - return null; + if (!prefs.getBoolean(app.getString(R.string.update_app_key), true) + || !isGithubApk(app)) { + return Disposables.empty(); } - // Make a network request to get latest NewPipe data. - try { - return DownloaderImpl.getInstance().get(NEWPIPE_API_URL).responseBody(); - } catch (IOException | ReCaptchaException e) { - // connectivity problems, do not alarm user and fail silently - if (DEBUG) { - Log.w(TAG, Log.getStackTraceString(e)); + return Observable.fromCallable(() -> { + if (!isConnected(app)) { + return null; } - } - - return null; - } - - @Override - protected void onPostExecute(final String response) { - // Parse the json from the response. - if (response != null) { + // Make a network request to get latest NewPipe data. try { - final JsonObject githubStableObject = JsonParser.object().from(response) - .getObject("flavors").getObject("github").getObject("stable"); - - final String versionName = githubStableObject.getString("version"); - final int versionCode = githubStableObject.getInt("version_code"); - final String apkLocationUrl = githubStableObject.getString("apk"); - - compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode); - - } catch (final JsonParserException e) { + return DownloaderImpl.getInstance().get(NEWPIPE_API_URL).responseBody(); + } catch (IOException | ReCaptchaException e) { // connectivity problems, do not alarm user and fail silently if (DEBUG) { Log.w(TAG, Log.getStackTraceString(e)); } } - } - } - /** - * Method to compare the current and latest available app version. - * If a newer version is available, we show the update notification. - * - * @param versionName Name of new version - * @param apkLocationUrl Url with the new apk - * @param versionCode Code of new version - */ - private void compareAppVersionAndShowNotification(final String versionName, - final String apkLocationUrl, - final int versionCode) { - final int notificationId = 2000; + return null; + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(response -> { + // Parse the json from the response. + if (response != null) { + try { + final JsonObject githubStableObject = JsonParser.object().from(response) + .getObject("flavors").getObject("github").getObject("stable"); - if (BuildConfig.VERSION_CODE < versionCode) { + final String versionName = githubStableObject.getString("version"); + final int versionCode = githubStableObject.getInt("version_code"); + final String apkLocationUrl = githubStableObject.getString("apk"); - // A pending intent to open the apk location url in the browser. - final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl)); - final PendingIntent pendingIntent - = PendingIntent.getActivity(APP, 0, intent, 0); - - final NotificationCompat.Builder notificationBuilder = new NotificationCompat - .Builder(APP, APP.getString(R.string.app_update_notification_channel_id)) - .setSmallIcon(R.drawable.ic_newpipe_update) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContentIntent(pendingIntent) - .setAutoCancel(true) - .setContentTitle(APP.getString(R.string.app_update_notification_content_title)) - .setContentText(APP.getString(R.string.app_update_notification_content_text) - + " " + versionName); - - final NotificationManagerCompat notificationManager - = NotificationManagerCompat.from(APP); - notificationManager.notify(notificationId, notificationBuilder.build()); - } - } - - private boolean isConnected() { - final ConnectivityManager cm = ContextCompat.getSystemService(APP, - ConnectivityManager.class); - return cm.getActiveNetworkInfo() != null - && cm.getActiveNetworkInfo().isConnected(); + compareAppVersionAndShowNotification(app, versionName, apkLocationUrl, + versionCode); + } catch (final JsonParserException e) { + // connectivity problems, do not alarm user and fail silently + if (DEBUG) { + Log.w(TAG, Log.getStackTraceString(e)); + } + } + } + }); } } diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index e72d4609e..9bcbe4ff1 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -30,9 +30,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import androidx.preference.PreferenceManager; import android.util.Log; - import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; @@ -46,6 +44,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; @@ -55,6 +54,7 @@ import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import androidx.preference.PreferenceManager; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.navigation.NavigationView; @@ -69,10 +69,11 @@ import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.player.VideoPlayer; import org.schabi.newpipe.player.event.OnKeyDownListener; +import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.report.ErrorActivity; -import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; @@ -87,6 +88,7 @@ import org.schabi.newpipe.views.FocusOverlayView; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; @@ -152,7 +154,7 @@ public class MainActivity extends AppCompatActivity { if (DeviceUtils.isTv(this)) { FocusOverlayView.setupFocusObserver(this); } - setupBroadcastReceiver(); + openMiniPlayerUponPlayerStarted(); } private void setupDrawer() throws Exception { @@ -758,32 +760,36 @@ public class MainActivity extends AppCompatActivity { if (intent.hasExtra(Constants.KEY_LINK_TYPE)) { final String url = intent.getStringExtra(Constants.KEY_URL); final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); - final String title = intent.getStringExtra(Constants.KEY_TITLE); - switch (((StreamingService.LinkType) intent - .getSerializableExtra(Constants.KEY_LINK_TYPE))) { + String title = intent.getStringExtra(Constants.KEY_TITLE); + if (title == null) { + title = ""; + } + + final StreamingService.LinkType linkType = ((StreamingService.LinkType) intent + .getSerializableExtra(Constants.KEY_LINK_TYPE)); + assert linkType != null; + switch (linkType) { case STREAM: - final boolean autoPlay = intent - .getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false); - final String intentCacheKey = intent - .getStringExtra(VideoPlayer.PLAY_QUEUE_KEY); + final String intentCacheKey = intent.getStringExtra( + VideoPlayer.PLAY_QUEUE_KEY); final PlayQueue playQueue = intentCacheKey != null ? SerializedCache.getInstance() .take(intentCacheKey, PlayQueue.class) : null; - NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), - serviceId, url, title, autoPlay, playQueue); + + final boolean switchingPlayers = intent.getBooleanExtra( + VideoDetailFragment.KEY_SWITCHING_PLAYERS, false); + NavigationHelper.openVideoDetailFragment( + getApplicationContext(), getSupportFragmentManager(), + serviceId, url, title, playQueue, switchingPlayers); break; case CHANNEL: NavigationHelper.openChannelFragment(getSupportFragmentManager(), - serviceId, - url, - title); + serviceId, url, title); break; case PLAYLIST: NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), - serviceId, - url, - title); + serviceId, url, title); break; } } else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) { @@ -805,34 +811,47 @@ public class MainActivity extends AppCompatActivity { } } - private void setupBroadcastReceiver() { - broadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(final Context context, final Intent intent) { - if (intent.getAction().equals(VideoDetailFragment.ACTION_PLAYER_STARTED)) { - final Fragment fragmentPlayer = getSupportFragmentManager() - .findFragmentById(R.id.fragment_player_holder); - if (fragmentPlayer == null) { - /* - * We still don't have a fragment attached to the activity. - * It can happen when a user started popup or background players - * without opening a stream inside the fragment. - * Adding it in a collapsed state (only mini player will be visible) - * */ - NavigationHelper.showMiniPlayer(getSupportFragmentManager()); + private void openMiniPlayerIfMissing() { + final Fragment fragmentPlayer = getSupportFragmentManager() + .findFragmentById(R.id.fragment_player_holder); + if (fragmentPlayer == null) { + // We still don't have a fragment attached to the activity. It can happen when a user + // started popup or background players without opening a stream inside the fragment. + // Adding it in a collapsed state (only mini player will be visible). + NavigationHelper.showMiniPlayer(getSupportFragmentManager()); + } + } + + private void openMiniPlayerUponPlayerStarted() { + if (getIntent().getSerializableExtra(Constants.KEY_LINK_TYPE) + == StreamingService.LinkType.STREAM) { + // handleIntent() already takes care of opening video detail fragment + // due to an intent containing a STREAM link + return; + } + + if (PlayerHolder.isPlayerOpen()) { + // if the player is already open, no need for a broadcast receiver + openMiniPlayerIfMissing(); + } else { + // listen for player start intent being sent around + broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + if (Objects.equals(intent.getAction(), + VideoDetailFragment.ACTION_PLAYER_STARTED)) { + openMiniPlayerIfMissing(); + // At this point the player is added 100%, we can unregister. Other actions + // are useless since the fragment will not be removed after that. + unregisterReceiver(broadcastReceiver); + broadcastReceiver = null; } - /* - * At this point the player is added 100%, we can unregister. - * Other actions are useless since the fragment will not be removed after that - * */ - unregisterReceiver(broadcastReceiver); - broadcastReceiver = null; } - } - }; - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED); - registerReceiver(broadcastReceiver, intentFilter); + }; + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED); + registerReceiver(broadcastReceiver, intentFilter); + } } private boolean bottomSheetHiddenOrCollapsed() { diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 388d7683a..537d71901 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -40,7 +40,9 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.helper.PlayerHelper; +import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; @@ -60,8 +62,6 @@ import org.schabi.newpipe.views.FocusOverlayView; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.List; import icepick.Icepick; @@ -116,8 +116,6 @@ public class RouterActivity extends AppCompatActivity { } } - internalRoute = getIntent().getBooleanExtra(INTERNAL_ROUTE_KEY, false); - setTheme(ThemeHelper.isLightThemeSelected(this) ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark); } @@ -398,14 +396,22 @@ public class RouterActivity extends AppCompatActivity { // show both "show info" and "video player", they are two different activities returnList.add(showInfo); returnList.add(videoPlayer); - } else if (capabilities.contains(VIDEO) - && PlayerHelper.isAutoplayAllowedByUser(context)) { - // show only "video player" since the details activity will be opened and the video - // will be autoplayed there and "show info" would do the exact same thing - returnList.add(videoPlayer); } else { - // show only "show info" if video player is not applicable or autoplay is disabled - returnList.add(showInfo); + final MainPlayer.PlayerType playerType = PlayerHolder.getType(); + if (capabilities.contains(VIDEO) + && PlayerHelper.isAutoplayAllowedByUser(context) + && playerType == null || playerType == MainPlayer.PlayerType.VIDEO) { + // show only "video player" since the details activity will be opened and the + // video will be auto played there. Since "show info" would do the exact same + // thing, use that as a key to let VideoDetailFragment load the stream instead + // of using FetcherService (see comment in handleChoice()) + returnList.add(new AdapterChoiceItem( + showInfo.key, videoPlayer.description, videoPlayer.icon)); + } else { + // show only "show info" if video player is not applicable, auto play is + // disabled or a video is playing in a player different than the main one + returnList.add(showInfo); + } } if (capabilities.contains(VIDEO)) { @@ -492,12 +498,7 @@ public class RouterActivity extends AppCompatActivity { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(intent -> { - if (!internalRoute) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - } startActivity(intent); - finish(); }, throwable -> handleError(throwable, currentUrl)) ); @@ -515,7 +516,7 @@ public class RouterActivity extends AppCompatActivity { @SuppressLint("CheckResult") private void openDownloadDialog() { - ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) + disposables.add(ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((@NonNull StreamInfo result) -> { @@ -532,10 +533,10 @@ public class RouterActivity extends AppCompatActivity { downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex); downloadDialog.show(fm, "downloadDialog"); fm.executePendingTransactions(); - downloadDialog.getDialog().setOnDismissListener(dialog -> finish()); + downloadDialog.requireDialog().setOnDismissListener(dialog -> finish()); }, (@NonNull Throwable throwable) -> { showUnsupportedUrlDialog(currentUrl); - }); + })); } @Override @@ -553,66 +554,6 @@ public class RouterActivity extends AppCompatActivity { } } - /*////////////////////////////////////////////////////////////////////////// - // Service Fetcher - //////////////////////////////////////////////////////////////////////////*/ - - private String removeHeadingGibberish(final String input) { - int start = 0; - for (int i = input.indexOf("://") - 1; i >= 0; i--) { - if (!input.substring(i, i + 1).matches("\\p{L}")) { - start = i + 1; - break; - } - } - return input.substring(start); - } - - /*////////////////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////////////////*/ - - private String trim(final String input) { - if (input == null || input.length() < 1) { - return input; - } else { - String output = input; - while (output.length() > 0 && output.substring(0, 1).matches(REGEX_REMOVE_FROM_URL)) { - output = output.substring(1); - } - while (output.length() > 0 - && output.substring(output.length() - 1).matches(REGEX_REMOVE_FROM_URL)) { - output = output.substring(0, output.length() - 1); - } - return output; - } - } - - /** - * Retrieves all Strings which look remotely like URLs from a text. - * Used if NewPipe was called through share menu. - * - * @param sharedText text to scan for URLs. - * @return potential URLs - */ - protected String[] getUris(final String sharedText) { - final Collection result = new HashSet<>(); - if (sharedText != null) { - final String[] array = sharedText.split("\\p{Space}"); - for (String s : array) { - s = trim(s); - if (s.length() != 0) { - if (s.matches(".+://.+")) { - result.add(removeHeadingGibberish(s)); - } else if (s.matches(".+\\..+")) { - result.add("http://" + s); - } - } - } - } - return result.toArray(new String[0]); - } - private static class AdapterChoiceItem { final String description; final String key; @@ -725,50 +666,34 @@ public class RouterActivity extends AppCompatActivity { final boolean isExtAudioEnabled = preferences.getBoolean( getString(R.string.use_external_audio_player_key), false); - PlayQueue playQueue; - final String playerChoice = choice.playerChoice; - + final PlayQueue playQueue; if (info instanceof StreamInfo) { - if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) { + if (choice.playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) { NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info); - - } else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) { + return; + } else if (choice.playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) { NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info); - - } else { - playQueue = new SinglePlayQueue((StreamInfo) info); - - if (playerChoice.equals(videoPlayerKey)) { - openMainPlayer(playQueue, choice); - } else if (playerChoice.equals(backgroundPlayerKey)) { - NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true); - } else if (playerChoice.equals(popupPlayerKey)) { - NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true); - } + return; } + playQueue = new SinglePlayQueue((StreamInfo) info); + } else if (info instanceof ChannelInfo) { + playQueue = new ChannelPlayQueue((ChannelInfo) info); + } else if (info instanceof PlaylistInfo) { + playQueue = new PlaylistPlayQueue((PlaylistInfo) info); + } else { + return; } - if (info instanceof ChannelInfo || info instanceof PlaylistInfo) { - playQueue = info instanceof ChannelInfo - ? new ChannelPlayQueue((ChannelInfo) info) - : new PlaylistPlayQueue((PlaylistInfo) info); - - if (playerChoice.equals(videoPlayerKey)) { - openMainPlayer(playQueue, choice); - } else if (playerChoice.equals(backgroundPlayerKey)) { - NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); - } else if (playerChoice.equals(popupPlayerKey)) { - NavigationHelper.playOnPopupPlayer(this, playQueue, true); - } + if (choice.playerChoice.equals(videoPlayerKey)) { + NavigationHelper.playOnMainPlayer(this, playQueue, false); + } else if (choice.playerChoice.equals(backgroundPlayerKey)) { + NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); + } else if (choice.playerChoice.equals(popupPlayerKey)) { + NavigationHelper.playOnPopupPlayer(this, playQueue, true); } }; } - private void openMainPlayer(final PlayQueue playQueue, final Choice choice) { - NavigationHelper.playOnMainPlayer(this, playQueue, choice.linkType, - choice.url, "", true, true); - } - @Override public void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java index c24636cf0..a15bb9e41 100644 --- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java +++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java @@ -12,8 +12,7 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.lifecycle.Lifecycle; +import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; @@ -34,7 +33,7 @@ public class AboutActivity extends AppCompatActivity { */ private static final SoftwareComponent[] SOFTWARE_COMPONENTS = new SoftwareComponent[]{ new SoftwareComponent("Giga Get", "2014 - 2015", "Peter Cai", - "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2), + "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL3), new SoftwareComponent("NewPipe Extractor", "2017 - 2020", "Christian Schabesberger", "https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3), new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley", @@ -95,8 +94,7 @@ public class AboutActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); // Create the adapter that will return a fragment for each of the three // primary sections of the activity. - mSectionsPagerAdapter = - new SectionsPagerAdapter(getSupportFragmentManager(), getLifecycle()); + mSectionsPagerAdapter = new SectionsPagerAdapter(this); // Set up the ViewPager with the sections adapter. mViewPager = findViewById(R.id.container); @@ -179,8 +177,8 @@ public class AboutActivity extends AppCompatActivity { * one of the sections/tabs/pages. */ public static class SectionsPagerAdapter extends FragmentStateAdapter { - public SectionsPagerAdapter(final FragmentManager fm, final Lifecycle lifecycle) { - super(fm, lifecycle); + public SectionsPagerAdapter(final FragmentActivity fa) { + super(fa); } @NonNull diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java index e869dbb14..bac789dbd 100644 --- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java @@ -1,6 +1,5 @@ package org.schabi.newpipe.about; -import android.app.Activity; import android.os.Bundle; import android.view.ContextMenu; import android.view.LayoutInflater; @@ -19,16 +18,21 @@ import org.schabi.newpipe.util.ShareUtils; import java.io.Serializable; import java.util.Arrays; +import java.util.Comparator; + +import io.reactivex.disposables.CompositeDisposable; /** * Fragment containing the software licenses. */ public class LicenseFragment extends Fragment { private static final String ARG_COMPONENTS = "components"; + private static final String LICENSE_KEY = "ACTIVE_LICENSE"; + private SoftwareComponent[] softwareComponents; private SoftwareComponent componentForContextMenu; private License activeLicense; - private static final String LICENSE_KEY = "ACTIVE_LICENSE"; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); public static LicenseFragment newInstance(final SoftwareComponent[] softwareComponents) { if (softwareComponents == null) { @@ -41,16 +45,6 @@ public class LicenseFragment extends Fragment { return fragment; } - /** - * Shows a popup containing the license. - * - * @param context the context to use - * @param license the license to show - */ - private static void showLicense(final Activity context, final License license) { - new LicenseFragmentHelper(context).execute(license); - } - @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -64,7 +58,13 @@ public class LicenseFragment extends Fragment { } } // Sort components by name - Arrays.sort(softwareComponents, (o1, o2) -> o1.getName().compareTo(o2.getName())); + Arrays.sort(softwareComponents, Comparator.comparing(SoftwareComponent::getName)); + } + + @Override + public void onDestroy() { + compositeDisposable.dispose(); + super.onDestroy(); } @Nullable @@ -76,8 +76,9 @@ public class LicenseFragment extends Fragment { final View licenseLink = rootView.findViewById(R.id.app_read_license); licenseLink.setOnClickListener(v -> { - activeLicense = StandardLicenses.GPL3; - showLicense(getActivity(), StandardLicenses.GPL3); + activeLicense = StandardLicenses.GPL3; + compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), + StandardLicenses.GPL3)); }); for (final SoftwareComponent component : softwareComponents) { @@ -94,13 +95,15 @@ public class LicenseFragment extends Fragment { componentView.setTag(component); componentView.setOnClickListener(v -> { activeLicense = component.getLicense(); - showLicense(getActivity(), component.getLicense()); + compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), + component.getLicense())); }); softwareComponentsView.addView(componentView); registerForContextMenu(componentView); } if (activeLicense != null) { - showLicense(getActivity(), activeLicense); + compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), + activeLicense)); } return rootView; } @@ -128,7 +131,8 @@ public class LicenseFragment extends Fragment { ShareUtils.openUrlInBrowser(getActivity(), component.getLink()); return true; case R.id.action_show_license: - showLicense(getActivity(), component.getLicense()); + compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), + component.getLicense())); } return false; } diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java index 01a01bc88..8a2ab6fa9 100644 --- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java @@ -1,8 +1,6 @@ package org.schabi.newpipe.about; -import android.app.Activity; import android.content.Context; -import android.os.AsyncTask; import android.util.Base64; import android.webkit.WebView; @@ -16,18 +14,18 @@ import org.schabi.newpipe.util.ThemeHelper; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.disposables.Disposables; +import io.reactivex.schedulers.Schedulers; + import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; -public class LicenseFragmentHelper extends AsyncTask { - private final WeakReference weakReference; - private License license; - - public LicenseFragmentHelper(@Nullable final Activity activity) { - weakReference = new WeakReference<>(activity); - } +public final class LicenseFragmentHelper { + private LicenseFragmentHelper() { } /** * @param context the context to use @@ -62,7 +60,7 @@ public class LicenseFragmentHelper extends AsyncTask { * @param context * @return String which is a CSS stylesheet according to the context's theme */ - private static String getLicenseStylesheet(final Context context) { + private static String getLicenseStylesheet(@NonNull final Context context) { final boolean isLightTheme = ThemeHelper.isLightThemeSelected(context); return "body{padding:12px 15px;margin:0;" + "background:#" + getHexRGBColor(context, isLightTheme @@ -84,45 +82,31 @@ public class LicenseFragmentHelper extends AsyncTask { * @param color the color number from R.color * @return a six characters long String with hexadecimal RGB values */ - private static String getHexRGBColor(final Context context, final int color) { + private static String getHexRGBColor(@NonNull final Context context, final int color) { return context.getResources().getString(color).substring(3); } - @Nullable - private Activity getActivity() { - final Activity activity = weakReference.get(); - - if (activity != null && activity.isFinishing()) { - return null; - } else { - return activity; - } - } - - @Override - protected Integer doInBackground(final Object... objects) { - license = (License) objects[0]; - return 1; - } - - @Override - protected void onPostExecute(final Integer result) { - final Activity activity = getActivity(); - if (activity == null) { - return; + static Disposable showLicense(@Nullable final Context context, @NonNull final License license) { + if (context == null) { + return Disposables.empty(); } - final String webViewData = Base64.encodeToString(getFormattedLicense(activity, license) - .getBytes(StandardCharsets.UTF_8), Base64.NO_PADDING); - final WebView webView = new WebView(activity); - webView.loadData(webViewData, "text/html; charset=UTF-8", "base64"); + return Observable.fromCallable(() -> getFormattedLicense(context, license)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(formattedLicense -> { + final String webViewData = Base64.encodeToString(formattedLicense + .getBytes(StandardCharsets.UTF_8), Base64.NO_PADDING); + final WebView webView = new WebView(context); + webView.loadData(webViewData, "text/html; charset=UTF-8", "base64"); - final AlertDialog.Builder alert = new AlertDialog.Builder(activity); - alert.setTitle(license.getName()); - alert.setView(webView); - assureCorrectAppLanguage(activity); - alert.setNegativeButton(activity.getString(R.string.finish), - (dialog, which) -> dialog.dismiss()); - alert.show(); + final AlertDialog.Builder alert = new AlertDialog.Builder(context); + alert.setTitle(license.getName()); + alert.setView(webView); + assureCorrectAppLanguage(context); + alert.setNegativeButton(context.getString(R.string.finish), + (dialog, which) -> dialog.dismiss()); + alert.show(); + }); } } diff --git a/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.java b/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.java index 75a7a8613..50ee5ebc3 100644 --- a/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.java +++ b/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.java @@ -4,8 +4,6 @@ package org.schabi.newpipe.about; * Class containing information about standard software licenses. */ public final class StandardLicenses { - public static final License GPL2 - = new License("GNU General Public License, Version 2.0", "GPLv2", "gpl_2.html"); public static final License GPL3 = new License("GNU General Public License, Version 3.0", "GPLv3", "gpl_3.html"); public static final License APACHE2 diff --git a/app/src/main/java/org/schabi/newpipe/database/Converters.java b/app/src/main/java/org/schabi/newpipe/database/Converters.java index ca2d8d875..c46b5f427 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Converters.java +++ b/app/src/main/java/org/schabi/newpipe/database/Converters.java @@ -5,31 +5,35 @@ import androidx.room.TypeConverter; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.local.subscription.FeedGroupIcon; -import java.util.Date; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; public final class Converters { private Converters() { } /** - * Convert a long value to a date. + * Convert a long value to a {@link OffsetDateTime}. * * @param value the long value - * @return the date + * @return the {@code OffsetDateTime} */ @TypeConverter - public static Date fromTimestamp(final Long value) { - return value == null ? null : new Date(value); + public static OffsetDateTime offsetDateTimeFromTimestamp(final Long value) { + return value == null ? null : OffsetDateTime.ofInstant(Instant.ofEpochMilli(value), + ZoneOffset.UTC); } /** - * Convert a date to a long value. + * Convert a {@link OffsetDateTime} to a long value. * - * @param date the date + * @param offsetDateTime the {@code OffsetDateTime} * @return the long value */ @TypeConverter - public static Long dateToTimestamp(final Date date) { - return date == null ? null : date.getTime(); + public static Long offsetDateTimeToTimestamp(final OffsetDateTime offsetDateTime) { + return offsetDateTime == null ? null : offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC) + .toInstant().toEpochMilli(); } @TypeConverter diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt index 74f5b369e..d8b4f72cc 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt @@ -7,7 +7,7 @@ import androidx.room.Query import androidx.room.Transaction import androidx.room.Update import io.reactivex.Flowable -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.feed.model.FeedEntity import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity import org.schabi.newpipe.database.stream.model.StreamEntity @@ -58,10 +58,10 @@ abstract class FeedDAO { INNER JOIN feed f ON s.uid = f.stream_id - WHERE s.upload_date < :date + WHERE s.upload_date < :offsetDateTime ) """) - abstract fun unlinkStreamsOlderThan(date: Date) + abstract fun unlinkStreamsOlderThan(offsetDateTime: OffsetDateTime) @Query(""" DELETE FROM feed @@ -106,10 +106,10 @@ abstract class FeedDAO { INNER JOIN feed_group_subscription_join fgs ON fgs.subscription_id = lu.subscription_id AND fgs.group_id = :groupId """) - abstract fun oldestSubscriptionUpdate(groupId: Long): Flowable> + abstract fun oldestSubscriptionUpdate(groupId: Long): Flowable> @Query("SELECT MIN(last_updated) FROM feed_last_updated") - abstract fun oldestSubscriptionUpdateFromAll(): Flowable> + abstract fun oldestSubscriptionUpdateFromAll(): Flowable> @Query("SELECT COUNT(*) FROM feed_last_updated WHERE last_updated IS NULL") abstract fun notLoadedCount(): Flowable @@ -135,7 +135,7 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold """) - abstract fun getAllOutdated(outdatedThreshold: Date): Flowable> + abstract fun getAllOutdated(outdatedThreshold: OffsetDateTime): Flowable> @Query(""" SELECT s.* FROM subscriptions s @@ -148,5 +148,5 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold """) - abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: Date): Flowable> + abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: OffsetDateTime): Flowable> } diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt b/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt index 78b2550a5..069d1138f 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt @@ -4,7 +4,7 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity.Companion.FEED_LAST_UPDATED_TABLE import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity.Companion.SUBSCRIPTION_ID import org.schabi.newpipe.database.subscription.SubscriptionEntity @@ -25,9 +25,8 @@ data class FeedLastUpdatedEntity( var subscriptionId: Long, @ColumnInfo(name = LAST_UPDATED) - var lastUpdated: Date? = null + var lastUpdated: OffsetDateTime? = null ) { - companion object { const val FEED_LAST_UPDATED_TABLE = "feed_last_updated" diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.java b/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.java index 752835182..fd4588700 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.java @@ -6,7 +6,7 @@ import androidx.room.Ignore; import androidx.room.Index; import androidx.room.PrimaryKey; -import java.util.Date; +import java.time.OffsetDateTime; import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SEARCH; @@ -24,7 +24,7 @@ public class SearchHistoryEntry { private long id; @ColumnInfo(name = CREATION_DATE) - private Date creationDate; + private OffsetDateTime creationDate; @ColumnInfo(name = SERVICE_ID) private int serviceId; @@ -32,7 +32,8 @@ public class SearchHistoryEntry { @ColumnInfo(name = SEARCH) private String search; - public SearchHistoryEntry(final Date creationDate, final int serviceId, final String search) { + public SearchHistoryEntry(final OffsetDateTime creationDate, final int serviceId, + final String search) { this.serviceId = serviceId; this.creationDate = creationDate; this.search = search; @@ -46,11 +47,11 @@ public class SearchHistoryEntry { this.id = id; } - public Date getCreationDate() { + public OffsetDateTime getCreationDate() { return creationDate; } - public void setCreationDate(final Date creationDate) { + public void setCreationDate(final OffsetDateTime creationDate) { this.creationDate = creationDate; } diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java index bf1f7a9dd..ad1941adb 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java @@ -9,7 +9,7 @@ import androidx.room.Index; import org.schabi.newpipe.database.stream.model.StreamEntity; -import java.util.Date; +import java.time.OffsetDateTime; import static androidx.room.ForeignKey.CASCADE; import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID; @@ -37,12 +37,12 @@ public class StreamHistoryEntity { @NonNull @ColumnInfo(name = STREAM_ACCESS_DATE) - private Date accessDate; + private OffsetDateTime accessDate; @ColumnInfo(name = STREAM_REPEAT_COUNT) private long repeatCount; - public StreamHistoryEntity(final long streamUid, @NonNull final Date accessDate, + public StreamHistoryEntity(final long streamUid, @NonNull final OffsetDateTime accessDate, final long repeatCount) { this.streamUid = streamUid; this.accessDate = accessDate; @@ -50,7 +50,7 @@ public class StreamHistoryEntity { } @Ignore - public StreamHistoryEntity(final long streamUid, @NonNull final Date accessDate) { + public StreamHistoryEntity(final long streamUid, @NonNull final OffsetDateTime accessDate) { this(streamUid, accessDate, 1); } @@ -62,11 +62,12 @@ public class StreamHistoryEntity { this.streamUid = streamUid; } - public Date getAccessDate() { + @NonNull + public OffsetDateTime getAccessDate() { return accessDate; } - public void setAccessDate(@NonNull final Date accessDate) { + public void setAccessDate(@NonNull final OffsetDateTime accessDate) { this.accessDate = accessDate; } diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt index c653e6c6f..b928b00bf 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt @@ -2,7 +2,7 @@ package org.schabi.newpipe.database.history.model import androidx.room.ColumnInfo import androidx.room.Embedded -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.stream.model.StreamEntity data class StreamHistoryEntry( @@ -13,7 +13,7 @@ data class StreamHistoryEntry( val streamId: Long, @ColumnInfo(name = StreamHistoryEntity.STREAM_ACCESS_DATE) - val accessDate: Date, + val accessDate: OffsetDateTime, @ColumnInfo(name = StreamHistoryEntity.STREAM_REPEAT_COUNT) val repeatCount: Long @@ -25,6 +25,6 @@ data class StreamHistoryEntry( fun hasEqualValues(other: StreamHistoryEntry): Boolean { return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId && - accessDate.compareTo(other.accessDate) == 0 + accessDate.isEqual(other.accessDate) } } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java index 3ce95631c..43dbd89ea 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java @@ -5,6 +5,7 @@ import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; public interface PlaylistLocalItem extends LocalItem { @@ -18,15 +19,8 @@ public interface PlaylistLocalItem extends LocalItem { items.addAll(localPlaylists); items.addAll(remotePlaylists); - Collections.sort(items, (left, right) -> { - final String on1 = left.getOrderingName(); - final String on2 = right.getOrderingName(); - if (on1 == null) { - return on2 == null ? 0 : 1; - } else { - return on2 == null ? -1 : on1.compareToIgnoreCase(on2); - } - }); + Collections.sort(items, Comparator.comparing(PlaylistLocalItem::getOrderingName, + Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))); return items; } diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt b/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt index dde1f0392..1e4c672ab 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt @@ -2,26 +2,25 @@ package org.schabi.newpipe.database.stream import androidx.room.ColumnInfo import androidx.room.Embedded -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.LocalItem import org.schabi.newpipe.database.history.model.StreamHistoryEntity import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.extractor.stream.StreamInfoItem class StreamStatisticsEntry( - @Embedded + @Embedded val streamEntity: StreamEntity, - @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) + @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) val streamId: Long, - @ColumnInfo(name = STREAM_LATEST_DATE) - val latestAccessDate: Date, + @ColumnInfo(name = STREAM_LATEST_DATE) + val latestAccessDate: OffsetDateTime, - @ColumnInfo(name = STREAM_WATCH_COUNT) + @ColumnInfo(name = STREAM_WATCH_COUNT) val watchCount: Long ) : LocalItem { - fun toStreamInfoItem(): StreamInfoItem { val item = StreamInfoItem(streamEntity.serviceId, streamEntity.url, streamEntity.title, streamEntity.streamType) item.duration = streamEntity.duration diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt index 921c08b46..89757c17d 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt @@ -7,7 +7,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import io.reactivex.Flowable -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.BasicDAO import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_ID @@ -129,7 +129,7 @@ abstract class StreamDAO : BasicDAO { var textualUploadDate: String? = null, @ColumnInfo(name = StreamEntity.STREAM_UPLOAD_DATE) - var uploadDate: Date? = null, + var uploadDate: OffsetDateTime? = null, @ColumnInfo(name = StreamEntity.STREAM_IS_UPLOAD_DATE_APPROXIMATION) var isUploadDateApproximation: Boolean? = null, diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt index d13f5cc2d..defcb7acf 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt @@ -6,8 +6,7 @@ import androidx.room.Ignore import androidx.room.Index import androidx.room.PrimaryKey import java.io.Serializable -import java.util.Calendar -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_SERVICE_ID import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_TABLE import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_URL @@ -55,18 +54,17 @@ data class StreamEntity( var textualUploadDate: String? = null, @ColumnInfo(name = STREAM_UPLOAD_DATE) - var uploadDate: Date? = null, + var uploadDate: OffsetDateTime? = null, @ColumnInfo(name = STREAM_IS_UPLOAD_DATE_APPROXIMATION) var isUploadDateApproximation: Boolean? = null ) : Serializable { - @Ignore constructor(item: StreamInfoItem) : this( serviceId = item.serviceId, url = item.url, title = item.name, streamType = item.streamType, duration = item.duration, uploader = item.uploaderName, thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount, - textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.date()?.time, + textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(), isUploadDateApproximation = item.uploadDate?.isApproximation ) @@ -75,7 +73,7 @@ data class StreamEntity( serviceId = info.serviceId, url = info.url, title = info.name, streamType = info.streamType, duration = info.duration, uploader = info.uploaderName, thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount, - textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.date()?.time, + textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(), isUploadDateApproximation = info.uploadDate?.isApproximation ) @@ -95,8 +93,7 @@ data class StreamEntity( if (viewCount != null) item.viewCount = viewCount as Long item.textualUploadDate = textualUploadDate item.uploadDate = uploadDate?.let { - DateWrapper(Calendar.getInstance().apply { time = it }, isUploadDateApproximation - ?: false) + DateWrapper(it, isUploadDateApproximation ?: false) } return item diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index 17d079d50..93398d990 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -646,7 +646,7 @@ public class DownloadDialog extends DialogFragment mainStorage = mainStorageVideo; // subtitle & video files go together format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat(); mime = format.mimeType; - filename += format == MediaFormat.TTML ? MediaFormat.SRT.suffix : format.suffix; + filename += (format == MediaFormat.TTML ? MediaFormat.SRT : format).suffix; break; default: throw new RuntimeException("No stream selected"); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index e71e7f19c..67bc51c58 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -110,6 +110,7 @@ import org.schabi.newpipe.views.LargeTextMovementMethod; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import icepick.State; @@ -128,7 +129,7 @@ import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfi import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; import static org.schabi.newpipe.util.AnimationUtils.animateView; -public class VideoDetailFragment +public final class VideoDetailFragment extends BaseStateFragment implements BackPressable, SharedPreferences.OnSharedPreferenceChangeListener, @@ -136,7 +137,7 @@ public class VideoDetailFragment View.OnLongClickListener, PlayerServiceExtendedEventListener, OnKeyDownListener { - public static final String AUTO_PLAY = "auto_play"; + public static final String KEY_SWITCHING_PLAYERS = "switching_players"; private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1; private static final int COMMENTS_UPDATE_FLAG = 0x2; @@ -167,19 +168,23 @@ public class VideoDetailFragment @State protected int serviceId = Constants.NO_SERVICE_ID; @State - protected String name; + @NonNull + protected String title = ""; @State - protected String url; - protected static PlayQueue playQueue; + @Nullable + protected String url = null; + @Nullable + protected PlayQueue playQueue = null; @State int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED; @State protected boolean autoPlayEnabled = true; - private static StreamInfo currentInfo; + @Nullable + private StreamInfo currentInfo = null; private Disposable currentWorker; @NonNull - private CompositeDisposable disposables = new CompositeDisposable(); + private final CompositeDisposable disposables = new CompositeDisposable(); @Nullable private Disposable positionSubscriber = null; @@ -284,6 +289,7 @@ public class VideoDetailFragment || (currentInfo != null && isAutoplayEnabled() && player.getParentActivity() == null)) { + autoPlayEnabled = true; // forcefully start playing openVideoPlayer(); } } @@ -298,8 +304,10 @@ public class VideoDetailFragment /*////////////////////////////////////////////////////////////////////////*/ - public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl, - final String name, final PlayQueue queue) { + public static VideoDetailFragment getInstance(final int serviceId, + @Nullable final String videoUrl, + @NonNull final String name, + @Nullable final PlayQueue queue) { final VideoDetailFragment instance = new VideoDetailFragment(); instance.setInitialData(serviceId, videoUrl, name, queue); return instance; @@ -444,8 +452,8 @@ public class VideoDetailFragment switch (requestCode) { case ReCaptchaActivity.RECAPTCHA_REQUEST: if (resultCode == Activity.RESULT_OK) { - NavigationHelper - .openVideoDetailFragment(getFM(), serviceId, url, name); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + serviceId, url, title, null, false); } else { Log.e(TAG, "ReCaptcha failed"); } @@ -514,6 +522,7 @@ public class VideoDetailFragment } break; case R.id.detail_thumbnail_root_layout: + autoPlayEnabled = true; // forcefully start playing openVideoPlayer(); break; case R.id.detail_title_root_layout: @@ -530,6 +539,7 @@ public class VideoDetailFragment player.hideControls(0, 0); showSystemUi(); } else { + autoPlayEnabled = true; // forcefully start playing openVideoPlayer(); } @@ -791,7 +801,7 @@ public class VideoDetailFragment player.onPause(); } restoreDefaultOrientation(); - setAutoplay(false); + setAutoPlay(false); return true; } @@ -819,14 +829,11 @@ public class VideoDetailFragment } private void setupFromHistoryItem(final StackItem item) { - setAutoplay(false); + setAutoPlay(false); hideMainPlayer(); - setInitialData( - item.getServiceId(), - item.getUrl(), - !TextUtils.isEmpty(item.getTitle()) ? item.getTitle() : "", - item.getPlayQueue()); + setInitialData(item.getServiceId(), item.getUrl(), + item.getTitle() == null ? "" : item.getTitle(), item.getPlayQueue()); startLoading(false); // Maybe an item was deleted in background activity @@ -860,18 +867,17 @@ public class VideoDetailFragment } } - public void selectAndLoadVideo(final int sid, final String videoUrl, final String title, - final PlayQueue queue) { - // Situation when user switches from players to main player. - // All needed data is here, we can start watching - if (this.playQueue != null && this.playQueue.equals(queue)) { - openVideoPlayer(); - return; - } - setInitialData(sid, videoUrl, title, queue); - if (player != null) { + public void selectAndLoadVideo(final int newServiceId, + @Nullable final String newUrl, + @NonNull final String newTitle, + @Nullable final PlayQueue newQueue) { + if (player != null && newQueue != null && playQueue != null + && !Objects.equals(newQueue.getItem(), playQueue.getItem())) { + // Preloading can be disabled since playback is surely being replaced. player.disablePreloadingOfCurrentTrack(); } + + setInitialData(newServiceId, newUrl, newTitle, newQueue); startLoading(false, true); } @@ -956,7 +962,7 @@ public class VideoDetailFragment playQueue = new SinglePlayQueue(result); } if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(playQueue)) { - stack.push(new StackItem(serviceId, url, name, playQueue)); + stack.push(new StackItem(serviceId, url, title, playQueue)); } } if (isAutoplayEnabled()) { @@ -977,7 +983,7 @@ public class VideoDetailFragment if (shouldShowComments()) { pageAdapter.addFragment( - CommentsFragment.getInstance(serviceId, url, name), COMMENTS_TAB_TAG); + CommentsFragment.getInstance(serviceId, url, title), COMMENTS_TAB_TAG); } if (showRelatedStreams && null == relatedStreamsLayout) { @@ -1068,7 +1074,7 @@ public class VideoDetailFragment } } - private void openVideoPlayer() { + public void openVideoPlayer() { if (PreferenceManager.getDefaultSharedPreferences(activity) .getBoolean(this.getString(R.string.use_external_video_player_key), false)) { showExternalPlaybackDialog(); @@ -1094,7 +1100,7 @@ public class VideoDetailFragment private void openMainPlayer() { if (playerService == null) { - PlayerHolder.startService(App.getApp(), true, this); + PlayerHolder.startService(App.getApp(), autoPlayEnabled, this); return; } if (currentInfo == null) { @@ -1105,11 +1111,13 @@ public class VideoDetailFragment // Video view can have elements visible from popup, // We hide it here but once it ready the view will be shown in handleIntent() - playerService.getView().setVisibility(View.GONE); + if (playerService.getView() != null) { + playerService.getView().setVisibility(View.GONE); + } addVideoPlayerView(); final Intent playerIntent = NavigationHelper - .getPlayerIntent(requireContext(), MainPlayer.class, queue, null, true); + .getPlayerIntent(requireContext(), MainPlayer.class, queue, true, autoPlayEnabled); activity.startService(playerIntent); } @@ -1143,8 +1151,8 @@ public class VideoDetailFragment // Utils //////////////////////////////////////////////////////////////////////////*/ - public void setAutoplay(final boolean autoplay) { - this.autoPlayEnabled = autoplay; + public void setAutoPlay(final boolean autoPlay) { + this.autoPlayEnabled = autoPlay; } private void startOnExternalPlayer(@NonNull final Context context, @@ -1166,7 +1174,7 @@ public class VideoDetailFragment .getBoolean(getString(R.string.use_external_video_player_key), false); } - // This method overrides default behaviour when setAutoplay() is called. + // This method overrides default behaviour when setAutoPlay() is called. // Don't auto play if the user selected an external player or disabled it in settings private boolean isAutoplayEnabled() { return autoPlayEnabled @@ -1246,9 +1254,9 @@ public class VideoDetailFragment final DisplayMetrics metrics = getResources().getDisplayMetrics(); if (getView() != null) { - final int height = isInMultiWindow() - ? requireView().getHeight() - : activity.getWindow().getDecorView().getHeight(); + final int height = (isInMultiWindow() + ? requireView() + : activity.getWindow().getDecorView()).getHeight(); setHeightThumbnail(height, metrics); getView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener); } @@ -1269,9 +1277,9 @@ public class VideoDetailFragment requireView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener); if (player != null && player.isFullscreen()) { - final int height = isInMultiWindow() - ? requireView().getHeight() - : activity.getWindow().getDecorView().getHeight(); + final int height = (isInMultiWindow() + ? requireView() + : activity.getWindow().getDecorView()).getHeight(); // Height is zero when the view is not yet displayed like after orientation change if (height != 0) { setHeightThumbnail(height, metrics); @@ -1279,9 +1287,9 @@ public class VideoDetailFragment requireView().getViewTreeObserver().addOnPreDrawListener(preDrawListener); } } else { - final int height = isPortrait - ? (int) (metrics.widthPixels / (16.0f / 9.0f)) - : (int) (metrics.heightPixels / 2.0f); + final int height = (int) (isPortrait + ? metrics.widthPixels / (16.0f / 9.0f) + : metrics.heightPixels / 2.0f); setHeightThumbnail(height, metrics); } } @@ -1302,12 +1310,14 @@ public class VideoDetailFragment contentRootLayoutHiding.setVisibility(View.VISIBLE); } - protected void setInitialData(final int sid, final String u, final String title, - final PlayQueue queue) { - this.serviceId = sid; - this.url = u; - this.name = !TextUtils.isEmpty(title) ? title : ""; - this.playQueue = queue; + protected void setInitialData(final int newServiceId, + @Nullable final String newUrl, + @NonNull final String newTitle, + @Nullable final PlayQueue newPlayQueue) { + this.serviceId = newServiceId; + this.url = newUrl; + this.title = newTitle; + this.playQueue = newPlayQueue; } private void setErrorImage(final int imageResource) { @@ -1400,7 +1410,7 @@ public class VideoDetailFragment animateView(detailPositionView, false, 100); animateView(positionView, false, 50); - videoTitleTextView.setText(name != null ? name : ""); + videoTitleTextView.setText(title); videoTitleTextView.setMaxLines(1); animateView(videoTitleTextView, true, 0); @@ -1445,7 +1455,7 @@ public class VideoDetailFragment } } animateView(thumbnailPlayButton, true, 200); - videoTitleTextView.setText(name); + videoTitleTextView.setText(title); if (!TextUtils.isEmpty(info.getSubChannelName())) { displayBothUploaderAndSubChannel(info); @@ -1527,7 +1537,7 @@ public class VideoDetailFragment if (info.getUploadDate() != null) { videoUploadDateView.setText(Localization - .localizeUploadDate(activity, info.getUploadDate().date().getTime())); + .localizeUploadDate(activity, info.getUploadDate().offsetDateTime())); videoUploadDateView.setVisibility(View.VISIBLE); } else { videoUploadDateView.setText(null); @@ -1735,31 +1745,33 @@ public class VideoDetailFragment @Override public void onQueueUpdate(final PlayQueue queue) { playQueue = queue; + if (DEBUG) { + Log.d(TAG, "onQueueUpdate() called with: serviceId = [" + + serviceId + "], videoUrl = [" + url + "], name = [" + + title + "], playQueue = [" + playQueue + "]"); + } + // This should be the only place where we push data to stack. // It will allow to have live instance of PlayQueue with actual information about // deleted/added items inside Channel/Playlist queue and makes possible to have // a history of played items - if ((stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue) - && queue.getItem() != null)) { - stack.push(new StackItem(queue.getItem().getServiceId(), - queue.getItem().getUrl(), - queue.getItem().getTitle(), - queue)); - } else { - final StackItem stackWithQueue = findQueueInStack(queue); - if (stackWithQueue != null) { - // On every MainPlayer service's destroy() playQueue gets disposed and - // no longer able to track progress. That's why we update our cached disposed - // queue with the new one that is active and have the same history. - // Without that the cached playQueue will have an old recovery position - stackWithQueue.setPlayQueue(queue); - } + @Nullable final StackItem stackPeek = stack.peek(); + if (stackPeek != null && !stackPeek.getPlayQueue().equals(queue)) { + @Nullable final PlayQueueItem playQueueItem = queue.getItem(); + if (playQueueItem != null) { + stack.push(new StackItem(playQueueItem.getServiceId(), playQueueItem.getUrl(), + playQueueItem.getTitle(), queue)); + return; + } // else continue below } - if (DEBUG) { - Log.d(TAG, "onQueueUpdate() called with: serviceId = [" - + serviceId + "], videoUrl = [" + url + "], name = [" - + name + "], playQueue = [" + playQueue + "]"); + @Nullable final StackItem stackWithQueue = findQueueInStack(queue); + if (stackWithQueue != null) { + // On every MainPlayer service's destroy() playQueue gets disposed and + // no longer able to track progress. That's why we update our cached disposed + // queue with the new one that is active and have the same history. + // Without that the cached playQueue will have an old recovery position + stackWithQueue.setPlayQueue(queue); } } @@ -1821,7 +1833,7 @@ public class VideoDetailFragment currentInfo = info; setInitialData(info.getServiceId(), info.getUrl(), info.getName(), queue); - setAutoplay(false); + setAutoPlay(false); // Delay execution just because it freezes the main thread, and while playing // next/previous video you see visual glitches // (when non-vertical video goes after vertical video) @@ -2035,7 +2047,7 @@ public class VideoDetailFragment private void checkLandscape() { if ((!player.isPlaying() && player.getPlayQueue() != playQueue) || player.getPlayQueue() == null) { - setAutoplay(true); + setAutoPlay(true); } player.checkLandscape(); @@ -2062,6 +2074,7 @@ public class VideoDetailFragment return url == null; } + @Nullable private StackItem findQueueInStack(final PlayQueue queue) { StackItem item = null; final Iterator iterator = stack.descendingIterator(); @@ -2284,10 +2297,10 @@ public class VideoDetailFragment }); } - private void updateOverlayData(@Nullable final String title, + private void updateOverlayData(@Nullable final String overlayTitle, @Nullable final String uploader, @Nullable final String thumbnailUrl) { - overlayTitleTextView.setText(TextUtils.isEmpty(title) ? "" : title); + overlayTitleTextView.setText(TextUtils.isEmpty(overlayTitle) ? "" : overlayTitle); overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader); overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); if (!TextUtils.isEmpty(thumbnailUrl)) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 6a8611d0e..41263bc34 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -321,8 +321,9 @@ public abstract class BaseListFragment extends BaseStateFragment private void onStreamSelected(final StreamInfoItem selectedItem) { onItemSelected(selectedItem); - NavigationHelper.openVideoDetailFragment(getFM(), - selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(), + null, false); } protected void onScrollToBottom() { @@ -378,11 +379,7 @@ public abstract class BaseListFragment extends BaseStateFragment final ActionBar supportActionBar = activity.getSupportActionBar(); if (supportActionBar != null) { supportActionBar.setDisplayShowTitleEnabled(true); - if (useAsFrontPage) { - supportActionBar.setDisplayHomeAsUpEnabled(false); - } else { - supportActionBar.setDisplayHomeAsUpEnabled(true); - } + supportActionBar.setDisplayHomeAsUpEnabled(!useAsFrontPage); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 8902834e4..6ec818909 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -50,7 +50,6 @@ import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; @@ -495,13 +494,12 @@ public class ChannelFragment extends BaseListInfoFragment // handling ContentNotSupportedException not to show the error but an appropriate string // so that crashes won't be sent uselessly and the user will understand what happened - for (Iterator it = errors.iterator(); it.hasNext();) { - final Throwable throwable = it.next(); + errors.removeIf(throwable -> { if (throwable instanceof ContentNotSupportedException) { showContentNotSupported(); - it.remove(); } - } + return throwable instanceof ContentNotSupportedException; + }); if (!errors.isEmpty()) { showSnackBarError(errors, UserAction.REQUESTED_CHANNEL, @@ -519,7 +517,7 @@ public class ChannelFragment extends BaseListInfoFragment monitorSubscription(result); headerPlayAllButton.setOnClickListener(view -> NavigationHelper - .playOnMainPlayer(activity, getPlayQueue(), true)); + .playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper .playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> NavigationHelper diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 67f1a007a..71b51f9a1 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -319,7 +319,7 @@ public class PlaylistFragment extends BaseListInfoFragment { .subscribe(getPlaylistBookmarkSubscriber()); headerPlayAllButton.setOnClickListener(view -> - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index c402565fd..1e54176d4 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -61,7 +61,6 @@ import org.schabi.newpipe.util.ServiceHelper; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; @@ -758,16 +757,9 @@ public class SearchFragment extends BaseListFragment iterator = networkResult.iterator(); - while (iterator.hasNext() && localResult.size() > 0) { - final SuggestionItem next = iterator.next(); - for (final SuggestionItem item : localResult) { - if (item.query.equals(next.query)) { - iterator.remove(); - break; - } - } - } + networkResult.removeIf(networkItem -> + localResult.stream().anyMatch(localItem -> + localItem.query.equals(networkItem.query))); if (networkResult.size() > 0) { result.addAll(networkResult); diff --git a/app/src/main/java/org/schabi/newpipe/ktx/OffsetDateTime.kt b/app/src/main/java/org/schabi/newpipe/ktx/OffsetDateTime.kt new file mode 100644 index 000000000..b3df83c25 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ktx/OffsetDateTime.kt @@ -0,0 +1,10 @@ +package org.schabi.newpipe.ktx + +import java.time.OffsetDateTime +import java.time.ZoneId +import java.util.Calendar +import java.util.GregorianCalendar + +fun OffsetDateTime.toCalendar(zoneId: ZoneId = ZoneId.systemDefault()): Calendar { + return GregorianCalendar.from(if (zoneId != offset) atZoneSameInstant(zoneId) else toZonedDateTime()) +} diff --git a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java index 5b67f51da..da8902c08 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -26,7 +26,8 @@ import org.schabi.newpipe.util.FallbackViewHolder; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.OnClickGesture; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.ArrayList; import java.util.List; @@ -69,7 +70,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter localItems; private final HistoryRecordManager recordManager; - private final DateFormat dateFormat; + private final DateTimeFormatter dateTimeFormatter; private boolean showFooter = false; private boolean useGridVariant = false; @@ -80,8 +81,8 @@ public class LocalItemListAdapter extends RecyclerView.Adapter(); - dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, - Localization.getPreferredLocale(context)); + dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) + .withLocale(Localization.getPreferredLocale(context)); } public void setSelectedListener(final OnClickGesture listener) { @@ -303,7 +304,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter> { val items = ArrayList(it.size) - for (streamEntity in it) items.add(streamEntity.toStreamInfoItem()) + it.mapTo(items) { it.toStreamInfoItem() } return@map items } } - fun outdatedSubscriptions(outdatedThreshold: Date) = feedTable.getAllOutdated(outdatedThreshold) + fun outdatedSubscriptions(outdatedThreshold: OffsetDateTime) = feedTable.getAllOutdated(outdatedThreshold) fun notLoadedCount(groupId: Long = FeedGroupEntity.GROUP_ALL_ID): Flowable { return when (groupId) { @@ -64,7 +60,7 @@ class FeedDatabaseManager(context: Context) { } } - fun outdatedSubscriptionsForGroup(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, outdatedThreshold: Date) = + fun outdatedSubscriptionsForGroup(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, outdatedThreshold: OffsetDateTime) = feedTable.getAllOutdatedForGroup(groupId, outdatedThreshold) fun markAsOutdated(subscriptionId: Long) = feedTable @@ -73,7 +69,7 @@ class FeedDatabaseManager(context: Context) { fun upsertAll( subscriptionId: Long, items: List, - oldestAllowedDate: Date = FEED_OLDEST_ALLOWED_DATE.time + oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE ) { val itemsToInsert = ArrayList() loop@ for (streamItem in items) { @@ -81,7 +77,7 @@ class FeedDatabaseManager(context: Context) { itemsToInsert += when { uploadDate == null && streamItem.streamType == StreamType.LIVE_STREAM -> streamItem - uploadDate != null && uploadDate.date().time >= oldestAllowedDate -> streamItem + uploadDate != null && uploadDate.offsetDateTime() >= oldestAllowedDate -> streamItem else -> continue@loop } } @@ -96,10 +92,11 @@ class FeedDatabaseManager(context: Context) { feedTable.insertAll(feedEntities) } - feedTable.setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, Calendar.getInstance().time)) + feedTable.setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, + OffsetDateTime.now(ZoneOffset.UTC))) } - fun removeOrphansOrOlderStreams(oldestAllowedDate: Date = FEED_OLDEST_ALLOWED_DATE.time) { + fun removeOrphansOrOlderStreams(oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE) { feedTable.unlinkStreamsOlderThan(oldestAllowedDate) streamTable.deleteOrphans() } @@ -159,7 +156,7 @@ class FeedDatabaseManager(context: Context) { .observeOn(AndroidSchedulers.mainThread()) } - fun oldestSubscriptionUpdate(groupId: Long): Flowable> { + fun oldestSubscriptionUpdate(groupId: Long): Flowable> { return when (groupId) { FeedGroupEntity.GROUP_ALL_ID -> feedTable.oldestSubscriptionUpdateFromAll() else -> feedTable.oldestSubscriptionUpdate(groupId) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt index da2b5ffa4..13c3183da 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt @@ -9,11 +9,11 @@ import io.reactivex.Flowable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.functions.Function4 import io.reactivex.schedulers.Schedulers -import java.util.Calendar -import java.util.Date +import java.time.OffsetDateTime import java.util.concurrent.TimeUnit import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.extractor.stream.StreamInfoItem +import org.schabi.newpipe.ktx.toCalendar import org.schabi.newpipe.local.feed.service.FeedEventManager import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ErrorResultEvent import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.IdleEvent @@ -41,7 +41,7 @@ class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEn feedDatabaseManager.notLoadedCount(groupId), feedDatabaseManager.oldestSubscriptionUpdate(groupId), - Function4 { t1: FeedEventManager.Event, t2: List, t3: Long, t4: List -> + Function4 { t1: FeedEventManager.Event, t2: List, t3: Long, t4: List -> return@Function4 CombineResultHolder(t1, t2, t3, t4.firstOrNull()) } ) @@ -51,8 +51,7 @@ class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEn .subscribe { val (event, listFromDB, notLoadedCount, oldestUpdate) = it - val oldestUpdateCalendar = - oldestUpdate?.let { Calendar.getInstance().apply { time = it } } + val oldestUpdateCalendar = oldestUpdate?.toCalendar() mutableStateLiveData.postValue(when (event) { is IdleEvent -> FeedState.LoadedState(listFromDB, oldestUpdateCalendar, notLoadedCount) @@ -71,5 +70,5 @@ class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEn combineDisposable.dispose() } - private data class CombineResultHolder(val t1: FeedEventManager.Event, val t2: List, val t3: Long, val t4: Date?) + private data class CombineResultHolder(val t1: FeedEventManager.Event, val t2: List, val t3: Long, val t4: OffsetDateTime?) } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt index 0181f2711..8d3afbc7e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt @@ -41,7 +41,8 @@ import io.reactivex.functions.Function import io.reactivex.processors.PublishProcessor import io.reactivex.schedulers.Schedulers import java.io.IOException -import java.util.Calendar +import java.time.OffsetDateTime +import java.time.ZoneOffset import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger @@ -161,8 +162,8 @@ class FeedLoadService : Service() { companion object { fun wrapList(subscriptionId: Long, info: ListInfo): List { val toReturn = ArrayList(info.errors.size) - for (error in info.errors) { - toReturn.add(RequestException(subscriptionId, info.serviceId.toString() + ":" + info.url, error)) + info.errors.mapTo(toReturn) { + RequestException(subscriptionId, info.serviceId.toString() + ":" + info.url, it) } return toReturn } @@ -172,9 +173,7 @@ class FeedLoadService : Service() { private fun startLoading(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, useFeedExtractor: Boolean, thresholdOutdatedSeconds: Int) { feedResultsHolder = ResultsHolder() - val outdatedThreshold = Calendar.getInstance().apply { - add(Calendar.SECOND, -thresholdOutdatedSeconds) - }.time + val outdatedThreshold = OffsetDateTime.now(ZoneOffset.UTC).minusSeconds(thresholdOutdatedSeconds.toLong()) val subscriptions = when (groupId) { FeedGroupEntity.GROUP_ALL_ID -> feedDatabaseManager.outdatedSubscriptions(outdatedThreshold) diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java index 6af57bc94..7f5c4f7a7 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java @@ -44,9 +44,10 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.playqueue.PlayQueueItem; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.List; import io.reactivex.Completable; @@ -85,7 +86,7 @@ public class HistoryRecordManager { return Maybe.empty(); } - final Date currentTime = new Date(); + final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); return Maybe.fromCallable(() -> database.runInTransaction(() -> { final long streamId = streamTable.upsert(new StreamEntity(info)); final StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry(streamId); @@ -161,7 +162,7 @@ public class HistoryRecordManager { return Maybe.empty(); } - final Date currentTime = new Date(); + final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); final SearchHistoryEntry newEntry = new SearchHistoryEntry(currentTime, serviceId, search); return Maybe.fromCallable(() -> database.runInTransaction(() -> { diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 48a0e3430..8cd4e4c7e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -25,6 +25,7 @@ import org.reactivestreams.Subscription; import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.stream.StreamStatisticsEntry; +import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.info_list.InfoItemDialog; @@ -43,6 +44,7 @@ import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; import icepick.State; @@ -68,18 +70,19 @@ public class StatisticsPlaylistFragment private HistoryRecordManager recordManager; private List processResult(final List results) { + final Comparator comparator; switch (sortMode) { case LAST_PLAYED: - Collections.sort(results, (left, right) -> - right.getLatestAccessDate().compareTo(left.getLatestAccessDate())); - return results; + comparator = Comparator.comparing(StreamStatisticsEntry::getLatestAccessDate); + break; case MOST_PLAYED: - Collections.sort(results, (left, right) -> - Long.compare(right.getWatchCount(), left.getWatchCount())); - return results; + comparator = Comparator.comparingLong(StreamStatisticsEntry::getWatchCount); + break; default: return null; } + Collections.sort(results, comparator.reversed()); + return results; } /////////////////////////////////////////////////////////////////////////// @@ -147,11 +150,10 @@ public class StatisticsPlaylistFragment @Override public void selected(final LocalItem selectedItem) { if (selectedItem instanceof StreamStatisticsEntry) { - final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem; - NavigationHelper.openVideoDetailFragment(getFM(), - item.getStreamEntity().getServiceId(), - item.getStreamEntity().getUrl(), - item.getStreamEntity().getTitle()); + final StreamEntity item = + ((StreamStatisticsEntry) selectedItem).getStreamEntity(); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + item.getServiceId(), item.getUrl(), item.getTitle(), null, false); } } @@ -323,7 +325,7 @@ public class StatisticsPlaylistFragment } headerPlayAllButton.setOnClickListener(view -> - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java index c4307fcde..a093d93e1 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java @@ -9,7 +9,7 @@ import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; /* * Created by Christian Schabesberger on 12.02.17. @@ -41,7 +41,7 @@ public abstract class LocalItemHolder extends RecyclerView.ViewHolder { } public abstract void updateFromItem(LocalItem item, HistoryRecordManager historyRecordManager, - DateFormat dateFormat); + DateTimeFormatter dateTimeFormatter); public void updateState(final LocalItem localItem, final HistoryRecordManager historyRecordManager) { } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index 458b3c30e..5560df3e0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -10,7 +10,7 @@ import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; public class LocalPlaylistItemHolder extends PlaylistItemHolder { public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { @@ -25,7 +25,7 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { if (!(localItem instanceof PlaylistMetadataEntry)) { return; } @@ -39,6 +39,6 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS); - super.updateFromItem(localItem, historyRecordManager, dateFormat); + super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java index 33722e380..f7cf69708 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java @@ -20,7 +20,7 @@ import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.concurrent.TimeUnit; @@ -52,7 +52,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { if (!(localItem instanceof PlaylistStreamEntry)) { return; } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java index 8eaef807a..f473b0277 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java @@ -20,7 +20,7 @@ import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.concurrent.TimeUnit; @@ -71,10 +71,10 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { } private String getStreamInfoDetailLine(final StreamStatisticsEntry entry, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { final String watchCount = Localization .shortViewCount(itemBuilder.getContext(), entry.getWatchCount()); - final String uploadDate = dateFormat.format(entry.getLatestAccessDate()); + final String uploadDate = dateTimeFormatter.format(entry.getLatestAccessDate()); final String serviceName = NewPipe.getNameOfService(entry.getStreamEntity().getServiceId()); return Localization.concatenateStrings(watchCount, uploadDate, serviceName); } @@ -82,7 +82,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { if (!(localItem instanceof StreamStatisticsEntry)) { return; } @@ -116,7 +116,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { } if (itemAdditionalDetails != null) { - itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateFormat)); + itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateTimeFormatter)); } // Default thumbnail is shown on error, while loading and if the url is empty diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java index 11e3deb67..e8c53161e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java @@ -9,7 +9,7 @@ import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; public abstract class PlaylistItemHolder extends LocalItemHolder { public final ImageView itemThumbnailView; @@ -34,7 +34,7 @@ public abstract class PlaylistItemHolder extends LocalItemHolder { @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { itemView.setOnClickListener(view -> { if (itemBuilder.getOnItemSelectedListener() != null) { itemBuilder.getOnItemSelectedListener().selected(localItem); diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java index a47d61d2f..a39e3cecb 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java @@ -11,7 +11,7 @@ import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; public class RemotePlaylistItemHolder extends PlaylistItemHolder { public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, @@ -27,7 +27,7 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { if (!(localItem instanceof PlaylistRemoteEntity)) { return; } @@ -48,6 +48,6 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { itemBuilder.displayImage(item.getThumbnailUrl(), itemThumbnailView, ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS); - super.updateFromItem(localItem, historyRecordManager, dateFormat); + super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 32fac9de0..71df07a4b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -30,6 +30,7 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.history.model.StreamHistoryEntry; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; +import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; @@ -178,10 +179,10 @@ public class LocalPlaylistFragment extends BaseLocalListFragment - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt index e9a7e4eb7..4b1a4df5e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt @@ -37,8 +37,8 @@ class FeedGroupDialogViewModel( BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) } ) .distinctUntilChanged() - .switchMap { filter -> - subscriptionManager.getSubscriptions(groupId, filter.query, filter.showOnlyUngrouped) + .switchMap { (query, showOnlyUngrouped) -> + subscriptionManager.getSubscriptions(groupId, query, showOnlyUngrouped) }.map { list -> list.map { PickerSubscriptionItem(it) } } private val mutableGroupLiveData = MutableLiveData() diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java index 0e5222f5e..2fc710fb0 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java @@ -2,11 +2,8 @@ package org.schabi.newpipe.player; import android.content.Intent; import android.view.Menu; -import android.view.MenuItem; import org.schabi.newpipe.R; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PermissionHelper; public final class BackgroundPlayerActivity extends ServicePlayerActivity { @@ -46,31 +43,6 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity { return R.menu.menu_play_queue_bg; } - @Override - public boolean onPlayerOptionSelected(final MenuItem item) { - if (item.getItemId() == R.id.action_switch_popup) { - - if (!PermissionHelper.isPopupEnabled(this)) { - PermissionHelper.showPopupEnablementToast(this); - return true; - } - - this.player.setRecovery(); - NavigationHelper.playOnPopupPlayer( - getApplicationContext(), player.playQueue, this.player.isPlaying()); - return true; - } - - if (item.getItemId() == R.id.action_switch_background) { - this.player.setRecovery(); - NavigationHelper.playOnBackgroundPlayer( - getApplicationContext(), player.playQueue, this.player.isPlaying()); - return true; - } - - return false; - } - @Override public void setupMenu(final Menu menu) { if (player == null) { diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 786e0d70d..fa2278652 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -136,7 +136,7 @@ public abstract class BasePlayer implements @NonNull public static final String RESUME_PLAYBACK = "resume_playback"; @NonNull - public static final String START_PAUSED = "start_paused"; + public static final String PLAY_WHEN_READY = "play_when_ready"; @NonNull public static final String SELECT_ON_APPEND = "select_on_append"; @NonNull @@ -236,7 +236,7 @@ public abstract class BasePlayer implements this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter); final TrackSelection.Factory trackSelectionFactory = PlayerHelper - .getQualitySelector(context); + .getQualitySelector(); this.trackSelector = new CustomTrackSelector(context, trackSelectionFactory); this.loadControl = new LoadController(); @@ -316,6 +316,7 @@ public abstract class BasePlayer implements final boolean samePlayQueue = playQueue != null && playQueue.equals(queue); final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode()); + final boolean playWhenReady = intent.getBooleanExtra(PLAY_WHEN_READY, true); final boolean isMuted = intent .getBooleanExtra(IS_MUTED, simpleExoPlayer != null && isMuted()); @@ -341,16 +342,20 @@ public abstract class BasePlayer implements simpleExoPlayer.retry(); } simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition()); - return; + simpleExoPlayer.setPlayWhenReady(playWhenReady); - } else if (samePlayQueue && !playQueue.isDisposed() && simpleExoPlayer != null) { + } else if (simpleExoPlayer != null + && samePlayQueue + && playQueue != null + && !playQueue.isDisposed()) { // Do not re-init the same PlayQueue. Save time // Player can have state = IDLE when playback is stopped or failed // and we should retry() in this case if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) { simpleExoPlayer.retry(); } - return; + simpleExoPlayer.setPlayWhenReady(playWhenReady); + } else if (intent.getBooleanExtra(RESUME_PLAYBACK, false) && isPlaybackResumeEnabled() && !samePlayQueue) { @@ -365,7 +370,7 @@ public abstract class BasePlayer implements state -> { queue.setRecovery(queue.getIndex(), state.getProgressTime()); initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, - playbackSkipSilence, true, isMuted); + playbackSkipSilence, playWhenReady, isMuted); }, error -> { if (DEBUG) { @@ -373,24 +378,22 @@ public abstract class BasePlayer implements } // In case any error we can start playback without history initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, - playbackSkipSilence, true, isMuted); + playbackSkipSilence, playWhenReady, isMuted); }, () -> { // Completed but not found in history initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, - playbackSkipSilence, true, isMuted); + playbackSkipSilence, playWhenReady, isMuted); } ); databaseUpdateReactor.add(stateLoader); - return; } + } else { + // Good to go... + // In a case of equal PlayQueues we can re-init old one but only when it is disposed + initPlayback(samePlayQueue ? playQueue : queue, repeatMode, playbackSpeed, + playbackPitch, playbackSkipSilence, playWhenReady, isMuted); } - // Good to go... - // In a case of equal PlayQueues we can re-init old one but only when it is disposed - initPlayback(samePlayQueue ? playQueue : queue, repeatMode, - playbackSpeed, playbackPitch, playbackSkipSilence, - !intent.getBooleanExtra(START_PAUSED, false), - isMuted); } private PlaybackParameters retrievePlaybackParametersFromPreferences() { diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java index e8554e5ac..63f6a400e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java @@ -30,6 +30,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import org.schabi.newpipe.R; @@ -225,12 +226,13 @@ public final class MainPlayer extends Service { // DisplayMetrics from activity context knows about MultiWindow feature // while DisplayMetrics from app context doesn't final DisplayMetrics metrics = (playerImpl != null - && playerImpl.getParentActivity() != null) - ? playerImpl.getParentActivity().getResources().getDisplayMetrics() - : getResources().getDisplayMetrics(); + && playerImpl.getParentActivity() != null + ? playerImpl.getParentActivity().getResources() + : getResources()).getDisplayMetrics(); return metrics.heightPixels < metrics.widthPixels; } + @Nullable public View getView() { if (playerImpl == null) { return null; @@ -240,7 +242,7 @@ public final class MainPlayer extends Service { } public void removeViewFromParent() { - if (getView().getParent() != null) { + if (getView() != null && getView().getParent() != null) { if (playerImpl.getParentActivity() != null) { // This means view was added to fragment final ViewGroup parent = (ViewGroup) getView().getParent(); diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 10fff5298..62f1d5dc2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -120,7 +120,10 @@ public final class NotificationUtil { .setCategory(NotificationCompat.CATEGORY_TRANSPORT) .setShowWhen(false) .setSmallIcon(R.drawable.ic_newpipe_triangle_white) - .setColor(ContextCompat.getColor(player.context, R.color.gray)) + .setColor(ContextCompat.getColor(player.context, R.color.dark_background_color)) + .setColorized(player.sharedPreferences.getBoolean( + player.context.getString(R.string.notification_colorize_key), + true)) .setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)); diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java index 72f56cb1b..ad4c603cd 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java @@ -27,9 +27,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; -import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; @@ -42,9 +40,9 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder; import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder; import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; -import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.ThemeHelper; import java.util.Collections; @@ -113,9 +111,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity public abstract int getPlayerOptionMenuResource(); - public abstract boolean onPlayerOptionSelected(MenuItem item); - public abstract void setupMenu(Menu m); + //////////////////////////////////////////////////////////////////////////// // Activity Lifecycle //////////////////////////////////////////////////////////////////////////// @@ -187,12 +184,22 @@ public abstract class ServicePlayerActivity extends AppCompatActivity return true; case R.id.action_switch_main: this.player.setRecovery(); - getApplicationContext().startActivity( - getSwitchIntent(MainActivity.class, MainPlayer.PlayerType.VIDEO) - .putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying())); + NavigationHelper.playOnMainPlayer(this, player.getPlayQueue(), true); + return true; + case R.id.action_switch_popup: + if (PermissionHelper.isPopupEnabled(this)) { + this.player.setRecovery(); + NavigationHelper.playOnPopupPlayer(this, player.playQueue, true); + } else { + PermissionHelper.showPopupEnablementToast(this); + } + return true; + case R.id.action_switch_background: + this.player.setRecovery(); + NavigationHelper.playOnBackgroundPlayer(this, player.playQueue, true); return true; } - return onPlayerOptionSelected(item) || super.onOptionsItemSelected(item); + return super.onOptionsItemSelected(item); } @Override @@ -201,24 +208,6 @@ public abstract class ServicePlayerActivity extends AppCompatActivity unbind(); } - protected Intent getSwitchIntent(final Class clazz, final MainPlayer.PlayerType playerType) { - return NavigationHelper.getPlayerIntent(getApplicationContext(), clazz, - this.player.getPlayQueue(), this.player.getRepeatMode(), - this.player.getPlaybackSpeed(), this.player.getPlaybackPitch(), - this.player.getPlaybackSkipSilence(), - null, - true, - !this.player.isPlaying(), - this.player.isMuted()) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM) - .putExtra(Constants.KEY_URL, this.player.getVideoUrl()) - .putExtra(Constants.KEY_TITLE, this.player.getVideoTitle()) - .putExtra(Constants.KEY_SERVICE_ID, - this.player.getCurrentMetadata().getMetadata().getServiceId()) - .putExtra(VideoPlayer.PLAYER_TYPE, playerType); - } - //////////////////////////////////////////////////////////////////////////// // Service Connection //////////////////////////////////////////////////////////////////////////// @@ -367,7 +356,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity final MenuItem detail = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, Menu.NONE, R.string.play_queue_stream_detail); detail.setOnMenuItemClickListener(menuItem -> { - onOpenDetail(item.getServiceId(), item.getUrl(), item.getTitle()); + // playQueue is null since we don't want any queue change + NavigationHelper.openVideoDetail(this, item.getServiceId(), item.getUrl(), + item.getTitle(), null, false); return true; }); @@ -454,11 +445,6 @@ public abstract class ServicePlayerActivity extends AppCompatActivity }; } - private void onOpenDetail(final int serviceId, final String videoUrl, - final String videoTitle) { - NavigationHelper.openVideoDetail(this, serviceId, videoUrl, videoTitle); - } - private void scrollToSelected() { if (player == null) { return; @@ -748,11 +734,10 @@ public abstract class ServicePlayerActivity extends AppCompatActivity //2) Icon change accordingly to current App Theme // using rootView.getContext() because getApplicationContext() didn't work - item.setIcon(player.isMuted() - ? ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), - R.attr.ic_volume_off) - : ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), - R.attr.ic_volume_up)); + item.setIcon(ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), + player.isMuted() + ? R.attr.ic_volume_off + : R.attr.ic_volume_up)); } } } diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index 2ac61cb7f..a0b536052 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -76,9 +76,7 @@ import com.google.android.exoplayer2.ui.SubtitleView; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.nostra13.universalimageloader.core.assist.FailReason; -import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; @@ -97,7 +95,6 @@ import org.schabi.newpipe.player.resolver.AudioPlaybackResolver; import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.player.resolver.VideoPlaybackResolver; import org.schabi.newpipe.util.AnimationUtils; -import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KoreUtil; import org.schabi.newpipe.util.ListHelper; @@ -264,7 +261,12 @@ public class VideoPlayerImpl extends VideoPlayer onQueueClosed(); // Android TV: without it focus will frame the whole player playPauseButton.requestFocus(); - onPlay(); + + if (simpleExoPlayer.getPlayWhenReady()) { + onPlay(); + } else { + onPause(); + } } NavigationHelper.sendPlayerStartedEvent(service); } @@ -800,40 +802,6 @@ public class VideoPlayerImpl extends VideoPlayer setupScreenRotationButton(); } - public void switchFromPopupToMain() { - if (DEBUG) { - Log.d(TAG, "switchFromPopupToMain() called"); - } - if (!popupPlayerSelected() || simpleExoPlayer == null || getCurrentMetadata() == null) { - return; - } - - setRecovery(); - service.removeViewFromParent(); - final Intent intent = NavigationHelper.getPlayerIntent( - service, - MainActivity.class, - this.getPlayQueue(), - this.getRepeatMode(), - this.getPlaybackSpeed(), - this.getPlaybackPitch(), - this.getPlaybackSkipSilence(), - null, - true, - !isPlaying(), - isMuted() - ); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Constants.KEY_SERVICE_ID, - getCurrentMetadata().getMetadata().getServiceId()); - intent.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM); - intent.putExtra(Constants.KEY_URL, getVideoUrl()); - intent.putExtra(Constants.KEY_TITLE, getVideoTitle()); - intent.putExtra(VideoDetailFragment.AUTO_PLAY, true); - service.onDestroy(); - context.startActivity(intent); - } - @Override public void onClick(final View v) { super.onClick(v); @@ -861,7 +829,9 @@ public class VideoPlayerImpl extends VideoPlayer } else if (v.getId() == openInBrowser.getId()) { onOpenInBrowserClicked(); } else if (v.getId() == fullscreenButton.getId()) { - switchFromPopupToMain(); + setRecovery(); + NavigationHelper.playOnMainPlayer(context, getPlayQueue(), true); + return; } else if (v.getId() == screenRotationButton.getId()) { // Only if it's not a vertical video or vertical video but in landscape with locked // orientation a screen orientation can be changed automatically @@ -1766,10 +1736,13 @@ public class VideoPlayerImpl extends VideoPlayer updateScreenSize(); + final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(service); final float defaultSize = service.getResources().getDimension(R.dimen.popup_default_width); final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(service); - popupWidth = sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize); + popupWidth = popupRememberSizeAndPos + ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) + : defaultSize; popupHeight = getMinimumVideoHeight(popupWidth); popupLayoutParams = new WindowManager.LayoutParams( @@ -1783,8 +1756,10 @@ public class VideoPlayerImpl extends VideoPlayer final int centerX = (int) (screenWidth / 2f - popupWidth / 2f); final int centerY = (int) (screenHeight / 2f - popupHeight / 2f); - popupLayoutParams.x = sharedPreferences.getInt(POPUP_SAVED_X, centerX); - popupLayoutParams.y = sharedPreferences.getInt(POPUP_SAVED_Y, centerY); + popupLayoutParams.x = popupRememberSizeAndPos + ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX; + popupLayoutParams.y = popupRememberSizeAndPos + ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY; checkPopupPositionBounds(); diff --git a/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt b/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt new file mode 100644 index 000000000..681c1b9af --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt @@ -0,0 +1,500 @@ +package org.schabi.newpipe.player.event + +import android.content.Context +import android.os.Handler +import android.util.Log +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import org.schabi.newpipe.player.BasePlayer +import org.schabi.newpipe.player.MainPlayer +import org.schabi.newpipe.player.VideoPlayerImpl +import org.schabi.newpipe.player.helper.PlayerHelper +import org.schabi.newpipe.util.AnimationUtils +import kotlin.math.abs +import kotlin.math.hypot +import kotlin.math.max + +/** + * Base gesture handling for [VideoPlayerImpl] + * + * This class contains the logic for the player gestures like View preparations + * and provides some abstract methods to make it easier separating the logic from the UI. + */ +abstract class BasePlayerGestureListener( + @JvmField + protected val playerImpl: VideoPlayerImpl, + @JvmField + protected val service: MainPlayer +) : GestureDetector.SimpleOnGestureListener(), View.OnTouchListener { + + // /////////////////////////////////////////////////////////////////// + // Abstract methods for VIDEO and POPUP + // /////////////////////////////////////////////////////////////////// + + abstract fun onDoubleTap(event: MotionEvent, portion: DisplayPortion) + + abstract fun onSingleTap(playerType: MainPlayer.PlayerType) + + abstract fun onScroll( + playerType: MainPlayer.PlayerType, + portion: DisplayPortion, + initialEvent: MotionEvent, + movingEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ) + + abstract fun onScrollEnd(playerType: MainPlayer.PlayerType, event: MotionEvent) + + // /////////////////////////////////////////////////////////////////// + // Abstract methods for POPUP (exclusive) + // /////////////////////////////////////////////////////////////////// + + abstract fun onPopupResizingStart() + + abstract fun onPopupResizingEnd() + + private var initialPopupX: Int = -1 + private var initialPopupY: Int = -1 + + private var isMovingInMain = false + private var isMovingInPopup = false + private var isResizing = false + + private val tossFlingVelocity = PlayerHelper.getTossFlingVelocity() + + // [popup] initial coordinates and distance between fingers + private var initPointerDistance = -1.0 + private var initFirstPointerX = -1f + private var initFirstPointerY = -1f + private var initSecPointerX = -1f + private var initSecPointerY = -1f + + // /////////////////////////////////////////////////////////////////// + // onTouch implementation + // /////////////////////////////////////////////////////////////////// + + override fun onTouch(v: View, event: MotionEvent): Boolean { + return if (playerImpl.popupPlayerSelected()) { + onTouchInPopup(v, event) + } else { + onTouchInMain(v, event) + } + } + + private fun onTouchInMain(v: View, event: MotionEvent): Boolean { + playerImpl.gestureDetector.onTouchEvent(event) + if (event.action == MotionEvent.ACTION_UP && isMovingInMain) { + isMovingInMain = false + onScrollEnd(MainPlayer.PlayerType.VIDEO, event) + } + return when (event.action) { + MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> { + v.parent.requestDisallowInterceptTouchEvent(playerImpl.isFullscreen) + true + } + MotionEvent.ACTION_UP -> { + v.parent.requestDisallowInterceptTouchEvent(false) + false + } + else -> true + } + } + + private fun onTouchInPopup(v: View, event: MotionEvent): Boolean { + playerImpl.gestureDetector.onTouchEvent(event) + if (event.pointerCount == 2 && !isMovingInPopup && !isResizing) { + if (DEBUG) { + Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing.") + } + onPopupResizingStart() + + // record coordinates of fingers + initFirstPointerX = event.getX(0) + initFirstPointerY = event.getY(0) + initSecPointerX = event.getX(1) + initSecPointerY = event.getY(1) + // record distance between fingers + initPointerDistance = Math.hypot(initFirstPointerX - initSecPointerX.toDouble(), + initFirstPointerY - initSecPointerY.toDouble()) + + isResizing = true + } + if (event.action == MotionEvent.ACTION_MOVE && !isMovingInPopup && isResizing) { + if (DEBUG) { + Log.d(TAG, "onTouch() ACTION_MOVE > v = [$v], e1.getRaw = [${event.rawX}" + + ", ${event.rawY}]") + } + return handleMultiDrag(event) + } + if (event.action == MotionEvent.ACTION_UP) { + if (DEBUG) { + Log.d(TAG, "onTouch() ACTION_UP > v = [$v], e1.getRaw = [${event.rawX}" + + ", ${event.rawY}]") + } + if (isMovingInPopup) { + isMovingInPopup = false + onScrollEnd(MainPlayer.PlayerType.POPUP, event) + } + if (isResizing) { + isResizing = false + + initPointerDistance = (-1).toDouble() + initFirstPointerX = (-1).toFloat() + initFirstPointerY = (-1).toFloat() + initSecPointerX = (-1).toFloat() + initSecPointerY = (-1).toFloat() + + onPopupResizingEnd() + playerImpl.changeState(playerImpl.currentState) + } + if (!playerImpl.isPopupClosing) { + playerImpl.savePositionAndSize() + } + } + + v.performClick() + return true + } + + private fun handleMultiDrag(event: MotionEvent): Boolean { + if (initPointerDistance != -1.0 && event.pointerCount == 2) { + // get the movements of the fingers + val firstPointerMove = hypot(event.getX(0) - initFirstPointerX.toDouble(), + event.getY(0) - initFirstPointerY.toDouble()) + val secPointerMove = hypot(event.getX(1) - initSecPointerX.toDouble(), + event.getY(1) - initSecPointerY.toDouble()) + + // minimum threshold beyond which pinch gesture will work + val minimumMove = ViewConfiguration.get(service).scaledTouchSlop + + if (max(firstPointerMove, secPointerMove) > minimumMove) { + // calculate current distance between the pointers + val currentPointerDistance = hypot(event.getX(0) - event.getX(1).toDouble(), + event.getY(0) - event.getY(1).toDouble()) + + val popupWidth = playerImpl.popupWidth.toDouble() + // change co-ordinates of popup so the center stays at the same position + val newWidth = popupWidth * currentPointerDistance / initPointerDistance + initPointerDistance = currentPointerDistance + playerImpl.popupLayoutParams.x += ((popupWidth - newWidth) / 2.0).toInt() + + playerImpl.checkPopupPositionBounds() + playerImpl.updateScreenSize() + + playerImpl.updatePopupSize( + Math.min(playerImpl.screenWidth.toDouble(), newWidth).toInt(), + -1) + return true + } + } + return false + } + + // /////////////////////////////////////////////////////////////////// + // Simple gestures + // /////////////////////////////////////////////////////////////////// + + override fun onDown(e: MotionEvent): Boolean { + if (DEBUG) + Log.d(TAG, "onDown called with e = [$e]") + + if (isDoubleTapping && isDoubleTapEnabled) { + doubleTapControls?.onDoubleTapProgressDown(getDisplayPortion(e)) + return true + } + + return if (playerImpl.popupPlayerSelected()) + onDownInPopup(e) + else + true + } + + private fun onDownInPopup(e: MotionEvent): Boolean { + // Fix popup position when the user touch it, it may have the wrong one + // because the soft input is visible (the draggable area is currently resized). + playerImpl.updateScreenSize() + playerImpl.checkPopupPositionBounds() + initialPopupX = playerImpl.popupLayoutParams.x + initialPopupY = playerImpl.popupLayoutParams.y + playerImpl.popupWidth = playerImpl.popupLayoutParams.width.toFloat() + playerImpl.popupHeight = playerImpl.popupLayoutParams.height.toFloat() + return super.onDown(e) + } + + override fun onDoubleTap(e: MotionEvent): Boolean { + if (DEBUG) + Log.d(TAG, "onDoubleTap called with e = [$e]") + + onDoubleTap(e, getDisplayPortion(e)) + return true + } + + override fun onSingleTapConfirmed(e: MotionEvent): Boolean { + if (DEBUG) + Log.d(TAG, "onSingleTapConfirmed() called with: e = [$e]") + + if (isDoubleTapping) + return true + + if (playerImpl.popupPlayerSelected()) { + if (playerImpl.player == null) + return false + + onSingleTap(MainPlayer.PlayerType.POPUP) + return true + } else { + super.onSingleTapConfirmed(e) + if (playerImpl.currentState == BasePlayer.STATE_BLOCKED) + return true + + onSingleTap(MainPlayer.PlayerType.VIDEO) + } + return true + } + + override fun onLongPress(e: MotionEvent?) { + if (playerImpl.popupPlayerSelected()) { + playerImpl.updateScreenSize() + playerImpl.checkPopupPositionBounds() + playerImpl.updatePopupSize(playerImpl.screenWidth.toInt(), -1) + } + } + + override fun onScroll( + initialEvent: MotionEvent, + movingEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + return if (playerImpl.popupPlayerSelected()) { + onScrollInPopup(initialEvent, movingEvent, distanceX, distanceY) + } else { + onScrollInMain(initialEvent, movingEvent, distanceX, distanceY) + } + } + + override fun onFling( + e1: MotionEvent?, + e2: MotionEvent?, + velocityX: Float, + velocityY: Float + ): Boolean { + return if (playerImpl.popupPlayerSelected()) { + val absVelocityX = abs(velocityX) + val absVelocityY = abs(velocityY) + if (absVelocityX.coerceAtLeast(absVelocityY) > tossFlingVelocity) { + if (absVelocityX > tossFlingVelocity) { + playerImpl.popupLayoutParams.x = velocityX.toInt() + } + if (absVelocityY > tossFlingVelocity) { + playerImpl.popupLayoutParams.y = velocityY.toInt() + } + playerImpl.checkPopupPositionBounds() + playerImpl.windowManager + .updateViewLayout(playerImpl.rootView, playerImpl.popupLayoutParams) + return true + } + return false + } else { + true + } + } + + private fun onScrollInMain( + initialEvent: MotionEvent, + movingEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + + if (!playerImpl.isFullscreen) { + return false + } + + val isTouchingStatusBar: Boolean = initialEvent.y < getStatusBarHeight(service) + val isTouchingNavigationBar: Boolean = (initialEvent.y + > playerImpl.rootView.height - getNavigationBarHeight(service)) + if (isTouchingStatusBar || isTouchingNavigationBar) { + return false + } + + val insideThreshold = abs(movingEvent.y - initialEvent.y) <= MOVEMENT_THRESHOLD + if (!isMovingInMain && (insideThreshold || abs(distanceX) > abs(distanceY)) || + playerImpl.currentState == BasePlayer.STATE_COMPLETED) { + return false + } + + isMovingInMain = true + + onScroll(MainPlayer.PlayerType.VIDEO, getDisplayHalfPortion(initialEvent), + initialEvent, movingEvent, distanceX, distanceY) + + return true + } + + private fun onScrollInPopup( + initialEvent: MotionEvent, + movingEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + + if (isResizing) { + return super.onScroll(initialEvent, movingEvent, distanceX, distanceY) + } + + if (!isMovingInPopup) { + AnimationUtils.animateView(playerImpl.closeOverlayButton, true, 200) + } + + isMovingInPopup = true + + val diffX: Float = (movingEvent.rawX - initialEvent.rawX) + var posX: Float = (initialPopupX + diffX) + val diffY: Float = (movingEvent.rawY - initialEvent.rawY) + var posY: Float = (initialPopupY + diffY) + + if (posX > playerImpl.screenWidth - playerImpl.popupWidth) { + posX = (playerImpl.screenWidth - playerImpl.popupWidth) + } else if (posX < 0) { + posX = 0f + } + + if (posY > playerImpl.screenHeight - playerImpl.popupHeight) { + posY = (playerImpl.screenHeight - playerImpl.popupHeight) + } else if (posY < 0) { + posY = 0f + } + + playerImpl.popupLayoutParams.x = posX.toInt() + playerImpl.popupLayoutParams.y = posY.toInt() + + onScroll(MainPlayer.PlayerType.POPUP, getDisplayHalfPortion(initialEvent), + initialEvent, movingEvent, distanceX, distanceY) + + playerImpl.windowManager + .updateViewLayout(playerImpl.rootView, playerImpl.popupLayoutParams) + return true + } + + // /////////////////////////////////////////////////////////////////// + // Multi double tapping + // /////////////////////////////////////////////////////////////////// + + var doubleTapControls: DoubleTapListener? = null + private set + + val isDoubleTapEnabled: Boolean + get() = doubleTapDelay > 0 + + var isDoubleTapping = false + private set + + fun doubleTapControls(listener: DoubleTapListener) = apply { + doubleTapControls = listener + } + + private var doubleTapDelay = DOUBLE_TAP_DELAY + private val doubleTapHandler: Handler = Handler() + private val doubleTapRunnable = Runnable { + if (DEBUG) + Log.d(TAG, "doubleTapRunnable called") + + isDoubleTapping = false + doubleTapControls?.onDoubleTapFinished() + } + + fun startMultiDoubleTap(e: MotionEvent) { + if (!isDoubleTapping) { + if (DEBUG) + Log.d(TAG, "startMultiDoubleTap called with e = [$e]") + + keepInDoubleTapMode() + doubleTapControls?.onDoubleTapStarted(getDisplayPortion(e)) + } + } + + fun keepInDoubleTapMode() { + if (DEBUG) + Log.d(TAG, "keepInDoubleTapMode called") + + isDoubleTapping = true + doubleTapHandler.removeCallbacks(doubleTapRunnable) + doubleTapHandler.postDelayed(doubleTapRunnable, doubleTapDelay) + } + + fun endMultiDoubleTap() { + if (DEBUG) + Log.d(TAG, "endMultiDoubleTap called") + + isDoubleTapping = false + doubleTapHandler.removeCallbacks(doubleTapRunnable) + doubleTapControls?.onDoubleTapFinished() + } + + fun enableMultiDoubleTap(enable: Boolean) = apply { + doubleTapDelay = if (enable) DOUBLE_TAP_DELAY else 0 + } + + // /////////////////////////////////////////////////////////////////// + // Utils + // /////////////////////////////////////////////////////////////////// + + private fun getDisplayPortion(e: MotionEvent): DisplayPortion { + return if (playerImpl.playerType == MainPlayer.PlayerType.POPUP) { + when { + e.x < playerImpl.popupWidth / 3.0 -> DisplayPortion.LEFT + e.x > playerImpl.popupWidth * 2.0 / 3.0 -> DisplayPortion.RIGHT + else -> DisplayPortion.MIDDLE + } + } else /* MainPlayer.PlayerType.VIDEO */ { + when { + e.x < playerImpl.rootView.width / 3.0 -> DisplayPortion.LEFT + e.x > playerImpl.rootView.width * 2.0 / 3.0 -> DisplayPortion.RIGHT + else -> DisplayPortion.MIDDLE + } + } + } + + // Currently needed for scrolling since there is no action more the middle portion + private fun getDisplayHalfPortion(e: MotionEvent): DisplayPortion { + return if (playerImpl.playerType == MainPlayer.PlayerType.POPUP) { + when { + e.x < playerImpl.popupWidth / 2.0 -> DisplayPortion.LEFT_HALF + else -> DisplayPortion.RIGHT_HALF + } + } else /* MainPlayer.PlayerType.VIDEO */ { + when { + e.x < playerImpl.rootView.width / 2.0 -> DisplayPortion.LEFT_HALF + else -> DisplayPortion.RIGHT_HALF + } + } + } + + private fun getNavigationBarHeight(context: Context): Int { + val resId = context.resources + .getIdentifier("navigation_bar_height", "dimen", "android") + return if (resId > 0) { + context.resources.getDimensionPixelSize(resId) + } else 0 + } + + private fun getStatusBarHeight(context: Context): Int { + val resId = context.resources + .getIdentifier("status_bar_height", "dimen", "android") + return if (resId > 0) { + context.resources.getDimensionPixelSize(resId) + } else 0 + } + + companion object { + private const val TAG = "BasePlayerGestListener" + private val DEBUG = BasePlayer.DEBUG + + private const val DOUBLE_TAP_DELAY = 550L + private const val MOVEMENT_THRESHOLD = 40 + } +} diff --git a/app/src/main/java/org/schabi/newpipe/player/event/DisplayPortion.kt b/app/src/main/java/org/schabi/newpipe/player/event/DisplayPortion.kt new file mode 100644 index 000000000..f15e42897 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/event/DisplayPortion.kt @@ -0,0 +1,5 @@ +package org.schabi.newpipe.player.event + +enum class DisplayPortion { + LEFT, MIDDLE, RIGHT, LEFT_HALF, RIGHT_HALF +} diff --git a/app/src/main/java/org/schabi/newpipe/player/event/DoubleTapListener.kt b/app/src/main/java/org/schabi/newpipe/player/event/DoubleTapListener.kt new file mode 100644 index 000000000..84cfb9b8d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/event/DoubleTapListener.kt @@ -0,0 +1,7 @@ +package org.schabi.newpipe.player.event + +interface DoubleTapListener { + fun onDoubleTapStarted(portion: DisplayPortion) {} + fun onDoubleTapProgressDown(portion: DisplayPortion) {} + fun onDoubleTapFinished() {} +} diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java index a2def2a64..fdea20775 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java @@ -1,16 +1,16 @@ package org.schabi.newpipe.player.event; import android.app.Activity; -import android.content.Context; import android.util.Log; -import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.Window; import android.view.WindowManager; import android.widget.ProgressBar; + import androidx.appcompat.content.res.AppCompatResources; + +import org.jetbrains.annotations.NotNull; import org.schabi.newpipe.R; import org.schabi.newpipe.player.BasePlayer; import org.schabi.newpipe.player.MainPlayer; @@ -23,217 +23,116 @@ import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME; import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA; import static org.schabi.newpipe.util.AnimationUtils.animateView; +/** + * GestureListener for the player + * + * While {@link BasePlayerGestureListener} contains the logic behind the single gestures + * this class focuses on the visual aspect like hiding and showing the controls or changing + * volume/brightness during scrolling for specific events. + */ public class PlayerGestureListener - extends GestureDetector.SimpleOnGestureListener + extends BasePlayerGestureListener implements View.OnTouchListener { private static final String TAG = ".PlayerGestureListener"; private static final boolean DEBUG = BasePlayer.DEBUG; - private final VideoPlayerImpl playerImpl; - private final MainPlayer service; - - private int initialPopupX; - private int initialPopupY; - - private boolean isMovingInMain; - private boolean isMovingInPopup; - - private boolean isResizing; - - private final int tossFlingVelocity; - private final boolean isVolumeGestureEnabled; private final boolean isBrightnessGestureEnabled; private final int maxVolume; - private static final int MOVEMENT_THRESHOLD = 40; - - // [popup] initial coordinates and distance between fingers - private double initPointerDistance = -1; - private float initFirstPointerX = -1; - private float initFirstPointerY = -1; - private float initSecPointerX = -1; - private float initSecPointerY = -1; - public PlayerGestureListener(final VideoPlayerImpl playerImpl, final MainPlayer service) { - this.playerImpl = playerImpl; - this.service = service; - this.tossFlingVelocity = PlayerHelper.getTossFlingVelocity(service); + super(playerImpl, service); isVolumeGestureEnabled = PlayerHelper.isVolumeGestureEnabled(service); isBrightnessGestureEnabled = PlayerHelper.isBrightnessGestureEnabled(service); maxVolume = playerImpl.getAudioReactor().getMaxVolume(); } - /*////////////////////////////////////////////////////////////////////////// - // Helpers - //////////////////////////////////////////////////////////////////////////*/ - - /* - * Main and popup players' gesture listeners is too different. - * So it will be better to have different implementations of them - * */ @Override - public boolean onDoubleTap(final MotionEvent e) { + public void onDoubleTap(@NotNull final MotionEvent event, + @NotNull final DisplayPortion portion) { if (DEBUG) { - Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " - + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); + Log.d(TAG, "onDoubleTap called with playerType = [" + + playerImpl.getPlayerType() + "], portion = [" + + portion + "]"); + } + if (playerImpl.isSomePopupMenuVisible()) { + playerImpl.hideControls(0, 0); } - if (playerImpl.popupPlayerSelected()) { - return onDoubleTapInPopup(e); - } else { - return onDoubleTapInMain(e); - } - } - - @Override - public boolean onSingleTapConfirmed(final MotionEvent e) { - if (DEBUG) { - Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]"); - } - - if (playerImpl.popupPlayerSelected()) { - return onSingleTapConfirmedInPopup(e); - } else { - return onSingleTapConfirmedInMain(e); - } - } - - @Override - public boolean onDown(final MotionEvent e) { - if (DEBUG) { - Log.d(TAG, "onDown() called with: e = [" + e + "]"); - } - - if (playerImpl.popupPlayerSelected()) { - return onDownInPopup(e); - } else { - return true; - } - } - - @Override - public void onLongPress(final MotionEvent e) { - if (DEBUG) { - Log.d(TAG, "onLongPress() called with: e = [" + e + "]"); - } - - if (playerImpl.popupPlayerSelected()) { - onLongPressInPopup(e); - } - } - - @Override - public boolean onScroll(final MotionEvent initialEvent, final MotionEvent movingEvent, - final float distanceX, final float distanceY) { - if (playerImpl.popupPlayerSelected()) { - return onScrollInPopup(initialEvent, movingEvent, distanceX, distanceY); - } else { - return onScrollInMain(initialEvent, movingEvent, distanceX, distanceY); - } - } - - @Override - public boolean onFling(final MotionEvent e1, final MotionEvent e2, - final float velocityX, final float velocityY) { - if (DEBUG) { - Log.d(TAG, "onFling() called with velocity: dX=[" - + velocityX + "], dY=[" + velocityY + "]"); - } - - if (playerImpl.popupPlayerSelected()) { - return onFlingInPopup(e1, e2, velocityX, velocityY); - } else { - return true; - } - } - - @Override - public boolean onTouch(final View v, final MotionEvent event) { - /*if (DEBUG && false) { - Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]"); - }*/ - - if (playerImpl.popupPlayerSelected()) { - return onTouchInPopup(v, event); - } else { - return onTouchInMain(v, event); - } - } - - - /*////////////////////////////////////////////////////////////////////////// - // Main player listener - //////////////////////////////////////////////////////////////////////////*/ - - private boolean onDoubleTapInMain(final MotionEvent e) { - if (e.getX() > playerImpl.getRootView().getWidth() * 2.0 / 3.0) { - playerImpl.onFastForward(); - } else if (e.getX() < playerImpl.getRootView().getWidth() / 3.0) { + if (portion == DisplayPortion.LEFT) { playerImpl.onFastRewind(); - } else { - playerImpl.getPlayPauseButton().performClick(); + } else if (portion == DisplayPortion.MIDDLE) { + playerImpl.onPlayPause(); + } else if (portion == DisplayPortion.RIGHT) { + playerImpl.onFastForward(); } - - return true; } - - private boolean onSingleTapConfirmedInMain(final MotionEvent e) { + @Override + public void onSingleTap(@NotNull final MainPlayer.PlayerType playerType) { if (DEBUG) { - Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]"); + Log.d(TAG, "onSingleTap called with playerType = [" + + playerImpl.getPlayerType() + "]"); } + if (playerType == MainPlayer.PlayerType.POPUP) { - if (playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED) { - return true; - } - - if (playerImpl.isControlsVisible()) { - playerImpl.hideControls(150, 0); - } else { - if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { - playerImpl.showControls(0); + if (playerImpl.isControlsVisible()) { + playerImpl.hideControls(100, 100); } else { + playerImpl.getPlayPauseButton().requestFocus(); playerImpl.showControlsThenHide(); } + + } else /* playerType == MainPlayer.PlayerType.VIDEO */ { + + if (playerImpl.isControlsVisible()) { + playerImpl.hideControls(150, 0); + } else { + if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + playerImpl.showControls(0); + } else { + playerImpl.showControlsThenHide(); + } + } } - return true; } - private boolean onScrollInMain(final MotionEvent initialEvent, final MotionEvent movingEvent, - final float distanceX, final float distanceY) { - if ((!isVolumeGestureEnabled && !isBrightnessGestureEnabled) - || !playerImpl.isFullscreen()) { - return false; + @Override + public void onScroll(@NotNull final MainPlayer.PlayerType playerType, + @NotNull final DisplayPortion portion, + @NotNull final MotionEvent initialEvent, + @NotNull final MotionEvent movingEvent, + final float distanceX, final float distanceY) { + if (DEBUG) { + Log.d(TAG, "onScroll called with playerType = [" + + playerImpl.getPlayerType() + "], portion = [" + + portion + "]"); } + if (playerType == MainPlayer.PlayerType.VIDEO) { + if (portion == DisplayPortion.LEFT_HALF) { + onScrollMainBrightness(distanceX, distanceY); - final boolean isTouchingStatusBar = initialEvent.getY() < getStatusBarHeight(service); - final boolean isTouchingNavigationBar = initialEvent.getY() - > playerImpl.getRootView().getHeight() - getNavigationBarHeight(service); - if (isTouchingStatusBar || isTouchingNavigationBar) { - return false; + } else /* DisplayPortion.RIGHT_HALF */ { + onScrollMainVolume(distanceX, distanceY); + } + + } else /* MainPlayer.PlayerType.POPUP */ { + final View closingOverlayView = playerImpl.getClosingOverlayView(); + if (playerImpl.isInsideClosingRadius(movingEvent)) { + if (closingOverlayView.getVisibility() == View.GONE) { + animateView(closingOverlayView, true, 250); + } + } else { + if (closingOverlayView.getVisibility() == View.VISIBLE) { + animateView(closingOverlayView, false, 0); + } + } } + } - /*if (DEBUG && false) Log.d(TAG, "onScrollInMain = " + - ", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" + - ", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" + - ", distanceXy = [" + distanceX + ", " + distanceY + "]");*/ - - final boolean insideThreshold = - Math.abs(movingEvent.getY() - initialEvent.getY()) <= MOVEMENT_THRESHOLD; - if (!isMovingInMain && (insideThreshold || Math.abs(distanceX) > Math.abs(distanceY)) - || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { - return false; - } - - isMovingInMain = true; - - final boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled; - final boolean acceptVolumeArea = acceptAnyArea - || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0; - - if (isVolumeGestureEnabled && acceptVolumeArea) { + private void onScrollMainVolume(final float distanceX, final float distanceY) { + if (isVolumeGestureEnabled) { playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY); final float currentProgressPercent = (float) playerImpl .getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength(); @@ -258,10 +157,14 @@ public class PlayerGestureListener if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE); } - } else { + } + } + + private void onScrollMainBrightness(final float distanceX, final float distanceY) { + if (isBrightnessGestureEnabled) { final Activity parent = playerImpl.getParentActivity(); if (parent == null) { - return true; + return; } final Window window = parent.getWindow(); @@ -299,330 +202,71 @@ public class PlayerGestureListener playerImpl.getVolumeRelativeLayout().setVisibility(View.GONE); } } - return true; } - private void onScrollEndInMain() { + @Override + public void onScrollEnd(@NotNull final MainPlayer.PlayerType playerType, + @NotNull final MotionEvent event) { if (DEBUG) { - Log.d(TAG, "onScrollEnd() called"); + Log.d(TAG, "onScrollEnd called with playerType = [" + + playerImpl.getPlayerType() + "]"); } + if (playerType == MainPlayer.PlayerType.VIDEO) { + if (DEBUG) { + Log.d(TAG, "onScrollEnd() called"); + } - if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) { - animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200); - } - if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { - animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200); - } + if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) { + animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, + false, 200, 200); + } + if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { + animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, + false, 200, 200); + } - if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { - playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); + if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { + playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); + } + } else { + if (playerImpl == null) { + return; + } + if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { + playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); + } + + if (playerImpl.isInsideClosingRadius(event)) { + playerImpl.closePopup(); + } else { + animateView(playerImpl.getClosingOverlayView(), false, 0); + + if (!playerImpl.isPopupClosing) { + animateView(playerImpl.getCloseOverlayButton(), false, 200); + } + } } } - private boolean onTouchInMain(final View v, final MotionEvent event) { - playerImpl.getGestureDetector().onTouchEvent(event); - if (event.getAction() == MotionEvent.ACTION_UP && isMovingInMain) { - isMovingInMain = false; - onScrollEndInMain(); - } - // This hack allows to stop receiving touch events on appbar - // while touching video player's view - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - v.getParent().requestDisallowInterceptTouchEvent(playerImpl.isFullscreen()); - return true; - case MotionEvent.ACTION_UP: - v.getParent().requestDisallowInterceptTouchEvent(false); - return false; - default: - return true; - } - } - - /*////////////////////////////////////////////////////////////////////////// - // Popup player listener - //////////////////////////////////////////////////////////////////////////*/ - - private boolean onDoubleTapInPopup(final MotionEvent e) { - if (playerImpl == null || !playerImpl.isPlaying()) { - return false; + @Override + public void onPopupResizingStart() { + if (DEBUG) { + Log.d(TAG, "onPopupResizingStart called"); } + playerImpl.showAndAnimateControl(-1, true); + playerImpl.getLoadingPanel().setVisibility(View.GONE); playerImpl.hideControls(0, 0); - - if (e.getX() > playerImpl.getPopupWidth() / 2) { - playerImpl.onFastForward(); - } else { - playerImpl.onFastRewind(); - } - - return true; + animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); + animateView(playerImpl.getResizingIndicator(), true, 200, 0); } - private boolean onSingleTapConfirmedInPopup(final MotionEvent e) { - if (playerImpl == null || playerImpl.getPlayer() == null) { - return false; + @Override + public void onPopupResizingEnd() { + if (DEBUG) { + Log.d(TAG, "onPopupResizingEnd called"); } - if (playerImpl.isControlsVisible()) { - playerImpl.hideControls(100, 100); - } else { - playerImpl.getPlayPauseButton().requestFocus(); - playerImpl.showControlsThenHide(); - } - return true; - } - - private boolean onDownInPopup(final MotionEvent e) { - // Fix popup position when the user touch it, it may have the wrong one - // because the soft input is visible (the draggable area is currently resized). - playerImpl.updateScreenSize(); - playerImpl.checkPopupPositionBounds(); - - initialPopupX = playerImpl.getPopupLayoutParams().x; - initialPopupY = playerImpl.getPopupLayoutParams().y; - playerImpl.setPopupWidth(playerImpl.getPopupLayoutParams().width); - playerImpl.setPopupHeight(playerImpl.getPopupLayoutParams().height); - return super.onDown(e); - } - - private void onLongPressInPopup(final MotionEvent e) { - playerImpl.updateScreenSize(); - playerImpl.checkPopupPositionBounds(); - playerImpl.updatePopupSize((int) playerImpl.getScreenWidth(), -1); - } - - private boolean onScrollInPopup(final MotionEvent initialEvent, - final MotionEvent movingEvent, - final float distanceX, - final float distanceY) { - if (isResizing || playerImpl == null) { - return super.onScroll(initialEvent, movingEvent, distanceX, distanceY); - } - - if (!isMovingInPopup) { - animateView(playerImpl.getCloseOverlayButton(), true, 200); - } - - isMovingInPopup = true; - - final float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX()); - float posX = (int) (initialPopupX + diffX); - final float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY()); - float posY = (int) (initialPopupY + diffY); - - if (posX > (playerImpl.getScreenWidth() - playerImpl.getPopupWidth())) { - posX = (int) (playerImpl.getScreenWidth() - playerImpl.getPopupWidth()); - } else if (posX < 0) { - posX = 0; - } - - if (posY > (playerImpl.getScreenHeight() - playerImpl.getPopupHeight())) { - posY = (int) (playerImpl.getScreenHeight() - playerImpl.getPopupHeight()); - } else if (posY < 0) { - posY = 0; - } - - playerImpl.getPopupLayoutParams().x = (int) posX; - playerImpl.getPopupLayoutParams().y = (int) posY; - - final View closingOverlayView = playerImpl.getClosingOverlayView(); - if (playerImpl.isInsideClosingRadius(movingEvent)) { - if (closingOverlayView.getVisibility() == View.GONE) { - animateView(closingOverlayView, true, 250); - } - } else { - if (closingOverlayView.getVisibility() == View.VISIBLE) { - animateView(closingOverlayView, false, 0); - } - } - -// if (DEBUG) { -// Log.d(TAG, "onScrollInPopup = " -// + "e1.getRaw = [" + initialEvent.getRawX() + ", " -// + initialEvent.getRawY() + "], " -// + "e1.getX,Y = [" + initialEvent.getX() + ", " -// + initialEvent.getY() + "], " -// + "e2.getRaw = [" + movingEvent.getRawX() + ", " -// + movingEvent.getRawY() + "], " -// + "e2.getX,Y = [" + movingEvent.getX() + ", " + movingEvent.getY() + "], " -// + "distanceX,Y = [" + distanceX + ", " + distanceY + "], " -// + "posX,Y = [" + posX + ", " + posY + "], " -// + "popupW,H = [" + popupWidth + " x " + popupHeight + "]"); -// } - playerImpl.windowManager - .updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams()); - return true; - } - - private void onScrollEndInPopup(final MotionEvent event) { - if (playerImpl == null) { - return; - } - if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { - playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); - } - - if (playerImpl.isInsideClosingRadius(event)) { - playerImpl.closePopup(); - } else { - animateView(playerImpl.getClosingOverlayView(), false, 0); - - if (!playerImpl.isPopupClosing) { - animateView(playerImpl.getCloseOverlayButton(), false, 200); - } - } - } - - private boolean onFlingInPopup(final MotionEvent e1, - final MotionEvent e2, - final float velocityX, - final float velocityY) { - if (playerImpl == null) { - return false; - } - - final float absVelocityX = Math.abs(velocityX); - final float absVelocityY = Math.abs(velocityY); - if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) { - if (absVelocityX > tossFlingVelocity) { - playerImpl.getPopupLayoutParams().x = (int) velocityX; - } - if (absVelocityY > tossFlingVelocity) { - playerImpl.getPopupLayoutParams().y = (int) velocityY; - } - playerImpl.checkPopupPositionBounds(); - playerImpl.windowManager - .updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams()); - return true; - } - return false; - } - - private boolean onTouchInPopup(final View v, final MotionEvent event) { - if (playerImpl == null) { - return false; - } - playerImpl.getGestureDetector().onTouchEvent(event); - - if (event.getPointerCount() == 2 && !isMovingInPopup && !isResizing) { - if (DEBUG) { - Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing."); - } - playerImpl.showAndAnimateControl(-1, true); - playerImpl.getLoadingPanel().setVisibility(View.GONE); - - playerImpl.hideControls(0, 0); - animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); - animateView(playerImpl.getResizingIndicator(), true, 200, 0); - //record coordinates of fingers - initFirstPointerX = event.getX(0); - initFirstPointerY = event.getY(0); - initSecPointerX = event.getX(1); - initSecPointerY = event.getY(1); - //record distance between fingers - initPointerDistance = Math.hypot(initFirstPointerX - initSecPointerX, - initFirstPointerY - initSecPointerY); - - isResizing = true; - } - - if (event.getAction() == MotionEvent.ACTION_MOVE && !isMovingInPopup && isResizing) { - if (DEBUG) { - Log.d(TAG, "onTouch() ACTION_MOVE > v = [" + v + "], " - + "e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); - } - return handleMultiDrag(event); - } - - if (event.getAction() == MotionEvent.ACTION_UP) { - if (DEBUG) { - Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], " - + "e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); - } - if (isMovingInPopup) { - isMovingInPopup = false; - onScrollEndInPopup(event); - } - - if (isResizing) { - isResizing = false; - - initPointerDistance = -1; - initFirstPointerX = -1; - initFirstPointerY = -1; - initSecPointerX = -1; - initSecPointerY = -1; - - animateView(playerImpl.getResizingIndicator(), false, 100, 0); - playerImpl.changeState(playerImpl.getCurrentState()); - } - - if (!playerImpl.isPopupClosing) { - playerImpl.savePositionAndSize(); - } - } - - v.performClick(); - return true; - } - - private boolean handleMultiDrag(final MotionEvent event) { - if (initPointerDistance != -1 && event.getPointerCount() == 2) { - // get the movements of the fingers - final double firstPointerMove = Math.hypot(event.getX(0) - initFirstPointerX, - event.getY(0) - initFirstPointerY); - final double secPointerMove = Math.hypot(event.getX(1) - initSecPointerX, - event.getY(1) - initSecPointerY); - - // minimum threshold beyond which pinch gesture will work - final int minimumMove = ViewConfiguration.get(service).getScaledTouchSlop(); - - if (Math.max(firstPointerMove, secPointerMove) > minimumMove) { - // calculate current distance between the pointers - final double currentPointerDistance = - Math.hypot(event.getX(0) - event.getX(1), - event.getY(0) - event.getY(1)); - - final double popupWidth = playerImpl.getPopupWidth(); - // change co-ordinates of popup so the center stays at the same position - final double newWidth = (popupWidth * currentPointerDistance / initPointerDistance); - initPointerDistance = currentPointerDistance; - playerImpl.getPopupLayoutParams().x += (popupWidth - newWidth) / 2; - - playerImpl.checkPopupPositionBounds(); - playerImpl.updateScreenSize(); - - playerImpl.updatePopupSize( - (int) Math.min(playerImpl.getScreenWidth(), newWidth), - -1); - return true; - } - } - return false; - } - - - /* - * Utils - * */ - - private int getNavigationBarHeight(final Context context) { - final int resId = context.getResources() - .getIdentifier("navigation_bar_height", "dimen", "android"); - if (resId > 0) { - return context.getResources().getDimensionPixelSize(resId); - } - return 0; - } - - private int getStatusBarHeight(final Context context) { - final int resId = context.getResources() - .getIdentifier("status_bar_height", "dimen", "android"); - if (resId > 0) { - return context.getResources().getDimensionPixelSize(resId); - } - return 0; + animateView(playerImpl.getResizingIndicator(), false, 100, 0); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java index a931c46bd..ffe19599d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java @@ -164,7 +164,7 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An @Override public void onAudioSessionId(final EventTime eventTime, final int audioSessionId) { - if (!PlayerHelper.isUsingDSP(context)) { + if (!PlayerHelper.isUsingDSP()) { return; } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index 1d1d056a8..d89b5dd19 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -84,12 +84,12 @@ public final class PlayerHelper { final int days = (milliSeconds % (86400000 * 7)) / 86400000; STRING_BUILDER.setLength(0); - return days > 0 + return (days > 0 ? STRING_FORMATTER.format("%d:%02d:%02d:%02d", days, hours, minutes, seconds) - .toString() : hours > 0 - ? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds).toString() - : STRING_FORMATTER.format("%02d:%02d", minutes, seconds).toString(); + ? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds) + : STRING_FORMATTER.format("%02d:%02d", minutes, seconds) + ).toString(); } public static String formatSpeed(final double speed) { @@ -210,6 +210,10 @@ public final class PlayerHelper { return isBrightnessGestureEnabled(context, true); } + public static boolean isRememberingPopupDimensions(@NonNull final Context context) { + return isRememberingPopupDimensions(context, true); + } + public static boolean isAutoQueueEnabled(@NonNull final Context context) { return isAutoQueueEnabled(context, false); } @@ -295,7 +299,7 @@ public final class PlayerHelper { return 60000; } - public static TrackSelection.Factory getQualitySelector(@NonNull final Context context) { + public static TrackSelection.Factory getQualitySelector() { return new AdaptiveTrackSelection.Factory( 1000, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, @@ -303,11 +307,11 @@ public final class PlayerHelper { AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION); } - public static boolean isUsingDSP(@NonNull final Context context) { + public static boolean isUsingDSP() { return true; } - public static int getTossFlingVelocity(@NonNull final Context context) { + public static int getTossFlingVelocity() { return 2500; } @@ -390,6 +394,12 @@ public final class PlayerHelper { .getBoolean(context.getString(R.string.brightness_gesture_control_key), b); } + private static boolean isRememberingPopupDimensions(@NonNull final Context context, + final boolean b) { + return getPreferences(context) + .getBoolean(context.getString(R.string.popup_remember_size_pos_key), b); + } + private static boolean isUsingInexactSeek(@NonNull final Context context) { return getPreferences(context) .getBoolean(context.getString(R.string.use_inexact_seek_key), false); diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java index 6d0f5fff7..854e3eb2b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java @@ -49,6 +49,17 @@ public final class PlayerHolder { return player.getPlayerType(); } + public static boolean isPlaying() { + if (player == null) { + return false; + } + return player.isPlaying(); + } + + public static boolean isPlayerOpen() { + return player != null; + } + public static void setListener(final PlayerServiceExtendedEventListener newListener) { listener = newListener; // Force reload data from service diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java index 8bef0b2e0..b8bb677e0 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java @@ -1,12 +1,8 @@ package org.schabi.newpipe.player.playqueue; -import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.player.playqueue.events.AppendEvent; import org.schabi.newpipe.player.playqueue.events.ErrorEvent; @@ -43,7 +39,6 @@ import io.reactivex.subjects.BehaviorSubject; *

*/ public abstract class PlayQueue implements Serializable { - private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); public static final boolean DEBUG = MainActivity.DEBUG; private ArrayList backup; @@ -55,7 +50,6 @@ public abstract class PlayQueue implements Serializable { private transient BehaviorSubject eventBroadcast; private transient Flowable broadcastReceiver; - private transient Subscription reportingReactor; private transient boolean disposed; @@ -87,10 +81,6 @@ public abstract class PlayQueue implements Serializable { broadcastReceiver = eventBroadcast.toFlowable(BackpressureStrategy.BUFFER) .observeOn(AndroidSchedulers.mainThread()) .startWith(new InitEvent()); - - if (DEBUG) { - broadcastReceiver.subscribe(getSelfReporter()); - } } /** @@ -100,13 +90,9 @@ public abstract class PlayQueue implements Serializable { if (eventBroadcast != null) { eventBroadcast.onComplete(); } - if (reportingReactor != null) { - reportingReactor.cancel(); - } eventBroadcast = null; broadcastReceiver = null; - reportingReactor = null; disposed = true; } @@ -167,19 +153,20 @@ public abstract class PlayQueue implements Serializable { } /** - * @return the current item that should be played + * @return the current item that should be played, or null if the queue is empty */ + @Nullable public PlayQueueItem getItem() { return getItem(getIndex()); } /** * @param index the index of the item to return - * @return the item at the given index - * @throws IndexOutOfBoundsException + * @return the item at the given index, or null if the index is out of bounds */ + @Nullable public PlayQueueItem getItem(final int index) { - if (index < 0 || index >= streams.size() || streams.get(index) == null) { + if (index < 0 || index >= streams.size()) { return null; } return streams.get(index); @@ -543,35 +530,5 @@ public abstract class PlayQueue implements Serializable { eventBroadcast.onNext(event); } } - - private Subscriber getSelfReporter() { - return new Subscriber() { - @Override - public void onSubscribe(final Subscription s) { - if (reportingReactor != null) { - reportingReactor.cancel(); - } - reportingReactor = s; - reportingReactor.request(1); - } - - @Override - public void onNext(final PlayQueueEvent event) { - Log.d(TAG, "Received broadcast: " + event.type().name() + ". " - + "Current index: " + getIndex() + ", play queue length: " + size() + "."); - reportingReactor.request(1); - } - - @Override - public void onError(final Throwable t) { - Log.e(TAG, "Received broadcast error", t); - } - - @Override - public void onComplete() { - Log.d(TAG, "Broadcast is shutting down."); - } - }; - } } diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java index 74aef07fa..79efc03ae 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java @@ -64,6 +64,20 @@ public class PlayQueueItem implements Serializable { this.recoveryPosition = RECOVERY_UNSET; } + @Override + public boolean equals(final Object o) { + if (o instanceof PlayQueueItem) { + return url.equals(((PlayQueueItem) o).url); + } else { + return false; + } + } + + @Override + public int hashCode() { + return url.hashCode(); + } + @NonNull public String getTitle() { return title; diff --git a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java index 6d6f0fa75..2f65af4d6 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java @@ -4,7 +4,8 @@ import android.os.Bundle; import androidx.preference.Preference; -import org.schabi.newpipe.CheckForNewAppVersionTask; +import org.schabi.newpipe.App; +import org.schabi.newpipe.CheckForNewAppVersion; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; @@ -15,7 +16,7 @@ public class MainSettingsFragment extends BasePreferenceFragment { public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { addPreferencesFromResource(R.xml.main_settings); - if (!CheckForNewAppVersionTask.isGithubApk()) { + if (!CheckForNewAppVersion.isGithubApk(App.getApp())) { final Preference update = findPreference(getString(R.string.update_pref_screen_key)); getPreferenceScreen().removePreference(update); diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt new file mode 100644 index 000000000..c68b699d3 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt @@ -0,0 +1,19 @@ +package org.schabi.newpipe.settings + +import android.os.Build +import android.os.Bundle +import androidx.preference.Preference +import org.schabi.newpipe.R + +class NotificationSettingsFragment : BasePreferenceFragment() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.notification_settings) + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + val colorizePref: Preference? = findPreference(getString(R.string.notification_colorize_key)) + colorizePref?.let { + preferenceScreen.removePreference(it) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java index c858c7f77..153adf4c0 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.settings; import android.app.Activity; -import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -34,6 +33,7 @@ import java.util.List; import java.util.Vector; import io.reactivex.Flowable; +import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; public class SelectPlaylistFragment extends DialogFragment { @@ -46,12 +46,11 @@ public class SelectPlaylistFragment extends DialogFragment { private final ImageLoader imageLoader = ImageLoader.getInstance(); private OnSelectedListener onSelectedListener = null; - private OnCancelListener onCancelListener = null; private ProgressBar progressBar; private TextView emptyView; private RecyclerView recyclerView; - private Disposable playlistsSubscriber; + private Disposable disposable = null; private List playlists = new Vector<>(); @@ -59,10 +58,6 @@ public class SelectPlaylistFragment extends DialogFragment { onSelectedListener = listener; } - public void setOnCancelListener(final OnCancelListener listener) { - onCancelListener = listener; - } - /*////////////////////////////////////////////////////////////////////////// // Fragment's Lifecycle //////////////////////////////////////////////////////////////////////////*/ @@ -70,15 +65,32 @@ public class SelectPlaylistFragment extends DialogFragment { @Override public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - final View v = - inflater.inflate(R.layout.select_playlist_fragment, container, false); + final View v = inflater.inflate(R.layout.select_playlist_fragment, container, false); + progressBar = v.findViewById(R.id.progressBar); recyclerView = v.findViewById(R.id.items_list); + emptyView = v.findViewById(R.id.empty_state_view); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); final SelectPlaylistAdapter playlistAdapter = new SelectPlaylistAdapter(); recyclerView.setAdapter(playlistAdapter); - progressBar = v.findViewById(R.id.progressBar); - emptyView = v.findViewById(R.id.empty_state_view); + loadPlaylists(); + return v; + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (disposable != null) { + disposable.dispose(); + } + } + + /*////////////////////////////////////////////////////////////////////////// + // Load and display playlists + //////////////////////////////////////////////////////////////////////////*/ + + private void loadPlaylists() { progressBar.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); emptyView.setVisibility(View.GONE); @@ -87,43 +99,36 @@ public class SelectPlaylistFragment extends DialogFragment { final LocalPlaylistManager localPlaylistManager = new LocalPlaylistManager(database); final RemotePlaylistManager remotePlaylistManager = new RemotePlaylistManager(database); - playlistsSubscriber = Flowable.combineLatest(localPlaylistManager.getPlaylists(), + disposable = Flowable.combineLatest(localPlaylistManager.getPlaylists(), remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge) + .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::displayPlaylists, this::onError); - - return v; } - @Override - public void onDestroy() { - super.onDestroy(); + private void displayPlaylists(final List newPlaylists) { + playlists = newPlaylists; + progressBar.setVisibility(View.GONE); + emptyView.setVisibility(newPlaylists.isEmpty() ? View.VISIBLE : View.GONE); + recyclerView.setVisibility(newPlaylists.isEmpty() ? View.GONE : View.VISIBLE); + } - if (playlistsSubscriber != null) { - playlistsSubscriber.dispose(); - playlistsSubscriber = null; - } + protected void onError(final Throwable e) { + final Activity activity = requireActivity(); + ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo + .make(UserAction.UI_ERROR, "none", "load_playlists", R.string.app_ui_crash)); } /*////////////////////////////////////////////////////////////////////////// // Handle actions //////////////////////////////////////////////////////////////////////////*/ - @Override - public void onCancel(final DialogInterface dialogInterface) { - super.onCancel(dialogInterface); - if (onCancelListener != null) { - onCancelListener.onCancel(); - } - } - private void clickedItem(final int position) { if (onSelectedListener != null) { final LocalItem selectedItem = playlists.get(position); if (selectedItem instanceof PlaylistMetadataEntry) { final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); - onSelectedListener - .onLocalPlaylistSelected(entry.uid, entry.name); + onSelectedListener.onLocalPlaylistSelected(entry.uid, entry.name); } else if (selectedItem instanceof PlaylistRemoteEntity) { final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); @@ -134,31 +139,6 @@ public class SelectPlaylistFragment extends DialogFragment { dismiss(); } - /*////////////////////////////////////////////////////////////////////////// - // Item handling - //////////////////////////////////////////////////////////////////////////*/ - - private void displayPlaylists(final List newPlaylists) { - this.playlists = newPlaylists; - progressBar.setVisibility(View.GONE); - if (newPlaylists.isEmpty()) { - emptyView.setVisibility(View.VISIBLE); - return; - } - recyclerView.setVisibility(View.VISIBLE); - - } - - /*////////////////////////////////////////////////////////////////////////// - // Error - //////////////////////////////////////////////////////////////////////////*/ - - protected void onError(final Throwable e) { - final Activity activity = getActivity(); - ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo - .make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash)); - } - /*////////////////////////////////////////////////////////////////////////// // Interfaces //////////////////////////////////////////////////////////////////////////*/ @@ -168,22 +148,20 @@ public class SelectPlaylistFragment extends DialogFragment { void onRemotePlaylistSelected(int serviceId, String url, String name); } - public interface OnCancelListener { - void onCancel(); - } - private class SelectPlaylistAdapter extends RecyclerView.Adapter { + @NonNull @Override public SelectPlaylistItemHolder onCreateViewHolder(final ViewGroup parent, - final int viewType) { + final int viewType) { final View item = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_playlist_mini_item, parent, false); return new SelectPlaylistItemHolder(item); } @Override - public void onBindViewHolder(final SelectPlaylistItemHolder holder, final int position) { + public void onBindViewHolder(@NonNull final SelectPlaylistItemHolder holder, + final int position) { final PlaylistLocalItem selectedItem = playlists.get(position); if (selectedItem instanceof PlaylistMetadataEntry) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java similarity index 74% rename from app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java rename to app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java index 13e500e12..1fe405552 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java @@ -1,10 +1,10 @@ -package org.schabi.newpipe.settings; +package org.schabi.newpipe.settings.custom; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.preference.PreferenceManager; +import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -13,18 +13,16 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RadioButton; import android.widget.RadioGroup; -import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.widget.TextViewCompat; -import androidx.fragment.app.Fragment; - +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; +import java.util.List; import org.schabi.newpipe.R; import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.NotificationConstants; @@ -32,56 +30,35 @@ import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.views.FocusOverlayView; -import java.util.List; +public class NotificationActionsPreference extends Preference { + + public NotificationActionsPreference(final Context context, final AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.settings_notification); + } -public class NotificationSettingsFragment extends Fragment { - private Switch scaleSwitch; private NotificationSlot[] notificationSlots; - private SharedPreferences pref; private List compactSlots; - private String scaleKey; //////////////////////////////////////////////////////////////////////////// // Lifecycle //////////////////////////////////////////////////////////////////////////// @Override - public void onCreate(@Nullable final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - pref = PreferenceManager.getDefaultSharedPreferences(requireContext()); - scaleKey = getString(R.string.scale_to_square_image_in_notifications_key); + public void onBindViewHolder(final PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + holder.itemView.setClickable(false); + setupActions(holder.itemView); } @Override - public View onCreateView(@NonNull final LayoutInflater inflater, - final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - return inflater.inflate(R.layout.settings_notification, container, false); - } - - @Override - public void onViewCreated(@NonNull final View rootView, - @Nullable final Bundle savedInstanceState) { - super.onViewCreated(rootView, savedInstanceState); - - setupScaleSwitch(rootView); - setupActions(rootView); - } - - @Override - public void onResume() { - super.onResume(); - ThemeHelper.setTitleToAppCompatActivity(getActivity(), - getString(R.string.settings_category_notification_title)); - } - - @Override - public void onPause() { - super.onPause(); + public void onDetached() { + super.onDetached(); saveChanges(); - requireContext().sendBroadcast(new Intent(MainPlayer.ACTION_RECREATE_NOTIFICATION)); + getContext().sendBroadcast(new Intent(MainPlayer.ACTION_RECREATE_NOTIFICATION)); } @@ -89,17 +66,10 @@ public class NotificationSettingsFragment extends Fragment { // Setup //////////////////////////////////////////////////////////////////////////// - private void setupScaleSwitch(@NonNull final View view) { - scaleSwitch = view.findViewById(R.id.notificationScaleSwitch); - scaleSwitch.setChecked(pref.getBoolean(scaleKey, false)); - - view.findViewById(R.id.notificationScaleSwitchClickableArea) - .setOnClickListener(v -> scaleSwitch.toggle()); - } - private void setupActions(@NonNull final View view) { compactSlots = - NotificationConstants.getCompactSlotsFromPreferences(requireContext(), pref, 5); + NotificationConstants.getCompactSlotsFromPreferences( + getContext(), getSharedPreferences(), 5); notificationSlots = new NotificationSlot[5]; for (int i = 0; i < 5; i++) { notificationSlots[i] = new NotificationSlot(i, view); @@ -112,16 +82,15 @@ public class NotificationSettingsFragment extends Fragment { //////////////////////////////////////////////////////////////////////////// private void saveChanges() { - final SharedPreferences.Editor editor = pref.edit(); - editor.putBoolean(scaleKey, scaleSwitch.isChecked()); + final SharedPreferences.Editor editor = getSharedPreferences().edit(); for (int i = 0; i < 3; i++) { - editor.putInt(getString(NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]), + editor.putInt(getContext().getString(NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]), (i < compactSlots.size() ? compactSlots.get(i) : -1)); } for (int i = 0; i < 5; i++) { - editor.putInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]), + editor.putInt(getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), notificationSlots[i].selectedAction); } @@ -183,7 +152,7 @@ public class NotificationSettingsFragment extends Fragment { } else if (compactSlots.size() < 3) { compactSlots.add(i); } else { - Toast.makeText(requireContext(), + Toast.makeText(getContext(), R.string.notification_actions_at_most_three, Toast.LENGTH_SHORT).show(); return; @@ -196,7 +165,8 @@ public class NotificationSettingsFragment extends Fragment { void setupSelectedAction(final View view) { icon = view.findViewById(R.id.notificationActionIcon); summary = view.findViewById(R.id.notificationActionSummary); - selectedAction = pref.getInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]), + selectedAction = getSharedPreferences().getInt( + getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), NotificationConstants.SLOT_DEFAULTS[i]); updateInfo(); } @@ -205,20 +175,20 @@ public class NotificationSettingsFragment extends Fragment { if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) { icon.setImageDrawable(null); } else { - icon.setImageDrawable(AppCompatResources.getDrawable(requireContext(), + icon.setImageDrawable(AppCompatResources.getDrawable(getContext(), NotificationConstants.ACTION_ICONS[selectedAction])); } - summary.setText(NotificationConstants.getActionName(requireContext(), selectedAction)); + summary.setText(NotificationConstants.getActionName(getContext(), selectedAction)); } void openActionChooserDialog() { - final LayoutInflater inflater = LayoutInflater.from(requireContext()); + final LayoutInflater inflater = LayoutInflater.from(getContext()); final LinearLayout rootLayout = (LinearLayout) inflater.inflate( R.layout.single_choice_dialog_view, null, false); final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list); - final AlertDialog alertDialog = new AlertDialog.Builder(requireContext()) + final AlertDialog alertDialog = new AlertDialog.Builder(getContext()) .setTitle(SLOT_TITLES[i]) .setView(radioGroup) .setCancelable(true) @@ -237,10 +207,10 @@ public class NotificationSettingsFragment extends Fragment { // if present set action icon with correct color if (NotificationConstants.ACTION_ICONS[action] != 0) { - Drawable drawable = AppCompatResources.getDrawable(requireContext(), + Drawable drawable = AppCompatResources.getDrawable(getContext(), NotificationConstants.ACTION_ICONS[action]); if (drawable != null) { - final int color = ThemeHelper.resolveColorFromAttr(requireContext(), + final int color = ThemeHelper.resolveColorFromAttr(getContext(), android.R.attr.textColorPrimary); drawable = DrawableCompat.wrap(drawable).mutate(); DrawableCompat.setTint(drawable, color); @@ -249,7 +219,7 @@ public class NotificationSettingsFragment extends Fragment { } } - radioButton.setText(NotificationConstants.getActionName(requireContext(), action)); + radioButton.setText(NotificationConstants.getActionName(getContext(), action)); radioButton.setChecked(action == selectedAction); radioButton.setId(id); radioButton.setLayoutParams(new RadioGroup.LayoutParams( @@ -259,7 +229,7 @@ public class NotificationSettingsFragment extends Fragment { } alertDialog.show(); - if (DeviceUtils.isTv(requireContext())) { + if (DeviceUtils.isTv(getContext())) { FocusOverlayView.setupFocusObserver(alertDialog); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java index e26c00fb2..0c840f8c3 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -18,6 +18,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -265,10 +266,8 @@ public final class ListHelper { */ private static void sortStreamList(final List videoStreams, final boolean ascendingOrder) { - Collections.sort(videoStreams, (o1, o2) -> { - final int result = compareVideoStreamResolution(o1, o2); - return result == 0 ? 0 : (ascendingOrder ? result : -result); - }); + final Comparator comparator = ListHelper::compareVideoStreamResolution; + Collections.sort(videoStreams, ascendingOrder ? comparator : comparator.reversed()); } /** diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 700d7b63d..9cebfa863 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -23,11 +23,13 @@ import org.schabi.newpipe.extractor.localization.ContentCountry; import java.math.BigDecimal; import java.math.RoundingMode; -import java.text.DateFormat; import java.text.NumberFormat; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.Arrays; import java.util.Calendar; -import java.util.Date; import java.util.List; import java.util.Locale; @@ -139,13 +141,16 @@ public final class Localization { return nf.format(number); } - public static String formatDate(final Date date, final Context context) { - return DateFormat.getDateInstance(DateFormat.MEDIUM, getAppLocale(context)).format(date); + public static String formatDate(final OffsetDateTime offsetDateTime, final Context context) { + return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) + .withLocale(getAppLocale(context)).format(offsetDateTime + .atZoneSameInstant(ZoneId.systemDefault())); } @SuppressLint("StringFormatInvalid") - public static String localizeUploadDate(final Context context, final Date date) { - return context.getString(R.string.upload_date_text, formatDate(date, context)); + public static String localizeUploadDate(final Context context, + final OffsetDateTime offsetDateTime) { + return context.getString(R.string.upload_date_text, formatDate(offsetDateTime, context)); } public static String localizeViewCount(final Context context, final long viewCount) { @@ -186,7 +191,7 @@ public final class Localization { } public static String shortCount(final Context context, final long count) { - if (Build.VERSION.SDK_INT >= 24) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return CompactDecimalFormat.getInstance(getAppLocale(context), CompactDecimalFormat.CompactStyle.SHORT).format(count); } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 6761fce59..b45a1e7b9 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -18,7 +18,6 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import androidx.preference.PreferenceManager; import com.nostra13.universalimageloader.core.ImageLoader; @@ -38,7 +37,6 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; -import org.schabi.newpipe.fragments.list.comments.CommentsFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment; @@ -52,13 +50,14 @@ import org.schabi.newpipe.player.BackgroundPlayerActivity; import org.schabi.newpipe.player.BasePlayer; import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.VideoPlayer; +import org.schabi.newpipe.player.helper.PlayerHelper; +import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.settings.SettingsActivity; import java.util.ArrayList; -@SuppressWarnings({"unused"}) public final class NavigationHelper { public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag"; public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag"; @@ -73,7 +72,6 @@ public final class NavigationHelper { public static Intent getPlayerIntent(@NonNull final Context context, @NonNull final Class targetClazz, @Nullable final PlayQueue playQueue, - @Nullable final String quality, final boolean resumePlayback) { final Intent intent = new Intent(context, targetClazz); @@ -83,9 +81,6 @@ public final class NavigationHelper { intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey); } } - if (quality != null) { - intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality); - } intent.putExtra(VideoPlayer.RESUME_PLAYBACK, resumePlayback); intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO); @@ -96,8 +91,10 @@ public final class NavigationHelper { public static Intent getPlayerIntent(@NonNull final Context context, @NonNull final Class targetClazz, @Nullable final PlayQueue playQueue, - final boolean resumePlayback) { - return getPlayerIntent(context, targetClazz, playQueue, null, resumePlayback); + final boolean resumePlayback, + final boolean playWhenReady) { + return getPlayerIntent(context, targetClazz, playQueue, resumePlayback) + .putExtra(BasePlayer.PLAY_WHEN_READY, playWhenReady); } @NonNull @@ -111,61 +108,25 @@ public final class NavigationHelper { .putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend); } - @NonNull - public static Intent getPlayerIntent(@NonNull final Context context, - @NonNull final Class targetClazz, - @Nullable final PlayQueue playQueue, - final int repeatMode, - final float playbackSpeed, - final float playbackPitch, - final boolean playbackSkipSilence, - @Nullable final String playbackQuality, - final boolean resumePlayback, - final boolean startPaused, - final boolean isMuted) { - return getPlayerIntent(context, targetClazz, playQueue, playbackQuality, resumePlayback) - .putExtra(BasePlayer.REPEAT_MODE, repeatMode) - .putExtra(BasePlayer.START_PAUSED, startPaused) - .putExtra(BasePlayer.IS_MUTED, isMuted); - } - public static void playOnMainPlayer(final AppCompatActivity activity, - final PlayQueue queue, - final boolean autoPlay) { - playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay); + @NonNull final PlayQueue playQueue) { + final PlayQueueItem item = playQueue.getItem(); + assert item != null; + openVideoDetailFragment(activity, activity.getSupportFragmentManager(), + item.getServiceId(), item.getUrl(), item.getTitle(), playQueue, false); } - public static void playOnMainPlayer(final FragmentManager fragmentManager, - final PlayQueue queue, - final boolean autoPlay) { - final PlayQueueItem currentStream = queue.getItem(); - openVideoDetailFragment( - fragmentManager, - currentStream.getServiceId(), - currentStream.getUrl(), - currentStream.getTitle(), - autoPlay, - queue); + public static void playOnMainPlayer(final Context context, + @NonNull final PlayQueue playQueue, + final boolean switchingPlayers) { + final PlayQueueItem item = playQueue.getItem(); + assert item != null; + openVideoDetail(context, + item.getServiceId(), item.getUrl(), item.getTitle(), playQueue, switchingPlayers); } - public static void playOnMainPlayer(@NonNull final Context context, - @Nullable final PlayQueue queue, - @NonNull final StreamingService.LinkType linkType, - @NonNull final String url, - @NonNull final String title, - final boolean autoPlay, - final boolean resumePlayback) { - - final Intent intent = getPlayerIntent(context, MainActivity.class, queue, resumePlayback); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Constants.KEY_LINK_TYPE, linkType); - intent.putExtra(Constants.KEY_URL, url); - intent.putExtra(Constants.KEY_TITLE, title); - intent.putExtra(VideoDetailFragment.AUTO_PLAY, autoPlay); - context.startActivity(intent); - } - - public static void playOnPopupPlayer(final Context context, final PlayQueue queue, + public static void playOnPopupPlayer(final Context context, + final PlayQueue queue, final boolean resumePlayback) { if (!PermissionHelper.isPopupEnabled(context)) { PermissionHelper.showPopupEnablementToast(context); @@ -300,9 +261,6 @@ public final class NavigationHelper { .setNegativeButton(R.string.cancel, (dialog, which) -> Log.i("NavigationHelper", "You unlocked a secret unicorn.")) .show(); -// Log.e("NavigationHelper", -// "Either no Streaming player for audio was installed, " -// + "or something important crashed:"); } else { Toast.makeText(context, R.string.no_player_found_toast, Toast.LENGTH_LONG).show(); } @@ -358,41 +316,6 @@ public final class NavigationHelper { .commit(); } - public static void openVideoDetailFragment(final FragmentManager fragmentManager, - final int serviceId, final String url, - final String title) { - openVideoDetailFragment(fragmentManager, serviceId, url, title, true, null); - } - - public static void openVideoDetailFragment( - final FragmentManager fragmentManager, - final int serviceId, - final String url, - final String title, - final boolean autoPlay, - final PlayQueue playQueue) { - final Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder); - - if (fragment instanceof VideoDetailFragment && fragment.isVisible()) { - expandMainPlayer(fragment.requireActivity()); - final VideoDetailFragment detailFragment = (VideoDetailFragment) fragment; - detailFragment.setAutoplay(autoPlay); - detailFragment - .selectAndLoadVideo(serviceId, url, title == null ? "" : title, playQueue); - detailFragment.scrollToTop(); - return; - } - - final VideoDetailFragment instance = VideoDetailFragment - .getInstance(serviceId, url, title == null ? "" : title, playQueue); - instance.setAutoplay(autoPlay); - - defaultTransaction(fragmentManager) - .replace(R.id.fragment_player_holder, instance) - .runOnCommit(() -> expandMainPlayer(instance.requireActivity())) - .commit(); - } - public static void expandMainPlayer(final Context context) { context.sendBroadcast(new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER)); } @@ -409,33 +332,76 @@ public final class NavigationHelper { .commitAllowingStateLoss(); } - public static void openChannelFragment(final FragmentManager fragmentManager, - final int serviceId, final String url, - final String name) { - defaultTransaction(fragmentManager) - .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, - name == null ? "" : name)) - .addToBackStack(null) - .commit(); + private interface RunnableWithVideoDetailFragment { + void run(VideoDetailFragment detailFragment); } - public static void openCommentsFragment(final FragmentManager fragmentManager, - final int serviceId, final String url, - final String name) { - fragmentManager.beginTransaction() - .setCustomAnimations(R.anim.switch_service_in, R.anim.switch_service_out) - .replace(R.id.fragment_holder, CommentsFragment.getInstance(serviceId, url, - name == null ? "" : name)) + public static void openVideoDetailFragment(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager, + final int serviceId, + @Nullable final String url, + @NonNull final String title, + @Nullable final PlayQueue playQueue, + final boolean switchingPlayers) { + + final boolean autoPlay; + @Nullable final MainPlayer.PlayerType playerType = PlayerHolder.getType(); + if (playerType == null) { + // no player open + autoPlay = PlayerHelper.isAutoplayAllowedByUser(context); + } else if (switchingPlayers) { + // switching player to main player + autoPlay = PlayerHolder.isPlaying(); // keep play/pause state + } else if (playerType == MainPlayer.PlayerType.VIDEO) { + // opening new stream while already playing in main player + autoPlay = PlayerHelper.isAutoplayAllowedByUser(context); + } else { + // opening new stream while already playing in another player + autoPlay = false; + } + + final RunnableWithVideoDetailFragment onVideoDetailFragmentReady = (detailFragment) -> { + expandMainPlayer(detailFragment.requireActivity()); + detailFragment.setAutoPlay(autoPlay); + if (switchingPlayers) { + // Situation when user switches from players to main player. All needed data is + // here, we can start watching (assuming newQueue equals playQueue). + detailFragment.openVideoPlayer(); + } else { + detailFragment.selectAndLoadVideo(serviceId, url, title, playQueue); + } + detailFragment.scrollToTop(); + }; + + final Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder); + if (fragment instanceof VideoDetailFragment && fragment.isVisible()) { + onVideoDetailFragmentReady.run((VideoDetailFragment) fragment); + } else { + final VideoDetailFragment instance = VideoDetailFragment + .getInstance(serviceId, url, title, playQueue); + instance.setAutoPlay(autoPlay); + + defaultTransaction(fragmentManager) + .replace(R.id.fragment_player_holder, instance) + .runOnCommit(() -> onVideoDetailFragmentReady.run(instance)) + .commit(); + } + } + + public static void openChannelFragment(final FragmentManager fragmentManager, + final int serviceId, final String url, + @NonNull final String name) { + defaultTransaction(fragmentManager) + .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, name)) .addToBackStack(null) .commit(); } public static void openPlaylistFragment(final FragmentManager fragmentManager, final int serviceId, final String url, - final String name) { + @NonNull final String name) { defaultTransaction(fragmentManager) - .replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, - name == null ? "" : name)) + .replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, name)) .addToBackStack(null) .commit(); } @@ -511,33 +477,26 @@ public final class NavigationHelper { context.startActivity(mIntent); } - public static void openChannel(final Context context, final int serviceId, final String url) { - openChannel(context, serviceId, url, null); - } + public static void openVideoDetail(final Context context, + final int serviceId, + final String url, + @NonNull final String title, + @Nullable final PlayQueue playQueue, + final boolean switchingPlayers) { - public static void openChannel(final Context context, final int serviceId, - final String url, final String name) { - final Intent openIntent = getOpenIntent(context, url, serviceId, - StreamingService.LinkType.CHANNEL); - if (name != null && !name.isEmpty()) { - openIntent.putExtra(Constants.KEY_TITLE, name); - } - context.startActivity(openIntent); - } - - public static void openVideoDetail(final Context context, final int serviceId, - final String url) { - openVideoDetail(context, serviceId, url, null); - } - - public static void openVideoDetail(final Context context, final int serviceId, - final String url, final String title) { - final Intent openIntent = getOpenIntent(context, url, serviceId, + final Intent intent = getOpenIntent(context, url, serviceId, StreamingService.LinkType.STREAM); - if (title != null && !title.isEmpty()) { - openIntent.putExtra(Constants.KEY_TITLE, title); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Constants.KEY_TITLE, title); + intent.putExtra(VideoDetailFragment.KEY_SWITCHING_PLAYERS, switchingPlayers); + + if (playQueue != null) { + final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class); + if (cacheKey != null) { + intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey); + } } - context.startActivity(openIntent); + context.startActivity(intent); } public static void openMainActivity(final Context context) { @@ -550,7 +509,6 @@ public final class NavigationHelper { public static void openRouterActivity(final Context context, final String url) { final Intent mIntent = new Intent(context, RouterActivity.class); mIntent.setData(Uri.parse(url)); - mIntent.putExtra(RouterActivity.INTERNAL_ROUTE_KEY, true); context.startActivity(mIntent); } @@ -564,14 +522,12 @@ public final class NavigationHelper { context.startActivity(intent); } - public static boolean openDownloads(final Activity activity) { - if (!PermissionHelper.checkStoragePermissions( + public static void openDownloads(final Activity activity) { + if (PermissionHelper.checkStoragePermissions( activity, PermissionHelper.DOWNLOADS_REQUEST_CODE)) { - return false; + final Intent intent = new Intent(activity, DownloadActivity.class); + activity.startActivity(intent); } - final Intent intent = new Intent(activity, DownloadActivity.class); - activity.startActivity(intent); - return true; } public static Intent getPlayQueueActivityIntent(final Context context) { @@ -600,7 +556,8 @@ public final class NavigationHelper { return getIntentByLink(context, NewPipe.getServiceByUrl(url), url); } - public static Intent getIntentByLink(final Context context, final StreamingService service, + public static Intent getIntentByLink(final Context context, + final StreamingService service, final String url) throws ExtractionException { final StreamingService.LinkType linkType = service.getLinkTypeByUrl(url); @@ -609,15 +566,7 @@ public final class NavigationHelper { + " url=" + url); } - final Intent rIntent = getOpenIntent(context, url, service.getServiceId(), linkType); - - if (linkType == StreamingService.LinkType.STREAM) { - rIntent.putExtra(VideoDetailFragment.AUTO_PLAY, - PreferenceManager.getDefaultSharedPreferences(context).getBoolean( - context.getString(R.string.autoplay_through_intent_key), false)); - } - - return rIntent; + return getOpenIntent(context, url, service.getServiceId(), linkType); } private static Uri openMarketUrl(final String packageName) { diff --git a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java index a23172bd3..e7a028d50 100644 --- a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java +++ b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java @@ -1,8 +1,7 @@ package org.schabi.newpipe.views; import android.content.Context; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; +import android.os.Build; import android.util.AttributeSet; import android.view.SurfaceView; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; @@ -47,7 +46,8 @@ public class ExpandableSurfaceView extends SurfaceView { if (resizeMode == RESIZE_MODE_FIT // KitKat doesn't work well when a view has a scale like needed for ZOOM - || (resizeMode == RESIZE_MODE_ZOOM && VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)) { + || (resizeMode == RESIZE_MODE_ZOOM + && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)) { if (aspectDeformation > 0) { height = (int) (width / videoAspectRatio); } else { diff --git a/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java b/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java index dc5bf7133..29c38511c 100644 --- a/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java +++ b/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java @@ -270,7 +270,7 @@ public final class FocusOverlayView extends Drawable implements clearFocusObstacles((ViewGroup) decor); } - @RequiresApi(api = 26) + @RequiresApi(api = Build.VERSION_CODES.O) private static void clearFocusObstacles(final ViewGroup viewGroup) { viewGroup.setTouchscreenBlocksFocus(false); diff --git a/app/src/main/java/us/shandian/giga/get/Mission.java b/app/src/main/java/us/shandian/giga/get/Mission.java index 8e814a2af..ff1319884 100644 --- a/app/src/main/java/us/shandian/giga/get/Mission.java +++ b/app/src/main/java/us/shandian/giga/get/Mission.java @@ -35,6 +35,10 @@ public abstract class Mission implements Serializable { */ public StoredFileHelper storage; + public long getTimestamp() { + return timestamp; + } + /** * Delete the downloaded file * diff --git a/app/src/main/java/us/shandian/giga/io/StoredDirectoryHelper.java b/app/src/main/java/us/shandian/giga/io/StoredDirectoryHelper.java index 8f6070ff4..8f7e18a31 100644 --- a/app/src/main/java/us/shandian/giga/io/StoredDirectoryHelper.java +++ b/app/src/main/java/us/shandian/giga/io/StoredDirectoryHelper.java @@ -212,7 +212,7 @@ public class StoredDirectoryHelper { @NonNull @Override public String toString() { - return docTree == null ? Uri.fromFile(ioTree).toString() : docTree.getUri().toString(); + return (docTree == null ? Uri.fromFile(ioTree) : docTree.getUri()).toString(); } diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManager.java b/app/src/main/java/us/shandian/giga/service/DownloadManager.java index 994c6ee63..dc4d5701b 100644 --- a/app/src/main/java/us/shandian/giga/service/DownloadManager.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManager.java @@ -12,7 +12,8 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; +import java.util.Comparator; +import java.util.List; import us.shandian.giga.get.DownloadMission; import us.shandian.giga.get.FinishedMission; @@ -198,7 +199,7 @@ public class DownloadManager { } if (mMissionsPending.size() > 1) - Collections.sort(mMissionsPending, (mission1, mission2) -> Long.compare(mission1.timestamp, mission2.timestamp)); + Collections.sort(mMissionsPending, Comparator.comparingLong(Mission::getTimestamp)); } /** @@ -563,14 +564,10 @@ public class DownloadManager { synchronized (DownloadManager.this) { ArrayList pending = new ArrayList<>(mMissionsPending); ArrayList finished = new ArrayList<>(mMissionsFinished); - ArrayList remove = new ArrayList<>(hidden); + List remove = new ArrayList<>(hidden); // hide missions (if required) - Iterator iterator = remove.iterator(); - while (iterator.hasNext()) { - Mission mission = iterator.next(); - if (pending.remove(mission) || finished.remove(mission)) iterator.remove(); - } + remove.removeIf(mission -> pending.remove(mission) || finished.remove(mission)); int fakeTotal = pending.size(); if (fakeTotal > 0) fakeTotal++; diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index ca590a892..80c238d91 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -1,13 +1,11 @@ package us.shandian.giga.ui.adapter; import android.annotation.SuppressLint; -import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.os.Message; @@ -26,10 +24,8 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; -import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.DiffUtil; @@ -47,12 +43,15 @@ import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; import java.io.File; -import java.lang.ref.WeakReference; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; import us.shandian.giga.get.DownloadMission; import us.shandian.giga.get.FinishedMission; import us.shandian.giga.get.Mission; @@ -117,11 +116,13 @@ public class MissionAdapter extends Adapter implements Handler.Callb private final Runnable rUpdater = this::updater; private final Runnable rDelete = this::deleteFinishedDownloads; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + public MissionAdapter(Context context, @NonNull DownloadManager downloadManager, View emptyMessage, View root) { mContext = context; mDownloadManager = downloadManager; - mInflater = ContextCompat.getSystemService(mContext, LayoutInflater.class); + mInflater = LayoutInflater.from(mContext); mLayout = R.layout.mission_item; mHandler = new Handler(context.getMainLooper()); @@ -676,7 +677,30 @@ public class MissionAdapter extends Adapter implements Handler.Callb return true; case R.id.md5: case R.id.sha1: - new ChecksumTask(mContext).execute(h.item.mission.storage, ALGORITHMS.get(id)); + ProgressDialog progressDialog = null; + if (mContext != null) { + // Create dialog + progressDialog = new ProgressDialog(mContext); + progressDialog.setCancelable(false); + progressDialog.setMessage(mContext.getString(R.string.msg_wait)); + progressDialog.show(); + } + final ProgressDialog finalProgressDialog = progressDialog; + final StoredFileHelper storage = h.item.mission.storage; + compositeDisposable.add( + Observable.fromCallable(() -> Utility.checksum(storage, ALGORITHMS.get(id))) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (finalProgressDialog != null) { + Utility.copyToClipboard(finalProgressDialog.getContext(), + result); + if (mContext != null) { + finalProgressDialog.dismiss(); + } + } + }) + ); return true; case R.id.source: /*Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(h.item.mission.source)); @@ -759,8 +783,8 @@ public class MissionAdapter extends Adapter implements Handler.Callb } } - public void onDestroy() { + compositeDisposable.dispose(); mDeleter.dispose(); } @@ -961,60 +985,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb } } - - static class ChecksumTask extends AsyncTask { - ProgressDialog progressDialog; - WeakReference weakReference; - - ChecksumTask(@NonNull Context context) { - weakReference = new WeakReference<>((Activity) context); - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - - Activity activity = getActivity(); - if (activity != null) { - // Create dialog - progressDialog = new ProgressDialog(activity); - progressDialog.setCancelable(false); - progressDialog.setMessage(activity.getString(R.string.msg_wait)); - progressDialog.show(); - } - } - - @Override - protected String doInBackground(Object... params) { - return Utility.checksum((StoredFileHelper) params[0], (String) params[1]); - } - - @Override - protected void onPostExecute(String result) { - super.onPostExecute(result); - - if (progressDialog != null) { - Utility.copyToClipboard(progressDialog.getContext(), result); - if (getActivity() != null) { - progressDialog.dismiss(); - } - } - } - - @Nullable - private Activity getActivity() { - Activity activity = weakReference.get(); - - if (activity != null && activity.isFinishing()) { - return null; - } else { - return activity; - } - } - } - public interface RecoverHelper { void tryRecover(DownloadMission mission); } - } diff --git a/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java b/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java index bec947540..2a8077d51 100644 --- a/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java +++ b/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java @@ -83,8 +83,8 @@ public class ProgressDrawable extends Drawable { // render marquee width += size * 2; Path marquee = new Path(); - for (float i = -size; i < width; i += size) { - marquee.addPath(mMarqueeLine, i + mMarqueeProgress, 0); + for (int i = -size; i < width; i += size) { + marquee.addPath(mMarqueeLine, ((float)i + mMarqueeProgress), 0); } marquee.close(); diff --git a/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java b/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java index 35f40aa82..265491b8a 100644 --- a/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java +++ b/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java @@ -224,9 +224,10 @@ public class MissionsFragment extends Fragment { mList.setAdapter(mAdapter); if (mSwitch != null) { - mSwitch.setIcon(mLinear - ? ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_grid) - : ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_list)); + mSwitch.setIcon(ThemeHelper.resolveResourceIdFromAttr( + requireContext(), mLinear + ? R.attr.ic_grid + : R.attr.ic_list)); mSwitch.setTitle(mLinear ? R.string.grid : R.string.list); mPrefs.edit().putBoolean("linear", mLinear).apply(); } diff --git a/app/src/main/res/layout/settings_notification.xml b/app/src/main/res/layout/settings_notification.xml index dc329a78a..2ade057d1 100644 --- a/app/src/main/res/layout/settings_notification.xml +++ b/app/src/main/res/layout/settings_notification.xml @@ -1,78 +1,17 @@ - - + android:layout_height="wrap_content" + android:paddingTop="16dp"> - - - - - - - - - - - + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@+id/textView" /> - diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 602933b81..87622e580 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -49,7 +49,7 @@ استخدام مشغل فيديو خارجي (إختبارية) إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية ( تشغيل الفيديو المباشر غير مدعوم حتى الأن ). استخدام تور - %1$s مشاهدات + %1$s مشاهدة محتوى غير متوفر تعذرت عملية تحميل كافة صور المعاينة خطأ @@ -90,6 +90,8 @@ عرض أعلى جودة بعض الأجهزة فقط تدعم تشغيل مقاطع الفيديو 2K/4K تنسيق الفيديو الافتراضي + تذكر خصائص النوافذ المنبثقة + تذكر آخر مكان و حجم للنافذة المنبثقة اعدادات إيماءة المشغل استخدم الإيماءات للتحكم في سطوع وصوت المشغل اقتراحات البحث @@ -98,17 +100,16 @@ تخزين طلبات البحث محليا تتبع مقاطع الفيديو التي تمت مشاهدتها استئناف التشغيل - مواصلة التشغيل بعد المقاطعات (مثل المكالمات الهاتفية) + متابعة التشغيل بعد المقاطعات (مثل المكالمات الهاتفية) إظهار التلميحات \"اضغط للتجاهل\" - إظهار تلميح عندما الضغط على الخلفية أو الزر المنبثق في الفيديو \"تفاصيل:\" + عرض تلميح عند الضغط على الخلفية أو الزر المنبثق في الفيديو \"التفاصيل:\" المشغل السلوك الوضع المنبثق تشغيل في وضع منبثق تم وضعه على قائمة الانتظار في مشغل الخلفية تم وضعه على قائمة الانتظار في مشغل النافذة المنبثقة - محتوى مقيد بحسب العمر - إظهار الفيديو المقيد بحسب العمر. التغييرات المستقبلية ممكنة من \"الإعدادات\". + إظهار محتوى مقيد حسب العمر بث مباشر تقرير خطأ قائمة التشغيل @@ -155,9 +156,9 @@ بليون ليس هناك مشترِكون - %s لا مشترِك - %s مشترِك - مشتركين + %s مشترك + %s مشترك + %s مشتركين %s مشتركين %s مشتركين %s مشتركين @@ -232,12 +233,12 @@ تحدي الكابتشا ضغط مطول للإدراج الى قائمة الانتظار - %s بدون مشاهد - %s مشاهدة - %s مشاهدات - %s مشاهدات - %s مشاهدات - %s مشاهدات + %s مشاهدة + %s مشاهد + %s مشاهدة + %s مشاهدة + %s مشاهدة + %s مشاهدة %s فيديو @@ -302,7 +303,7 @@ تنزيل ملف البث الإشارات المرجعية استعمال التقديم السريع الغير دقيق - الطلب غير الدقيق يسمح للمشغل بالبحث عن مواقع أسرع بدقة أقل. البحث عن 5 ,15 أو 25 ثانية لا يعمل مع هذا. + يسمح البحث غير الدقيق للمشغل بالبحث عن مواضع بشكل أسرع وبدقة منخفضة. البحث عن 5 ,15 أو 25 ثانية لا يعمل مع هذا. تحميل الصور المصغرة تم إفراغ مساحة ذاكرة التخزين المؤقتة الخاصة بالصور الملف @@ -326,7 +327,7 @@ إزالة جميع بيانات صفحات الويب المخزنة مؤقتًا تم محو ذاكرة التخزين المؤقت للبيانات الوصفية وضع البث القادم تلقائيا في قائمة الإنتظار - متابعة إنهاء قائمة انتظار التشغيل (غير المتكررة) من خلال إلحاق تدفق ذي صلة + استمر في إنهاء قائمة انتظار التشغيل (غير-المتكررة) من خلال إلحاق تدفق ذي صلة إضافة صورة مصغرة إلى قائمة التشغيل تفضيل قائمة التشغيل تم تغيير الصورة المصغرة لقائمة التشغيل. @@ -363,11 +364,14 @@ نسخة احتياطية تعذر استيراد الاشتراكات لا يمكن تصدير الاشتراكات - استيراد اشتراكات YouTube عن طريق تنزيل ملف التصدير: -\n -\n1. انتقل إلى عنوان URL هذا: %1$s -\n2. تسجيل الدخول عندما يطلب منك -\n3. يجب أن يبدأ التنزيل (وهذا ملف التصدير) + استيراد اشتراكات YouTube من Google Takeout +\n +\n1. انتقل إلى عنوان URL هذا : %1$s +\n2. تسجيل الدخول عندما يُطلب منك ذلك +\n3. انقر على \"جميع البيانات المدرجة\" ، ثم على \"إلغاء تحديد الكل\" ، ثم حدد \"الاشتراكات\" فقط وانقر على \"موافق\" +\n4. انقر على \"الخطوة التالية\" ثم على \"إنشاء تصدير\" +\n5. انقر فوق الزر \"تنزيل\" بعد ظهوره و +\n6. من الملف المضغوط الذي تم تنزيله ، استخرج ملف .json (عادةً ضمن \"YouTube و YouTube)Music/subscriptions/subscriptions.json\") واستورده قم باستيراد ملف تعريف SoundCloud عن طريق كتابة عنوان URL أو معرفك: \n \n1. تمكين \"وضع سطح المكتب\" في متصفح الويب (الموقع غير متاح للأجهزة المحمولة) @@ -409,7 +413,7 @@ اختر علامة التبويب استخدم إيماءات التحكم في صوت المشغل التحكم بالإيماءات السطوع - استخدم الإيماءات للتحكم في سطوع المشغل + استخدام الإيماءات للتحكم في سطوع المشغل التحديثات تم حذف الملف تتبيه تحديث التطبيق @@ -503,7 +507,7 @@ تغيير مجلدات التنزيل إلى حيز التنفيذ‮‮‮ تبديل الخدمة ، المحدد حاليًا: الكشك الافتراضي - لاتوجد مشاهدة + لا توجد مشاهدة لا أحد يستمع ستتغير اللغة بمجرد إعادة تشغيل التطبيق. @@ -630,7 +634,7 @@ الأغاني هذا الفيديو مقيد بالفئة العمرية. \n -\nقم بتشغيل \"المحتوى المقيد بالفئة العمرية\" في الإعدادات إذا كنت تريد مشاهدته. +\nقم بتشغيل \"%1$s\" في الإعدادات إذا كنت تريد رؤيته. نعم، ومقاطع الفيديو التي تمت مشاهدتها جزئيًا ستتم إزالة مقاطع الفيديو التي تمت مشاهدتها قبل وبعد إضافتها إلى قائمة التشغيل. \nهل أنت واثق؟ هذا لا يمكن التراجع عنها! @@ -638,7 +642,7 @@ إزالة ماتمت مشاهدته ستكون النصوص الأصلية من الخدمات مرئية في عناصر البث عرض الوقت الأصلي على العناصر - وضع مقيد يوتيوب + شغيل \"وضع تقييد المحتوى\" في يوتيوب لـ %s أنشأها %s الصورة الرمزية للقناة @@ -673,4 +677,14 @@ زر الإجراء الأول قياس الصورة المصغرة للفيديو المعروض في الإشعار من 16: 9 إلى 1: 1 نسبة العرض إلى الارتفاع (قد يؤدي إلى تشوهات) مقياس الصورة المصغرة إلى نسبة عرض إلى ارتفاع 1: 1 + امسح ملفات تعريف الارتباط التي يخزنها NewPipe عند حل reCAPTCHA + تم مسح ملفات تعريف الارتباط reCAPTCHA + امسح ملفات تعريف الارتباط reCAPTCHA + يوفر YouTube \"وضع تقييد المحتوى\" الذي يخفي المحتوى المحتمل للكبار. + عرض المحتوى الذي يُحتمل أن يكون غير مناسب للأطفال لأن له حدًا عمريًا (مثل 18+). + إظهار تسرب الذاكرة + قائمة الانتظار + قائمة الانتظار + اجعل أندرويد يخصص لون الإشعار وفقا للون الرئيسي في الصورة المصغرة (لاحظ أن هذا غير متوفر على جميع الأجهزة + تلوين الاشعارات \ No newline at end of file diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index d586d0980..6deac3bee 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -88,8 +88,6 @@ Qeyri-dəqiq axtarış (videonu irəli/geri çəkmə) istifadə edin Qeyri-dəqiq axtarış pleyerə azaldılmış həssaslıqla mövqeləri daha sürətlə axtarmağa imkan verir. 5, 15 və ya 25 saniyəlik axtarış bununla işləmir. Cəld irəli/geri çəkmə müddəti - Ani pəncərənin sonuncu ölçü və mövqeyini xatırla - Ani pəncərə xüsusiyyətlərini xatırla Heç nə Buferizasiya olunur Qarışdır @@ -110,4 +108,32 @@ Abunəlik yenilənmədi Abunəlik dəyişdirilmədi Nəticələr göstərilir: %s + Kanallar + Kanal + %s tərəfindən + \"Youtube\"un \"Məhdudiyyətli Rejimi\"ni aktivləşdir + Yaş limiti olduğuna görə (məs. 18+) böyük ehtimal uşaqlar üçün uyğun olmayan məzmunu göstər. + Yaş məhdudiyyətli məzmunu göstər + Məzmun + Ani pəncərə növbəyə salındı + Fon pleyeri növbəyə salındı + Ani pəncərədə oxudulur + Fonda oxudulur + Bildiriş + Yeniləmələr + Sazlama + Digər + Görünüş + Ani pəncərə + Tarix və keş + Video və səs + Davranış + Pleyer + İlkin məzmun dili + Xidmət + Məzmun üçün ilkin ölkə + URL tanınmadı. Başqa bir tətbiq ilə açılsın\? + Dəstəklənməyən URL + \"Əlavə etmək üçün basılı tutun\" məsləhətini göstər + \"Növbəti\" və \"Bənzər\" videoları göstər \ No newline at end of file diff --git a/app/src/main/res/values-b+uz+Latn/strings.xml b/app/src/main/res/values-b+uz+Latn/strings.xml new file mode 100644 index 000000000..497994e8a --- /dev/null +++ b/app/src/main/res/values-b+uz+Latn/strings.xml @@ -0,0 +1,402 @@ + + + Tashqi video pleerdan foydalanish + aylantirish + Brauzer tanlang + Bilan baham ko\'rish + %s uchun natijalar ko\'rsatilmoqda + \"%1$s\" demoqchimisiz\? + Sozlashlar + Qidirish + stream file yuklab olish + Yuklab olish + Baham ko\'rish + O\'rnatish + Brauzerda ochish + Qalqib chiqadigan rejimda ochish + Bekor qilish + Stream pleer topilmadi (uni ijro etish uchun VLC o\'rnatishingiz mumkin). + Hech qanday translatsiya pleyeri topilmadi. VLC o\'rnatilsinmi\? + %1$s tomonidan e‘lon qilingan + %1$s marta ko‘rilgan + Boshlash uchun \"Izlash\" tugmasini bosing +\n + Player yorqinligini va ovoz balandligini boshqarish uchun imo-ishoralardan foydalanish + Player imo-ishoralarini boshqarish + Player tovushini boshqarish uchun imo-ishoralardan foydalanish + Player yorqinligini boshqarish uchun imo-ishoralardan foydalaning + Yorqinlik ishoralarini boshqarish + Ovoz balandligini ishoralarni boshqarish + Avto-navbat + Tegishli stream qo\'shib, ijro etish navbatini tugatishni (takrorlanmaydigan) davom ettirish + avtomatik navbat next stream + Metadata keshi o\'chirildi + Barcha keshlangan veb-sahifa ma\'lumotlarini olib tashlash + Keshlangan metadatalarni o\'chirish + Rasm keshi o\'chirildi + Eskizlarni yuklash, ma\'lumotlarni tejash va xotiradan foydalanishni oldini olish uchun o\'chirib qo\'ying. O\'zgarishlar xotiradagi va diskdagi rasm keshini tozalaydi. + sharhlarni yashirishni o\'chirish + Izohlarni ko\'rsatish + Eskizlarni yuklang + Aktiv ijro etish navbati almashtiriladi + Bir ijro etishdan boshqasiga o\'tish sizning navbatingizni almashtirishi mumkin + Navbatni tozalashdan oldin tasdiqlashni so\'rash + Oldinga tez / oldinga siljish davomiyligini qidirish + Noto\'g\'ri izlash ijro etuvchiga aniqlikni pasayishi bilan tezroq pozitsiyalarni qidirishga imkon beradi. 5, 15 yoki 25 soniyani qidirish bu bilan ishlamaydi. + Tez aniq bo\'lmagan izlashdan foydalanish + Qora + qorong\'i + Yorug\' + Tema + Standart video format + Standart audio format + Audio + Hech narsa + Buferlash + Aralash + Takrorlash + Bildirishnomada ko\'rsatish uchun eng ko\'p uchta amalni tanlashingiz mumkin! + Quyidagi har bir bildirishnomani ustiga bosib uni tahrir qiling. O\'ng tomondagi katakchalar yordamida ixcham bildirishnomada ko\'rsatilishi uchun ulardan uchtasini tanlang. + Beshinchi harakat tugmasi + To\'rtinchi harakat tugmasi + Uchinchi harakat tugmasi + Ikkinchi harakat tugmasi + Birinchi harakat tugmasi + Bildirishnomada ko\'rsatilgan video eskizini 16: 9dan 1: 1 gacha tomonlarning nisbatiga qarab o\'lchamang (buzilishlarni keltirib chiqarishi mumkin) + Eskizini 1: 1 tomonlar nisbatiga o‘lchash + Kodi media-markazi orqali videoni ijro etish variantini ko\'rsatish + \"Kodi bilan ijro etish\" parametrini ko\'rsatish + missing Kore dasturini o\'rnatasizmi\? + Kodi bilan ijro etish + Faqat ba\'zi qurilmalar 2K / 4K videolarni ijro etishi mumkin + Yuqori o\'lchamlarni ko\'rsatish + "Standart pop-up o\'lchamlari" + Standart o\'lchamlari + NewPipe boshqa dasturdan chaqirilganda videoni ijro etadi + Avtomatikplay + Yuklash papkalarini kuchga kirishi uchun o\'zgartirish + Audio fayllar uchun yuklab olish papkasini tanlash + Yuklab olingan videofayllar shu yerda saqlanadi + Yuklab olingan audio fayllar shu yerda saqlanadi + Ovozni yuklab olish papkasi + Video fayllar uchun yuklab olish papkasini tanlash + Videoni yuklab olish jildi + Qo\'shish + Qalqib ko\'rinish + Fon ko\'rinishi + Yorliqlangan pleylistlar + Yorliqni tanlash + Yangi yorliq + Obunalar + Asosiy + Ma\'lumotni ko\'rsatish + Obunani yangilab bo\'lmadi + Obunani o\'zgartirib bo\'lmadi + Kanal obunasi bekor qilindi + Obunani bekor qilish + Obuna bo\'lindi + Obuna bo\'lish + Qalqib ko\'rinadigan rejim + Tashqi audio pleerdan foydalanish + Ba\'zi piksellarda ovozni o\'chirish + Tomosha tarixini tozalash + ReCAPTCHA-ni hal qilganingizda NewPipe-da saqlanadigan cookie-fayllarni o\'chirib tashlang + Eksport tarixi, obunalari va pleylistlari + Joriy tarixingiz va obunalaringizni bekor qiladi + reCAPTCHA cookies fayllari o\'chirildi + ReCAPTCHA cookie-fayllarini o\'chirish + Ma\'lumotlar bazasini eksport qilish + Ma\'lumotlar bazasini import qilish + Asosiyga o\'tish + Pop-upga o\'tish + Orqa fonga o\'tish + Yo\'nalishni almashtirish / yoqish + [Noma\'lum] + NewPipe yangi versiyasi haqida bildirishnomalar + Ilovani yangilash bildirishnomasi + NewPipe fon va popup pleyerlari uchun bildirishnomalar + NewPipe bildirishnomasi + Fayl + Faqat bittasi + Har doim + Barchasini ijro etish + Fayl o\'chirildi + Bekor qilish + Eng yaxshi qaror + Hajmi o\'zgartirilmoqda + Tozalash + Yangilash + Filter + Ijrochilar o\'chirib qo\'yilgan + Keyin + Ha + Artistlar + Albomlar + Qo\'shiqlar + Natijalar + Foydalanuvchilar + Treklar + Videolar + Playlistlar + Playlist + Kanallar + Kanal + Hammasi + Xato haqida xabar berish + Yuklanganlar + Yuklanganlar + Jonli + Ushbu video yoshga cheklangan. +\n +\nAgar xohlasangiz, sozlamalarda \"%1$s\" ni yoqing. + YouTube \"cheklangan rejim\" ni taqdim etadi, u katta yoshlilar uchun tarkibni yashiradi. + YouTube-ning \"Cheklangan rejimi\" ni yoqish + Tarkibni bolalar uchun yaroqsiz deb ko\'rsating, chunki uning yosh chegarasi bor (18+ kabi). + Yoshi cheklangan tarkibni ko\'rsatish + Tarkib + Pop-up pleyerida navbat ketma-ketlikda + Orqa fon pleyerida navbat ketma-ketlikda + Pop-up rejimda ijro etish + Ijro etish foni + Bildirishnoma + Yangilanishlar + Nosozliklarni tuzatish + Boshqalar + Tashqi ko\'rinish + Pop-up + Tarix va kesh + Video va audio + Xatti-harakat + Ijro etish + Namuna allaqachon mavjud + Faqat HTTPS URL-lari qo\'llab-quvvatlanadi + Namunani tasdiqlab bo\'lmadi + Namuna URL manzilini kiriting + Namuna qo\'shish + %s da sizga yoqadigan misollarni toping + Sevimli PeerTube nusxalarini tanlang + PeerTube misollari + Standart kontent tili + Xizmatlar + Standart kontent mamlakati + URL manzili aniqlanmadi. Boshqa ilova bilan ochilsinmi\? + Qo\'llab-quvvatlanmaydigan URL manzili + \"Tafsilotlar:\" videodagi fon yoki po-pup tugmachasini bosganda ko\'rsatma. + \"Qo\'shish uchun ushlab turish\" maslahatini ko\'rsatish + \'Keyingi\' va \'O\'xshash\' videolarni namoyish etish + Avtoplay + Uzilishlardan keyin ijro etishni davom ettirish (masalan. phonecalls) + Yuklab olish + Ijro etishni davom ettirish + Ko\'rilgan videolarni kuzatib borish + Ma\'lumotlarni tozalash + Ro\'yxatlarda ijro holati ko\'rsatkichlarini ko\'rsatish + Ro\'yxatlardagi pozitsiyalar + Oxirgi ijro holatini tiklash + Ijro etishni davom ettirish + Qidirayotganda takliflarni ko\'rsatish + Tarixni ko\'rish + Qidiruv so\'rovlarini mahalliy sifatida saqlash + Qidiruv tarixi + Takliflarni qidirish + Ogohlantirish: Barcha fayllarni import qilib bo\'lmadi. + Haqiqiy ZIP fayli yo‘q + Import qilindi + Eksport qilindi + kiosk tanlash + Hali pleylist xatcho\'plari yo\'q + Pleylistni tanlang + Hech qanday kanal obunasi yo\'q + Kanal tanlash + Kanal sahifasi + Feed sahifasi + Obuna sahifasi + Standart kiosk + Kiosk sahifasi + Bo\'sh sahifa + Tanlash + Asosiy sahifada qanday yorliqlar ko\'rsatilgan + Asosiy sahifaning tarkibi + Eng ko\'p ijrolar etilganlar + Oxirgi ijro + Haqiqatan ham barcha narsalarni tarixdan o\'chirishni xohlaysizmi\? + Ushbu narsani tomosha tarixidan o\'chirishni xohlaysizmi\? + Ushbu narsani qidiruv tarixidan o\'chirmoqchimisiz\? + Element o\'chirildi + Tarix tozlandi + Tarix bo\'sh + Tarix + Tarix o\'chirilgan + Qidirilgan + Ko\'rilgan + Tarix + Litsenziyani o\'qish + NewPipe - bu nusxa ko\'chirish dasturiy ta\'minotidir: Siz foydalanishingiz, baham ko\'rishingiz va o\'zingizning xohishingiz bilan yaxshilashingiz mumkin. Xususan, siz uni bepul dasturiy ta\'minot fondi tomonidan e\'lon qilingan GNU umumiy jamoat litsenziyasi shartlari asosida qayta tarqatishingiz va / yoki o\'zgartirishingiz mumkin, Litsenziyaning 3-versiyasi yoki (sizning xohishingizga ko\'ra) har qanday keyingi versiyada. + NewPipe litsenziyasi + Maxfiylik siyosatini o\'qish + NewPipe loyihasi sizning shaxsiy hayotingizga jiddiy e\'tibor beradi. Shuning uchun ilova sizning roziligingizsiz biron bir ma\'lumot to\'plamaydi. +\nNewPipe-ning maxfiylik siyosati halokat to\'g\'risidagi hisobotni yuborganingizda qanday ma\'lumotlar yuborilishi va saqlanishi haqida batafsil ma\'lumot beradi. + NewPipe-ning maxfiylik siyosati + Qo\'shimcha ma\'lumot va yangiliklar uchun NewPipe veb-saytiga tashrif buyuring. + Websayt + Qaytarib berish + Hadya etish + NewPipe ko\'ngillilar tomonidan bo\'sh vaqtlarini sarflash orqali sizga eng yaxshi foydalanuvchi tajribasini taqdim etadi. Ishlab chiquvchilarga bir chashka qahvadan zavqlanib, NewPipe-ni yanada yaxshiroq qilishlariga yordam berish. + GitHubda ko\'rish + Sizda g\'oyalar bormi; tarjima, dizayndagi o\'zgarishlar, kodni tozalash yoki haqiqiy og\'ir kodni o\'zgartirish - yordam har doim mamnuniyat bilan qabul qilinadi. Qancha ko\'p ish qilinsa, shuncha yaxshi bo\'ladi! + Hissa qo\'shish + Androidda Libre yengil streaming. + Litsenziyalar + Xissadorlar + Haqida + Veb-saytni ochish + Litsenziyani yuklab bo\'lmadi + © %1$s tomonidan %2$s gacha %3$s + Uchinchi tomon litsenziyalari + Haqida + Sozlamalar + NewPipe haqida + Ushbu faylni ijro etish uchun dastur o\'rnatilmagan + Ko\'pchilik maxsus belgilar + Yozuvlar va raqamlar + O\'zgartirish belgisi + Noto\'g\'ri belgilar ushbu qiymat bilan almashtiriladi + Fayl nomidagi ruxsat berilgan belgilar + Yuklab olish + Bajarildi + reCAPTCHA muammosi so\'raldi + Hal etilganda \"Bajarildi\" tugmasini bosing + reCAPTCHA muammosi + 1 ta element o\'chirildi. + Ushbu ruxsat zarur +\npopup rejimida oching + Yuklab olish papkasini keyinroq sozlamalarda belgilang + Buferga nusxa olindi + Iltimos kuting… + Tafsilotlar uchun bosing + NePipe yuklab olinmoqda + Noto\'g\'ri shakllangan URL yoki Internet mavjud emas + Fayl allaqachon mavjud + Qo\'llab-quvvatlanmaydigan server + Xato + Iplar + Faylnomi + Ok + Yangi missiya + Nomni o\'zgartirish + Tarqatish + Sumnazorat + Hammasini o\'chirish + Bittasini o\'chirish + O\'chirish + Yaratish + Ijro etish + Pauza + Boshlash + Izohlar yo\'q + + %s video + %s videolar + + ∞ videolar + 100+ videolar + Videolar yo\'q + + %s tinglovchi + %s tinglovchilar + + Hech kim tinglamayapti + + %s ko\'ryapti + %s ko\'ryaptilar + + Hech kim ko\'rmayapti + + %s ko\'rish + %s ko\'rishlar + + Ko\'rishlar yo\'q + Obunachilar soni mavjud emas + + %s bunachisi + %s obunachilar + + Obunachilar yo\'q + Hozirda tanlangan xizmatni yoqish: + B + M + k + Avval omborga kirishga ruxsat berish + Qayta + Audio + Video + \'%1$s\' yuklab olish katalogi yaratildi + \'%1$s \' yuklab olish katalogini yaratib bo\'lmadi + Qayta tartiblash uchun tortish + Bu erda kriketlardan boshqa hech narsa yo\'q + Natija yo\'q + Foydalanuvchi hisoboti + Hisobotda xato + (Eksperimental) Maxfiylikni oshirish uchun Tor orqali trafikni majburan yuklab oling (videolarni streamlash hali qo\'llab-quvvatlanmaydi). + Tor-dan foydalanish + Dislayklar + Layklar + Yuklovchining avatar eskizi + Videoni ijro etish muddati, davomiyligi: + Videoni oldindan ko\'rish uchun eskiz + Detallar: + Sizning sharhingiz (ingliz tilida): + Nima: \\n So\'rov: \\nTarkib tili: \\nTarkib mamlakati: \\nIlova tili: \\ nXizmat: \\ nGMT vaqti: \\ nPaket: \\ nVersion: \\ nOS versiyasi: + Nima sodir bo\'ldi: + Info: + Hisobot + Kechirasiz, biron bir xato yuz berdi. + Iltimos, sizning harakatingizni muhokama qiladigan muammo allaqachon mavjudligini tekshiring. Ikki nusxadagi ticketlarni yaratishda siz bizdan vaqt ajratib, biz haqiqiy xatolarni tuzatishga sarflashimiz mumkin edi. + GitHub haqida hisobot + Formatlangan hisobotni nusxalash + Ushbu xato haqida elektron pochta orqali xabar berish + Kechirasiz, bunday bo\'lmasligi kerak edi. + Boshqa ilovalar orqali ko\'rsatishga ruxsat berish + Birlamchi parametrlarni tiklashni xohlaysizmi\? + Birlamchi parametrlarni tiklash + Saqlangan yorliqlarni o\'qib bo\'lmadi, shuning uchun standartlardan foydalaning + Yuklash uchun stream mavjud emas + Xato yuz berdi: %1$s + Fayl nomi bo\'sh bo\'lishi mumkin emas + Fayl mavjud emas yoki uni o\'qish yoki yozish uchun ruxsat yo\'q + Bunday fayl / tarkib manbai yo\'q + Bunday papka yo\'q + Fayl ko\'chirildi yoki o\'chirildi + Hech qanday audio stream topilmadi + Hech qanday video stream topilmadi + URL manzili yaroqsiz + Tashqi playerlar ushbu turdagi havolalarni qo\'llab-quvvatlamaydilar + Player xatosidan qutulish + Qayta tiklanmaydigan pleyerda xatolik yuz berdi + Ushbu stream ijro etilmadi + Rasm yuklanmadi + Hech qanday stream olinmadi + Live streamlar hali qo\'llab-quvvatlanmaydi + Yuklab olish menyusi sozlanmadi + Tarkib mavjud emas + Veb-saytni to\'liq tahlil qilib bo\'lmadi + Veb-saytni tahlil qilib bo\'lmadi + Videoning URL manzilini o\'chirib bo\'lmadi + Barcha eskizlarni yuklab bo\'lmadi + Tarmoqda xato + Tashqi SD-kartaga yuklab olishning iloji yo\'q. Yuklash papkasining joylashuvi tiklansinmi\? + Tashqi xotira mavjud emas + Xato + Yordam + Qidiruv tarixi o\'chirildi. + Butun qidiruv tarixi o\'chirilsinmi\? + Qidiruv kalit so\'zlar tarixini o\'chiradi + Qidiruv tarixini tozalash + Ijro pozitsiyalari o\'chirildi. + Barcha ijro holatlari o\'chirilsinmi\? + Barcha ijro holatlarini o\'chiradi + Ijro pozitsiyalarini o\'chirib tashlash + Tomosha tarixi o\'chirildi. + Tomosha tarixi butunlay o\'chirib tashlansinmi\? + Ijro etilgan streamlar tarixi va ijro holatlarini o\'chiradi + \ No newline at end of file diff --git a/app/src/main/res/values-b+zh+HANS+CN/strings.xml b/app/src/main/res/values-b+zh+HANS+CN/strings.xml index 74dcfc717..d0f2731eb 100644 --- a/app/src/main/res/values-b+zh+HANS+CN/strings.xml +++ b/app/src/main/res/values-b+zh+HANS+CN/strings.xml @@ -167,8 +167,7 @@ 视频和音频 在后台播放 内容 - 受年龄限制的内容 - 显示受年龄限制的视频。可从设置允许此类内容。 + 展示年龄限制的内容 直播 下载 下载 @@ -210,6 +209,8 @@ 使用更高的分辨率 仅某些设备支持播放2K / 4K视频 清除 + 记住悬浮窗属性 + 记住最后一次使用悬浮窗的大小和位置 悬浮窗 调整大小 隐藏部分没有音频的分辨率 @@ -358,11 +359,14 @@ 以前的导出 无法导入订阅 无法导出订阅 - 通过下载导出文件来导入 YouTube 订阅: + 从 Google takeout 导入YouTube 订阅: \n -\n1. 转到此网站: %1$s -\n2. 登录(如果需要) -\n3. 应该立即开始下载(即导出文件) +\n1. 转到这个URL:%1$s +\n2. 登录谷歌账户 +\n3. 点击“所有包含的数据”,然后点击“取消选择全部”,然后只选择“订阅”,然后点击“确定” +\n4. 点击“下一步”然后点击“创建导出” +\n5. 在“下载”按钮出现后,点击它 +\n6. 从下载的takeout压缩包提取.json文件 (通常能够位于\"YouTube and YouTube Music/subscriptions/subscriptions.json\")并在此导入它。 通过输入网址或你的 ID 导入 SoundCloud 配置文件: \n \n1. 在浏览器中启用\"电脑模式\"(该网站不适用于移动设备) @@ -581,7 +585,7 @@ 歌曲 该视频有年龄限制。 \n -\n如果您想要观看,请在设置中启用“年龄限制内容”。 +\n如果您想要观看,请在设置中启用\"%1$s\"。 由 %s 由%s创建 频道的头像缩略图 @@ -592,7 +596,7 @@ 移除看过的视频 来自服务的原始文本将在流项目中可见 在项目上显示原始时间 - YouTube受限模式 + 打开YouTube\"受限模式\" 仅显示未分组订阅 播放列表页 尚无播放列表书签 @@ -624,4 +628,14 @@ 将通知中显示的视频缩略图长宽比从16:9缩放到1:1(可能会导致失真) 缩放缩略图到1:1的长宽比 通知 + 显示内存泄漏 + 已加入队列 + 加入队列 + 清理你在解决验证码时 NewPipe 存储的cookies + reCAPTCHA cookies 已被清理 + 清理 reCAPTCHA cookies + YouTube提供了一个“受限模式”,会隐藏潜在的成人内容。 + 展示可能不适合儿童观看的内容,因为它有年龄限制(比如18岁以上)。 + 让安卓系统根据视频缩略图的主色彩自定义通知的颜色(注意,该特性并非在所有设备上都可用) + 对通知着色 \ No newline at end of file diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 0a65bda22..89d0b3997 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -58,6 +58,8 @@ Светлая Цёмная Чорная + Аднавіць акно + Запамінаць памер і становішча ўсплываючага акна Хуткі пошук пазіцыі Недакладны пошук дазваляе плэеру шукаць пазіцыю хутчэй, але менш дакладна Загружаць мініяцюры @@ -100,7 +102,6 @@ Дададзена ў чаргу ў акне Кантэнт Кантэнт 18+ - Відэа з узроставымі абмежаваннямі. Дазволіць падобны кантэнт можна ў \"Наладах\". Трансляцыя Загрузкі Загрузкі diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 65e122ed2..73ec90c82 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -52,6 +52,8 @@ Светла Тъмна Черна + Помни размера и позицията на прозореца + Използвай размера и позицията на прозореца от предишния път Контролиране на плейъра чрез жестове Позволи използване на жестове за контрол на яркостта и силата на звука на плейъра Предложения за търсене @@ -80,7 +82,6 @@ Включен в опашката в нов прозорец Съдържание Съдържание за възрастни - Покажи съдържание за възрастни. Разрешаването на такова съдържание става от Настройки. НА ЖИВО Изтегляния Изтегляния diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index 8db8f61e4..647160e69 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -44,6 +44,8 @@ উজ্জ্বল অন্ধকার কালো + পপআপ আকার এবং অবস্থান মনে রাখো + শেষ আকার এবং পপআপ সেট অবস্থান মনে রাখো ডাউনলোড পরবর্তী এবং অনুরূপ ভিডিওগুলি দেখাও URL সমর্থিত নয় @@ -56,7 +58,6 @@ পপআপ মোডে চলছে কন্টেন্ট বয়স সীমাবদ্ধ কন্টেন্ট দেখাও - ভিডিওটিকে বয়স সীমিত করা হয়েছে। প্রথমে সেটিংসে বয়স সীমাবদ্ধ ভিডিওগুলি সক্ষম করো। লাইভ ডাউনলোডগুলি ডাউনলোডগুলি @@ -359,4 +360,5 @@ পুনরায় প্রথম ক্রিয়া বোতাম থাম্বনেলে ১:১ অনুপাতে করো + আমদানি/রপ্তানি \ No newline at end of file diff --git a/app/src/main/res/values-bn-rIN/strings.xml b/app/src/main/res/values-bn-rIN/strings.xml index 96e51912d..8cdef4553 100644 --- a/app/src/main/res/values-bn-rIN/strings.xml +++ b/app/src/main/res/values-bn-rIN/strings.xml @@ -82,7 +82,6 @@ ডাউনলোডগুলি ডাউনলোডগুলি লাইভ - ভিডিওটিকে বয়স সীমিত করা হয়েছে। প্রথমে সেটিংসে বয়স সীমাবদ্ধ ভিডিওগুলি সক্ষম করো। বয়স সীমাবদ্ধ কন্টেন্ট দেখাও কন্টেন্ট পপআপ মোডে চলছে @@ -119,6 +118,8 @@ দ্রুত-ফরওয়ার্ড/-পুনরায় সন্ধান সময়কাল অনির্দিষ্ট সন্ধান প্লেয়ারকে আরো দ্রুত গতিতে সন্ধান করার সুবিধা দেয়, কিন্তু এটি সম্পূর্ণ নির্ভুল নাও হতে পারে ৷ ৫, ১৫ ও ২৫ সেকেন্ডের জন্য এটা কাজ করবে না ৷ দ্রুত টানা ব্যাবহার করুন + শেষ আকার এবং পপআপ সেট অবস্থান মনে রাখো + পপআপ আকার এবং অবস্থান মনে রাখো কালো অন্ধকার উজ্জ্বল diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 114d9607c..01d492260 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -233,7 +233,6 @@ ডাউনলোডগুলি লাইভ YouTube নিষিদ্ধ মোড - ভিডিওটিকে বয়স সীমিত করা হয়েছে। প্রথমে সেটিংসে বয়স সীমাবদ্ধ ভিডিওগুলি সক্ষম করো। বয়স সীমাবদ্ধ কন্টেন্ট দেখাও কন্টেন্ট পপআপ মোডে চলছে @@ -287,6 +286,8 @@ দ্রুত-ফরওয়ার্ড/-পুনরায় সন্ধান সময়কাল অনির্দিষ্ট সন্ধান প্লেয়ারকে আরো দ্রুত গতিতে সন্ধান করার সুবিধা দেয়, কিন্তু এটি সম্পূর্ণ নির্ভুল নাও হতে পারে ৷ ৫, ১৫ ও ২৫ সেকেন্ডের জন্য এটা কাজ করবে না ৷ দ্রুত টানা ব্যাবহার করুন + শেষ আকার এবং পপআপ সেট অবস্থান মনে রাখো + পপআপ আকার এবং অবস্থান মনে রাখো কালো অন্ধকার উজ্জ্বল @@ -359,4 +360,119 @@ চতুর্থ অ্যাকশন বাটন তৃতীয় অ্যাকশন বাটন দ্বিতীয় অ্যাকশান বাটন + একটি সংশ্লিষ্ট স্ট্রিম যোগ করে প্লেব্যাক সারি শেষ করা অব্যাহত রাখো (পুনরাবৃত্তি ছাড়া) + সক্রিয় প্লেয়ার সারি প্রতিস্থাপন করা হবে + এক প্লেয়ার থেকে অন্য প্লেয়ারে পরিবর্তন করলে তোমার সারি প্রতিস্থাপিত হতে পারে + কিউ মোছার আগে নিশ্চিত করো + কমপ্যাক্ট বিজ্ঞপ্তিতে প্রদর্শন করতে তুমি সর্বাধিক তিনটি ক্রিয়া নির্বাচন করতে পারো! + নিচের প্রতিটি প্রজ্ঞাপন ক্রিয়া সম্পাদনা করো। ডান দিকের চেকবাক্স ব্যবহার করে কম্প্যাক্ট নোটিফিকেশনে দেখানোর জন্য তিনটি পর্যন্ত নির্বাচন করো। + ১৬:৯ থেকে ১:১অনুপাতে প্রদর্শিত ভিডিও থাম্বনেইল পরিবর্তন করো (বিকৃতি প্রবর্তন করতে পারে) + ফিড + ওভাররাইট + সারিবদ্ধ + পুনরুদ্ধাররত + পরে-প্রক্রিয়াকরণ + সারিবদ্ধ + প্রক্রিয়ারত + কখনো না + হালনাগাদ + পুনশুরু + স্টেপ + পিচ + টেম্পো + রপ্তানি করা হচ্ছে… + আমদানি করা হচ্ছে… + + %s সদস্যতা + %s সদস্যতাগণ + + ব্যবহারকারীরা + বিজ্ঞপ্তি + বাধার পর প্লে চালিয়ে যাও (উদাহরণস্বরূপ ফোনকল) + সদস্যতা রপ্তানি করা যায়নি + সদস্যতা/সাবস্ক্রিপশন আমদানি করা যায়নি + স্বয়ংক্রিয়ভাবে উৎপাদিত (কোনও আপলোডার পাওয়া যায়নি) + পছন্দ-তালিকা মুছে ফেলা যায়নি। + প্লে-তালিকা থাম্বনেইল হিসেবে সেট করো + কোনও বৈধ জিপ ফাইল নেই + এখনো কোন প্লে-তালিকা বুকমার্ক নেই + এখনও কোনও চ্যানেল সাবস্ক্রিপশন নেই + মূল পৃষ্ঠার বিষয়বস্তু + ফাইলের নামে অনুমোদিত অক্ষরসমূহ + সমাধান হয়ে গেলে \"সম্পন্ন\" টিপো + কেউ শুনছে না + কেউ দেখছে না + সেবাটি পরিবর্তন করো, বর্তমানে নির্বাচিত: + এখানে ঝিঝিপোকা ছাড়া আর কিছু নেই + এই ধরনের কোন ফাইল/বিষয়বস্তুর উৎস নেই + অপুনরুদ্ধারযোগ্য প্লেয়ার ত্রুটি ঘটেছে + পপআপ প্লেয়ারে সারিবদ্ধ + পটভূমি প্লেয়ারে সারিবদ্ধ + ইন্সট্যান্সটি যাচাই করা যায়নি + রিক্যাপচা কুকিগুলো পরিষ্কার করা হয়েছে + হ্যাঁ, এবং আংশিকভাবে দেখা ভিডিও + সিস্টেম দ্বারা অনুমতি অগ্রাহ্য করা হয়েছে + ব্যবস্থা দ্বারা ক্রিয়া অস্বীকার করা হয়েছে + "স্বয়ংক্রিয়ভাবে প্লেব্যাক শুরু করো %s — তে" + একটি পপ-আপে প্লে শুরু করো + পটভূমিতে প্লে শুরু করো + অ্যান্ড্রয়েডে মুক্ত সহজ স্ট্রিমিং। + ইতিহাস, সদস্যতা এবং পছন্দ-তালিকা রপ্তানি করো + \"সংযোজন করতে ধরে রাখো\" পরামর্শ দেখাও + উপলব্ধ হলে আলাদা ফিড থেকে এনো + সার্ভার ডেটা পাঠায় না + সার্ভারে সংযোগ করা যাচ্ছে না + আমদানি + আমদানি/রপ্তানি + সম্মেলন + নির্বাচন + তালিকায় প্লেব্যাক অবস্থান সূচক দেখাও + স্বত-সারি + সংযোগের সময় শেষ + পোস্ট-প্রক্রিয়াকরণ ব্যর্থ হয়েছে + প্রদর্শন পরিবর্তন করো + তোমার আইডি, soundcloud.com/আইডি + আগের রপ্তানি + ফাইল আমদানি করো + রপ্তানি করো + আমদানি করো + বুকমার্ক প্লেলিস্ট + তথ্য আনা হচ্ছে… + পপআপ প্লেয়ার + পটভূমি প্লেয়ার + প্লে সারি + সর্বোচ্চ পছন্দ + ফিড পৃষ্ঠা + সদস্যতা পৃষ্ঠা + ডিফল্ট কিয়স্ক + কিয়স্ক পৃষ্ঠা + প্রতিস্থাপক অক্ষর + + %sটি ভিডিও + %sটি ভিডিও + + ∞ ভিডিও + ১০০+ ভিডিও + + %s জন শ্রোতা + %s জন শ্রোতা + + + %s জন দেখছে + %s জন দেখছে + + + %s বার দেখা + %s বার দেখা + + সারিবদ্ধ করা হয়েছে + এনকুই + + %d সেকেন্ড + %d সেকেন্ড + + দেখা থেকে অপসারণ করো + সিস্টেম ডিফল্ট + সাফ ব্যবহার করো + বিজ্ঞপ্তি রঙিন করো \ No newline at end of file diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 4b3a25555..78ee40d15 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -37,7 +37,6 @@ Depuració Contingut Desactiva les restriccions per edat - Mostra el vídeo restringit per edat. Podeu permetre aquesta mena de continguts des dels paràmetres. Directe Baixades Baixades @@ -143,6 +142,8 @@ No s\'ha trobat l\'aplicació Kore. Voleu instal·lar-la\? Mostra «Reprodueix amb el Kodi» Mostra una opció per reproduir un vídeo amb el centre multimèdia Kodi + Reproductor emergent intel·ligent + Recorda la darrera mida i posició del reproductor emergent Cerca ràpida poc precisa La cerca poc precisa permet que el reproductor cerqui una posició més ràpidament amb menys precisió. Cerques de 5, 15 o 25 segons no hi funcionaran. Carrega les miniatures diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml index 421442971..75161746a 100644 --- a/app/src/main/res/values-ckb/strings.xml +++ b/app/src/main/res/values-ckb/strings.xml @@ -91,6 +91,7 @@ پەخشی ڕاستەوخۆ پشتگیری ناکرێ لەئێستادا به‌شداریت نەما له‌ كه‌ناڵ ناتوانرێ ئەم پەخشە کارپێبکرێ + بیرهاتنه‌وه‌ی شوێن و قه‌باره‌ی په‌نجه‌ره‌ی بچووک گێڕانەوەی کارپێکەر بۆکاتی پێش کێشە هیچیان بەسوودە بۆ کاتی گۆڕینی هێڵ بۆ داتای مۆبایل, لەگەڵ ئەوەشدا زۆربەی داگرتنەکان ڕاناگرێت @@ -341,6 +342,7 @@ تەواو بەدڵبوون ناتوانرێ مۆڵەت باربکرێ + بیرهاتنه‌وه‌ی كۆتا قه‌باره‌ و شوێنی په‌نجه‌ره‌ی بچووك دروستکردن ئەوە بزانە ئەم کردارە پێویستی بە هێڵێکی گران هەیە. \n @@ -585,7 +587,6 @@ پاشگه‌زبوونه‌وه‌ تراکەکان ڕێکخستنەکانی دەنگ - پیشاندانی ئەو ڤیدیۆیانەی سنوری تەمەنیان بۆ دانراوە. لە ڕێکخستنەکانەوە ڕێگەی پێدەدرێت. پرسیارت لێ دەکرێت بۆ شوێنی داگرتنی هەر پەڕگەیەک دواین کارپێکراو ناتوانرێ لیستی داگرتن دابنرێ diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 25c331f9e..be3447540 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -58,8 +58,7 @@ Automaticky přehrávat Přehrává video, když je NewPipe otevřen z jiné aplikace Obsah - Věkově omezený obsah - Zobrazit video s věkovým omezením. Změnit tuto volbu v budoucnu lze v \"Nastavení\". + Zobrazit věkově omezený obsah Živě Nebylo možné kompletně analyzovat stránku Začít klepnutím na \"Hledat\" @@ -121,6 +120,8 @@ Zobrazovat vyšší rozlišení Pouze některá zařízení dokáží přehrát 2K/4K videa Výchozí formát videa + Pamatovat si vlastnosti vyskakovacího okna + Pamatovat si poslední velikost a pozici vyskakovacího okna Režim vyskakovacího okna Odebírat Odebíráno @@ -600,7 +601,7 @@ Písně Toto je video s věkovým omezením. \n -\nPokud ho chcete vidět, povolte \"Věkově omezený obsah\" v Nastavení. +\nPokud ho chcete vidět, povolte \"%1$s\" v Nastavení. Ano, i zčásti shlédnutá videa Odstranit shlédnutá videa\? Odstranit shlédnutá @@ -608,7 +609,7 @@ \nJste se jisti\? Nelze zvrátit! Původní texty služeb budou viditelné u položek streamů U položek ukázat původní čas \"před\" - Omezený režim YouTube + Zapnout \"Omezený režim YouTube\" Od %s Vytvořil %s Miniatura avatara kanálu @@ -643,4 +644,12 @@ První akční tlačítko Zmenšit miniaturu videa zobrazenou v oznámení z poměru stran 16: 9 na 1: 1 (může způsobit zkreslení) Změnit poměr stran miniatury na 1:1 + Ukázat memory leaks + Zařazeno do fronty + Zařadit do fronty + Vymazat cookies, které NewPipe uloží, po vyřešení reCAPTCHA + Cookies reCAPTCHA byly vymazány + Vymazat cookies reCAPTCHA + YouTube poskytuje \"Omezený režim\", který skrývá potenciální obsahy pro dospělé. + Zobrazit obsah, i když je patrně nevhodný pro děti, protože odkazuje na věkové omezení (např. 18+). \ No newline at end of file diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 34573e745..2b85a2539 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -61,6 +61,8 @@ Lyst Mørkt Sort + Husk størrelse og placering af pop op + Husk sidste størrelse og placering af pop op-afspiller Brug hurtig og upræcis søgning Upræcis søgning lader afspilleren finde placeringer hurtigere, men mindre præcist Indlæs miniaturebilleder @@ -108,7 +110,6 @@ Føjet til pop op-afspilningskøen Indhold Aldersbegrænset indhold - Vis aldersbegrænsede videoer. Du kan tillade denne type videoer under Indstillinger. LIVE Downloads Downloads diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index ce4560f1e..d03b85f89 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -2,7 +2,7 @@ %1$s Aufrufe Veröffentlicht am %1$s - Keinen Stream-Player gefunden. VLC installieren\? + Kein Stream-Player gefunden. VLC installieren\? Installieren Abbrechen Im Browser öffnen @@ -10,7 +10,7 @@ Herunterladen Suchen Einstellungen - Meintest du: \"%1$s\"\? + Meintest du „%1$s“\? Teilen mit Browser auswählen Bildschirm drehen @@ -56,8 +56,7 @@ Konnte Webseite nicht analysieren Inhalt nicht verfügbar Inhalt - Altersbeschränkte Inhalte - Altersbeschränktes Video anzeigen. Spätere Änderungen sind in den Einstellungen möglich. + Altersbeschränkte Inhalte anzeigen Konnte Download-Menü nicht einrichten Live-Streams werden noch nicht unterstützt Konnte Webseite nicht vollständig analysieren @@ -129,7 +128,9 @@ Nur manche Geräte können Videos in 2K/4K abspielen Hintergrund Pop-up + Pop-up Eigenschaften merken Entfernt Tonspur bei manchen Auflösungen + Letzte Größe und Position des Pop-ups merken Gestensteuerung Helligkeit und Lautstärke mittels Gesten einstellen Suchvorschläge @@ -229,7 +230,7 @@ Kiosk-Seite Kiosk auswählen Kiosk - Tipp anzeigen, wenn der Hintergrundwiedergabe- oder Pop-up-Button „Details:“ im Video gedrückt wird + Tipp anzeigen, wenn der Hintergrundwiedergabe- oder Pop-up-Knopf „Details:“ im Video gedrückt wird In der Warteschlange der Hintergrundwiedergabe Neu und Heiß Halten, um zur Wiedergabeliste hinzuzufügen @@ -343,16 +344,19 @@ Die Überwachung von Speicherlecks kann dazu führen, dass die App beim Heap-Dumping nicht mehr reagiert Fehler außerhalb des Lebenszyklus melden Erzwingen der Meldung unzustellbarer Rx-Ausnahmen außerhalb des Lebenszyklus von Fragmenten oder Aktivitäten nach der Entsorgung - Importiere YouTube-Abonnements, indem du die Exportdatei herunterlädst: -\n -\n1. Gehe zu dieser URL: %1$s -\n2. Melde dich an, falls du dazu aufgefordert wirst. -\n3. Der Ladevorgang sollte beginnen (das ist die Exportdatei) - Importiere ein SoundCloud-Profil, indem du entweder die URL oder deine ID eingibst: -\n -\n1. Aktiviere den Desktop-Modus in einem Web-Browser (die Seite ist für mobile Geräte nicht verfügbar) -\n2. Gehe zu dieser URL: %1$s -\n3. Melde dich an, falls du dazu aufgefordert wirst + Importiere YouTube-Abonnements aus dem Google Takeout: +\n +\n1. Gehe zu dieser URL: %1$s +\n2. Melde dich an, falls du dazu aufgefordert wirst +\n3. Klicke auf \"Alle Daten enthalten\", dann auf \"Alle abwählen\", wähle dann nur \"Abonnements\" und klicke auf \"OK\" +\n4. Klicke auf \"Nächster Schritt\" und dann auf \"Export erstellen\" +\n5. Klicke auf die Schaltfläche \"Download\", nachdem sie erscheint und +\n6. Entpacke aus dem heruntergeladenen Takeout-Zip die .json-Datei (normalerweise unter \"YouTube und YouTube Music/subscriptions/subscriptions.json\") und importiere sie hier. + Importiere ein SoundCloud-Profil, indem die URL oder deine ID eingegeben wird: +\n +\n1. Aktiviere den Desktop-Modus in einem Web-Browser (die Seite ist für mobile Geräte nicht verfügbar) +\n2. Gehe zu dieser URL: %1$s +\n3. Melde dich an, falls du dazu aufgefordert wirst \n4. Kopiere die Profil-URL, zu der du weitergeleitet wurdest. yourID, soundcloud.com/yourid Keine Streams zum Download verfügbar @@ -398,7 +402,7 @@ Neuer Tab Tab wählen Gestensteuerung für Lautstärke - Player-Lautstärke über Gesten steuern + Verwende Gesten um die Abspielerlautstärke einzustellen Gestensteuerung für Helligkeit Player-Helligkeit über Gesten steuern Aktualisierungen @@ -420,7 +424,7 @@ Gitter Auto Ansicht wechseln - NewPipe-Update ist verfügbar! + Eine NewPipe-Aktualisierung ist verfügbar! Zum Herunterladen antippen Fertig Ausstehend @@ -481,7 +485,7 @@ Downloads starten Downloads anhalten Download-Ziel abfragen - Du wirst gefragt, wohin du jeden Download speichern willst + Du wirst gefragt, wo jede heruntergeladene Datei gespeichert werden soll Du wirst gefragt, wohin du jeden Download speichern willst. \nAktiviere diese Option, wenn du auf die externe SD-Karte herunterladen möchtest SAF verwenden @@ -490,7 +494,7 @@ Wiedergabepositionen löschen Alle Wiedergabepositionen löschen Alle Wiedergabepositionen löschen\? - Ändere die Downloadordner, damit sie wirksam werden + Wähle einen neuen Heruntergeladen-Ordner Dienst umschalten, aktuell ausgewählt: Standard-Kiosk Niemand schaut zu @@ -592,7 +596,7 @@ \nEs wird hoffentlich in einer zukünftigen Version unterstützt. Dieses Video ist altersbeschränkt. \n -\nAktiviere in den Einstellungen „Altersbeschränkte Inhalte“, falls du diese sehen möchtest. +\nAktiviere in den Einstellungen „%1$s“, falls du diese sehen möchtest.
Videos, die vor und nach dem Hinzufügen zur Wiedergabeliste angeschaut wurden, werden entfernt. \nBist du sicher\? Dies kann nicht rückgängig gemacht werden! Ja, und teilweise gesehene Videos @@ -600,7 +604,7 @@ Gesehene Videos entfernen\? Originalzeit vor Elementen anzeigen Originaltexte von Diensten werden in Stream-Elementen sichtbar sein - Eingeschränkter YouTube-Modus + Aktivieren des „Eingeschränkten Modus“ von YouTube Avatarbild des Kanals Erstellt von %s Von %s @@ -633,4 +637,14 @@ Den Player zu wechseln könnte deine Warteschlange überschreiben Bestätige das Leeren der Warteschlange Die aktive Wiedergabeliste wird ersetzt werden + Eingereiht + YouTube bietet einen „Eingeschränkten Modus“, der potenzielle Inhalte für Erwachsene ausblendet. + Speicherlecks anzeigen + Lösche Cookies, die NewPipe speichert, wenn du ein reCAPTCHA löst + reCAPTCHA-Cookies wurden gelöscht + reCAPTCHA-Cookies löschen + Zeige Inhalt, der möglicherweise unpassend für Kinder ist, da er eine Altersbeschränkung (wie z.B. 18+) hat. + Wiedergabe einreihen + Android kann die Farbe der Benachrichtigung entsprechend der Hauptfarbe in der Miniaturansicht anpassen (beachte, dass dies nicht auf allen Geräten verfügbar ist) + Benachrichtigung farblich anpassen \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index bcb05819d..f709f2a9b 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -19,7 +19,7 @@ Φάκελος λήψης βίντεο Τα ληφθέντα αρχεία βίντεο αποθηκεύονται εδώ Επιλέξτε φάκελο λήψης για αρχεία βίντεο - Διαδρομή λήψης αρχείων ήχου + Φάκελος λήψης ήχου Τα ληφθέντα αρχεία ήχου αποθηκεύονται εδώ Επιλέξτε φάκελο λήψης για αρχεία ήχου Προεπιλεγμένη ανάλυση @@ -41,16 +41,16 @@ Άλλα Αναπαραγωγή στο παρασκήνιο Σφάλμα δικτύου - Μικρογραφία προεπισκόπισης βίντεο + Μικρογραφία προεπισκόπησης βίντεο Αναπαραγωγή βίντεο, διάρκεια: Μικρογραφία εικόνας προφίλ του χρήστη Like Dislike Χρήση του Tor - (Πειραματικό) Αναγκάζει την κίνηση λήψης μέσω Tor για αυξημένη προστασία προσωπικών δεδομένων (η αναπαραγωγή δεν υποστηρίζεται ακόμη). - Δεν μπόρεσε να δημιουργηθεί ο φάκελος \'%1$s\' - Δημιουργήθηκε ο φάκελος \'%1$s\' - Δις + (Πειραματικό) Εξαναγκάζει τη λήψη μέσω του Tor για αυξημένη ιδιωτικότητα (η αναπαραγωγή δεν υποστηρίζεται ακόμη). + Αδυναμία δημιουργίας φακέλου λήψεων \'%1$s\' + Δημιουργήθηκε ο φάκελος λήψεων \'%1$s\' + δισ/ρια Άνοιγμα σε αναδυόμενο παράθυρο Εγγραφή Εγγεγραμμένος @@ -71,7 +71,7 @@ Τι συνέβη: Το σχόλιό σας (στα Αγγλικά): Λεπτομέρειες: - Αναφορά σφάλματος + Αναφορά Σφάλματος Βίντεο Ήχος Παύση @@ -87,18 +87,19 @@ Ιστορικό Ιστορικό Εμφάνιση πληροφοριών - Πατήστε \"Αναζήτηση\" για να ξεκινήσετε + Πατήστε \"Αναζήτηση\" για να ξεκινήσετε +\n Δε βρέθηκε πρόγραμμα αναπαραγωγής ροής δεδομένων (μπορείτε να εγκαταστήσετε το VLC για να κάνετε αναπαραγωγή). Λήψη του αρχείου ροής - Αφαίρεση του ήχου από κάποιες αναλύσεις + Αφαιρείται ο ήχος από κάποιες αναλύσεις Λειτουργία αναδυόμενου παραθύρου - Απεγγραφή από το κανάλι + Το κανάλι διαγράφηκε Αδύνατη η αλλαγή της εγγραφής Αδύνατη η ενημέρωση της εγγραφής Κύριο Συνδρομές Αγαπημένες λίστες αναπαραγωγής - Νέα + Τι νέο υπάρχει Στο παρασκήνιο Αναδυόμενο παράθυρο Προσθήκη σε @@ -107,39 +108,40 @@ Προεπιλεγμένη ανάλυση αναδυόμενου παραθύρου Εμφάνιση υψηλότερων αναλύσεων Προεπιλεγμένη μορφή βίντεο + Ενθύμιση τις ιδιότητες του αναδυόμενου παραθύρου + Ενθύμιση του τελευταίου μεγέθους και θέσης του παραθύρου Χρήση γρήγορης ανακριβούς αναζήτησης Η μην ακριβής αναζήτηση επιτρέπει στην εφαρμογή να αναζητεί θέσεις στο βίντεο γρηγορότερα με μειωμένη ακρίβεια. Δε λειτουργεί για διαστήματα των 5, 15 ή 25 δευτερολέπτων. Φόρτωση μικρογραφιών - Με την απενεργοποίηση δε φορτώνονται οι μικρογραφίες, χρησιμοποιώντας λιγότερα δεδομένα και μνήμη. Οι αλλαγές σβήνουν τις προσωρινά αποθηκευμένες εικόνες στη μνήμη και στον δίσκο. + Με την απενεργοποίηση δε φορτώνονται οι μικρογραφίες, εξοικονομώντας δεδομένα και μνήμη. Οι αλλαγές σβήνουν τις προσωρινά αποθηκευμένες εικόνες στη μνήμη και στον δίσκο. Εκκαθαρίστηκε η προσωρινή μνήμη εικόνων Εκκαθάριση προσωρινά αποθηκευμένων μεταδεδομένων Αφαίρεση όλων των προσωρινά αποθηκευμένων δεδομένων ιστοσελίδων Η προσωρινή μνήμη μεταδεδομένων εκκαθαρίστηκε Αυτόματη πρόσθεση της επόμενης ροής στην ουρά - Συνεχίστε να τερματίζετε (μη επαναλαμβανόμενη) τη σειρά αναπαραγωγής προσθέτοντας μια σχετική ροή + Συνέχεια της τρέχουσας (μη επαναλαμβανόμενης) ουράς μετά τη λήξη της, με την προσθήκη μιας σχετικής ροής Έλεγχος αναπαραγωγής με χειρονομίες Χρήση χειρονομιών για τον έλεγχο της φωτεινότητας και της έντασης ήχου Εμφάνιση προτάσεων ενώ κάνετε αναζήτηση Αποθήκευση αναζητήσεων στη συσκευή Προβολή Ιστορικού - Κρατήστε ιστορικό των βίντεο που έχετε δει - Συνέχεια αναπαραγωγής + Κρατήστε ιστορικό των αναπαραχθέντων βίντεο + Ανάκτηση αναπαραγωγής Συνέχιση της αναπαραγωγής έπειτα από διακοπές (π.χ. κλήσεις) - Εμφάνιση της βοήθειας \"Πιέστε παρατεταμένα για πρόσθεση\" - Εμφάνιση υπόδειξης όταν πατηθεί το κουμπί Παρασκηνίου ή Αναδυόμενου παραθύρου στη σελίδα λεπτομερειών του βίντεο + Εμφάνιση επεξήγησης του \"Πιέστε παρατεταμένα για προσθήκη\" + Εμφάνιση υπόδειξης όταν πατηθεί το κουμπί παρασκηνίου ή αναδυόμενου παραθύρου στη σελίδα λεπτομερειών του βίντεο Προεπιλεγμένη χώρα περιεχομένου Υπηρεσία - Συσκευή Αναπαραγωγής + Αναπαραγωγός Συμπεριφορά - Ιστορικό και προσωρινή αποθήκευση + Ιστορικό και προσωρινή μνήμη Αναδυόμενο παράθυρο - Απασφαλμάτωση + Αποσφαλμάτωση Αναπαραγωγή σε αναδυόμενο παράθυρο Προστέθηκε στη λίστα αναπαραγωγής παρασκηνίου Προστέθηκε στη λίστα αναπαραγωγής αναδυόμενου παραθύρου Περιεχόμενο - Περιεχόμενο περιορισμένης ηλικίας - Εμφάνιση βίντεο με περιορισμό ηλικίας. Μελλοντικές αλλαγές είναι δυνατές από τις ρυθμίσεις. + Εμφάνιση περιεχομένου περιορισμένης ηλικίας Ζωντανά Αναφορά σφαλμάτων Κανάλια @@ -158,7 +160,7 @@ Μόνο μία φορά Αρχείο Ειδοποίηση NewPipe - Ειδοποιήσεις για την αναπαραγωγή Παρασκηνίου και Αναδυόμενου Παραθύρου + Ειδοποιήσεις αναπαραγωγής παρασκηνίου και αναδυόμενου παραθύρου [Άγνωστο] Αλλαγή προσανατολισμού Αλλαγή σε Παρασκήνιο @@ -166,10 +168,10 @@ Αλλαγή σε Κύριο Εισαγωγή βάσης δεδομένων Εξαγωγή βάσης δεδομένων - Θα παρακάμψει το τρέχον ιστορικό και εγγραφές σας + Παρακάμπτει το τρέχον ιστορικό και τις εγγραφές σας Εξαγωγή ιστορικού, εγγραφών και λιστών αναπαραγωγής Εκκαθάριση ιστορικού προβολής - Διαγράφει το ιστορικό των ροών που έχουν αναπαραχθεί + Διαγράφει το ιστορικό των αναπαραχθέντων ροών και των θέσεων αναπαραγωγής Διαγραφή ολόκληρου του ιστορικού προβολής; Το στορικό προβολής διαγράφηκε. Διαγραφή ιστορικού αναζητήσεων @@ -177,25 +179,25 @@ Διαγραφή ολόκληρου του ιστορικού αναζητήσεων; Το ιστορικό αναζητήσεων διαγράφηκε. Δεν ήταν δυνατή η φόρτωση όλων των εικονιδίων - Δεν ήταν δυνατή η αποκρυπτογράφηση της υπογραφής του URL του βίντεο + Δεν ήταν δυνατή η αποκρυπτογράφηση της υπογραφής της URL του βίντεο Δεν ήταν δυνατή η ανάλυση του ιστοτόπου Δεν ήταν δυνατή η ανάλυση ολόκληρου του ιστοτόπου Το περιεχόμενο δεν είναι διαθέσιμο Δεν ήταν δυνατή η ρύθμιση του μενού λήψεων - Η Ζωντανή Ροή δεν υποστηρίζεται ακόμα - Δεν ήταν δυνατή η λήψη καμίας ροής + Οι ζωντανές ροές δεν υποστηρίζονται ακόμα + Δεν ήταν δυνατή η λήψη οποιασδήποτε ροής Δεν ήταν δυνατή η φόρτωση της εικόνας - Η εφαρμογή κράσαρε + Η εφαρμογή κατέρρευσε Δεν ήταν δυνατή η αναπαραγωγή αυτής της ροής Συνέβη ένα μη ανακτήσιμο σφάλμα στη συσκευή αναπαραγωγής Ανάκτηση από σφάλμα της συσκευής αναπαραγωγής Οι εξωτερικές συσκευές αναπαραγωγής δεν υποστηρίζουν αυτού του είδους συνδέσμους - Μη έγκυρο URL + Μη έγκυρη URL Δε βρέθηκαν ροές βίντεο Δε βρέθηκαν ροές ήχου Δεν υπάρχει αυτός ο φάκελος Δεν υπάρχει το αρχείο/πηγή περιεχομένου - Το αρχείο δεν υπάρχει ή δεν έχουμε επαρκή εξουσιοδότηση για να διαβάσουμε ή να γράψουμε σε αυτό + Το αρχείο δεν υπάρχει ή δεν υπάρχει επαρκής εξουσιοδότηση ανάγνωσης ή εγγραφής σε αυτό Το όνομα αρχείου δεν μπορεί να είναι κενό Προέκυψε ένα σφάλμα: %1$s Δεν υπάρχουν διαθέσιμες ροές για λήψη @@ -208,13 +210,13 @@ Δεν υπάρχει τίποτα εδώ Σύρετε για ταξινόμηση Προσπάθεια εκ νέου - Παραχώρηση πρόσβασης πρώτα στον αποθηκευτικό χώρο - χιλ - Εκ - Κανένας εγγεγραμένος χρήστης + Παραχώρηση πρώτα πρόσβασης στον αποθηκευτικό χώρο + χιλ. + εκ/ρια + Κανένας συνδρομητής - %s εγγεγραμένος χρήστης - %s εγγεγραμένοι χρήστες + %s συνδρομητής + %s συνδρομητές Καμία προβολή @@ -223,8 +225,8 @@ Κανένα βίντεο - %s Βίντεο - %s Βίντεο + %s βίντεο + %s βίντεο Εκκίνηση Αναπαραγωγή @@ -234,7 +236,7 @@ Άθροισμα ελέγχου Αγνόηση Μετονομασία - Νέα αποστολη + Νέα αποστολή ΟΚ Όνομα αρχείου Νήματα @@ -243,40 +245,40 @@ Λήψη NewPipe Παρακαλώ περιμένετε… Αντιγράφηκε στο πρόχειρο - Παρακαλώ ορίστε έναν διαθέσιμο φάκελο λήψεων αργότερα στις ρυθμίσεις + Παρακαλώ ορίστε έναν φάκελο λήψεων αργότερα στις ρυθμίσεις Αυτή η άδεια είναι απαραίτητη για \nτο άνοιγμα αναδυόμενων παραθύρων 1 αντικείμενο διαγράφηκε. - Πρόκληση reCAPTCHA - Ζητήθηκε πρόκληση reCAPTCHA + Δοκιμασία reCAPTCHA + Ζητήθηκε δοκιμασία reCAPTCHA Επιτρεπόμενοι χαρακτήρες σε ονόματα αρχείων Οι μη έγκυροι χαρακτήρες αντικαθίστανται με αυτήν την τιμή Αντικαταστάτης χαρακτήρας Οι περισσότεροι ειδικοί χαρακτήρες - Δεν υπάρχει εφαρμογή εγκατεστημένη για την αναπαραγωγή αυτού του αρχείου + Δεν υπάρχει εγκατεστημένη εφαρμογή για την αναπαραγωγή αυτού του αρχείου Σχετικά με το NewPipe Περί Άδειες Τρίτων © %1$s από %2$s υπό %3$s Δεν ήταν δυνατή η φόρτωση της άδειας Περί - Συνεισφέροντες + Συντελεστές Ανοιχτού κώδικα, ελαφριά εφαρμογή Android, για την αναπαραγωγή πολυμέσων από το διαδίκτυο. Συνεισφέρετε - Αν έχετε ιδέες για μετάφραση, αλλαγή σχεδιασμού, εκκαθάριση ή ριζικές αλλαγές κώδικα της εφαρμογής—η βοήθεια σας είναι πάντα ευπρόσδεκτη. Όσο περισσότερη έχουμε, τόσο καλύτεροι γινόμαστε! + Αν έχετε ιδέες για μετάφραση, αλλαγή σχεδιασμού, εκκαθάριση ή ριζικές αλλαγές κώδικα της εφαρμογής, η βοήθεια σας είναι πάντα ευπρόσδεκτη. Όσο περισσότερη έχουμε, τόσο καλύτεροι γινόμαστε! Δείτε το στο GitHub Κάντε μια δωρεά - Το NewPipe αναπτύσσεται από εθελοντές που δαπανούν τον ελεύθερο χρόνο τους για να σας προσφέρουν τη βέλτιστη δυνατή εμπειρία χρήστη. Δώστε πίσω για να βοηθήσετε τους προγραμματιστές του NewPipe να το κάνουν ακόμα καλύτερο, όσο απολαμβάνουν ένα φλιτζάνι καφέ. - Προσφέρτε + Το NewPipe αναπτύσσεται από εθελοντές που δαπανούν τον ελεύθερο χρόνο τους για να σας προσφέρουν τη βέλτιστη δυνατή εμπειρία χρήστη. Ανταποδώστε το, για να βοηθήσετε τους προγραμματιστές του NewPipe να το κάνουν ακόμα καλύτερο, όσο απολαμβάνουν ένα φλιτζάνι καφέ. + Προσφέρετε Ιστότοπος Επισκευτείτε τον ιστότοπο του NewPipe για περισσότερες πληροφορίες και νέα. - Η πολιτική ιδιωτικού απόρρητου του NewPipe - Το NewPipe παίρνει πολύ σοβαρά το ιδιωτικό σας απόρρητο. Έτσι, η εφαρμογή αυτή δεν συλλέγει δεδομένα από εσάς χωρίς τη συγκατάθεσή σας. + Πολιτική ιδιωτικού απόρρητου του NewPipe + Το NewPipe παίρνει πολύ σοβαρά την ιδιωτικότητα σας. Έτσι, η εφαρμογή αυτή δεν συλλέγει δεδομένα από εσάς χωρίς τη συγκατάθεσή σας. \nΗ πολιτική ιδιωτικού απόρρητου του NewPipe εξηγεί λεπτομερώς ποια δεδομένα αποστέλλονται και αποθηκεύονται όταν επιλέγετε να στείλετε μια αναφορά σφαλμάτων. - Διαβάστε την πολιτική ιδιωτικού απόρρητου + Ανάγνωση της πολιτικής ιδιωτικού απόρρητου Η άδεια του NewPipe Το NewPipe είναι copylelft ελεύθερο λογισμικό: Μπορείτε να το χρησιμοποιήσετε, να το μελετήσετε, να το μοιραστείτε και να το βελτιώσετε κατά βούληση. Ειδικότερα, μπορείτε να το αναδιανείμετε ή/και να το τροποποιήσετε υπό την άδεια GNU General Public Licence όπως αυτή εκδόθηκε από το Free Software Foundation, είτε υπό την έκδοση 3 της Άδειας είτε (προεραιτικά) υπό οποιαδήποτε μεταγενέστερη άδεια. - Διαβάστε την άδεια + Ανάγνωση της άδειας Αναζητήθηκαν Προβλήθηκαν Το ιστορικό έχει απενεργοποιηθεί @@ -291,7 +293,7 @@ Περιεχόμενο της κεντρικής σελίδας Κενή σελίδα Σελίδα περιπτέρου - Σελίδα εγγραφών + Σελίδα συνδρομών Σελίδα καναλιών Επιλέξτε ένα κανάλι Δεν έχει γίνει εγγραφή σε κάποιο κανάλι ακόμα @@ -306,31 +308,31 @@ Αφαίρεση Λεπτομέρειες Ρυθμίσεις ήχου - Πιέστε για να προστεθεί στην ουρά - Εκκίνηση Αναπαραγωγής εδώ + Πιέστε παρατεταμένα για να προστεθεί στην ουρά + Εκκίνηση αναπαραγωγής εδώ Εκκίνηση αναπαραγωγής στο παρασκήνιο Εκκίνηση αναπαραγωγής σε ένα αναδυόμενο παράθυρο - Άνοιγμα Συρταριού - Κλείσιμο Συρταριού - Κάτι θα παιχτεί εδω σύντομα ;D + Άνοιγμα συρταριού + Κλείσιμο συρταριού + Κάτι θα εμφανιστεί εδώ σύντομα ;D Τοπ 50 Καινούρια και δημοφιλή - Προτιμώμενη ενέργεια ανοίγματος + Προτιμώμενη ενέργεια κοινοποίησης Προεπιλεγμένη ενέργεια για το άνοιγμα περιεχομένου — %s Συσκευή αναπαραγωγής βίντεο - Αναπαραγωγή Παρασκηνίου - Αναπαραγωγή σε Αναδυόμενο Παράθυρο + Αναπαραγωγή παρασκηνίου + Αναπαραγωγή σε αναδυόμενο παράθυρο Πάντα ερώτηση Γίνεται λήψη πληροφοριών… Γίνεται φόρτωση του ζητούμενου περιεχομένου - Νέα Λίστα Αναπαραγωγής + Νέα λίστα αναπαραγωγής Διαγραφή Μετονομασία Όνομα - Προσθήκη στη Λίστα + Προσθήκη σε λίστα αναπαραγωγής Ορισμός ως μικρογραφία λίστας αναπαραγωγής - Προσθήκη Σελιδοδείκτη στη Λίστα - Διαγραφή Σελιδοδείκτη + Προσθήκη σελιδοδείκτη στη λίστα + Διαγραφή σελιδοδείκτη Διαγραφή αυτής της λίστας αναπαραγωγής; Η λίστα αναπαραγωγής δημιουργήθηκε Προστέθηκε στη λίστα αναπαραγωγής @@ -354,10 +356,10 @@ Εισαγωγή αρχείου Προηγούμενη εξαγωγή Δεν ήταν δυνατή η εισαγωγή των εγγραφών - Δεν ήταν δυνατή η εισαγωγή των εγγραφών + Δεν ήταν δυνατή η εξαγωγή των εγγραφών Κάντε εισαγωγή των εγγραφών σας στο YouTube κατεβάζοντας το εξής αρχείο: \n -\n1. Πλοηγηθήτε στο: %1$s +\n1. Πλοηγηθείτε στο: %1$s \n2. Εισέλθετε στο λογαριασμό σας, όταν σας ζητηθεί \n3. Η λήψη του αρχείου των εγγραφών σας θα ξεκινήσει Για να εισάγετε τον λογαριασμό SoundCloud σας, πληκτρολογήστε τον σύνδεσμο ή το ID σας: @@ -365,7 +367,7 @@ \n1. Ενεργοποιήστε τη λειτουργία \"Desktop mode\" στον φυλλομετρητή σας (καθώς η ιστοσελίδα δεν είναι διαθέσιμη για κινητά) \n2. Πλοηθηθείτε στο %1$s \n3. Εισέλθετε στο λογαριασμό σας, όταν σας ζητηθεί -\n4. Αντιγράψτε τον σύνδεσμο του λογαριαμού στον οποίο ανακατευθυνθήκατε. +\n4. Αντιγράψτε τον σύνδεσμο του λογαριασμού στον οποίο ανακατευθυνθήκατε. Αυτή η διαδικασία μπορεί να χρησιμοποιήσει μεγάλο όγκο δεδομένων. \n \nΕπιθυμείτε να συνεχίσετε; @@ -373,16 +375,16 @@ Τέμπο Τόνος Ενέργεια κατά τη μετάβαση σε άλλη εφαρμογή — %s - Σελίδα Ροής + Σελίδα ροής Δημοφιλή Αναφορά σφαλμάτων εκτός κύκλου ζωής Το όνομα χρήστη σας, soundcloud.com/όνομαχρήστη - Αποσύνδεση (μπορεί να προκαλέσει παραμόρφωση) - Επιτάχυνση αναπαραγωγής κατά τη διάρκεια σιωπής + Απαγκίστρωση (μπορεί να προκαλέσει παραμόρφωση) + Γρήγορη αναπαραγωγή κατά τη διάρκεια της σίγασης Βήμα Επαναφορά - Προς συμμόρφωση με τον Ευρωπαϊκό Γενικό Κανονισμό για την Προστασία Δεδομένων (GDPR), σας επιστούμε την προσοχή στην πολιτική προστασίας προσωπικών δεδομένων του NewPipe. Παραλούμε, διαβάστε την προσεκτικά. -\nΘα πρέπει να την αποδεχτέιτε προκειμένου να μας αποστείλετε την αναφορά σφάλματος. + Προς συμμόρφωση με τον Ευρωπαϊκό Γενικό Κανονισμό για την Προστασία Δεδομένων (GDPR), σας εφιστούμε την προσοχή στην πολιτική προστασίας προσωπικών δεδομένων του NewPipe. Παραλούμε, διαβάστε την προσεκτικά. +\nΘα πρέπει να την αποδεχτείτε προκειμένου να μας αποστείλετε την αναφορά σφάλματος. Αποδοχή Απόρριψη Χωρίς όριο @@ -394,9 +396,9 @@ Απεγγραφή Νέα Καρτέλα Επιλογή Καρτέλας - Ρυθμίσεις χειρονομιών ήχου - Χρησιμοποιήστε χειρονομίες για τον έλεγχο έντασης του ήχου - Ρυθμίσεις χειρονομιών φωτεινότητας + Έλεγχος ήχου με χειρονομιές + Χρησιμοποιήστε χειρονομίες για τον έλεγχο της έντασης του ήχου + Έλεγχος φωτεινότητας με χειρονομίες Χρησιμοποιήστε χειρονομίες για τον έλεγχο φωτεινότητας Ενημερώσεις Συμβάντα @@ -405,51 +407,51 @@ Ειδοποίηση για νεότερη έκδοση του NewPipe Εξωτερική μνήμη αποθήκευσης μη διαθέσιμη Η αποθήκευση στην SD κάρτα δεν είναι δυνατή. Επαναφορά στην αρχική τοποθεσία λήψης; - Δεν ήταν δυνατή η ανάγνωση αποθηκευμένων καρτελών, επομένως χρήση προεπιλεγμένων καρτελών + Δεν ήταν δυνατή η ανάγνωση των αποθηκευμένων καρτελών. Θα γίνει χρήση των προεπιλεγμένων Επαναφορά προεπιλεγμένων ρυθμίσεων - Θέλετε να επαναφέρετε τις προεπιλογές; - Ο αριθμός συνδρομητών δεν είναι διαθέσιμος + Θέλετε να επαναφέρετε τις προεπιλεγμένες ρυθμίσεις; + Το πλήθος των συνδρομητών δεν είναι διαθέσιμο Ποιές καρτέλες θα εμφανίζονται στην αρχική σελίδα Επιλογή Συνέδρια Ενημερώσεις Εμφάνιση ειδοποίησης όταν μια υπάρχει μια νεότερη έκδοση - Λειτουργία προβολής ως λίστα + Προβολή λίστας Λίστα Πλέγμα Αυτόματα Αλλαγή τρόπου προβολής - Νέα έκδοση του NewPipe είναι διαθέσιμη! + Μια νέα έκδοση του NewPipe είναι διαθέσιμη! Πατήστε για λήψη Ολοκληρώθηκε Εκκρεμεί - Παύση - στην ουρά - Μετεπεξεργασία + σε παύση + σε ουρά + σε μετεπεξεργασία Ουρά - Η δράση απορρίφθηκε από το σύστημα + Η ενέργεια απορρίφθηκε από το σύστημα Η λήψη απέτυχε Η λήψη ολοκληρώθηκε - %s λήψεις ολοκρηρώθηκαν + %s λήψεις ολοκληρώθηκαν Δημιουργία μοναδικού ονόματος Αντικατάσταση Ένα αρχείο με αυτό το όνομα υπάρχει ήδη - Ένα αρχείο που έχει ληφθεί με αυτό το όνομα υπάρχει ήδη + Ένα ληφθέν αρχείο με αυτό το όνομα υπάρχει ήδη Υπάρχει μια λήψη σε εξέλιξη με αυτό το όνομα Εμφάνιση σφάλματος - Κωδικός + Κώδικας Δεν είναι δυνατή η δημιουργία του φακέλου προορισμού Δεν είναι δυνατή η δημιουργία του αρχείου - Η αδειοδότηση απορρίφθηκε απο το σύστημα + Η άδεια απορρίφθηκε από το σύστημα Δεν ήταν δυνατή η δημιουργία ασφαλούς σύνδεσης Αδυναμία εύρεσης του εξυπηρετητή Αδυναμία σύνδεσης με τον εξυπηρετητή - Ο εξυπηρετητής δεν μπορεί να στείλει τα δεδομένα - Ο εξυπηρετητής δέν υποστηρίζει πολυνηματικές λήψεις, ξαναπροσπαθήστε με @string/msg_threads = 1 - Δεν βρέθηκε - Μετεπεξεργασία απέτυχε + Ο εξυπηρετητής δεν στέλνει δεδομένα + Ο εξυπηρετητής δεν υποστηρίζει πολυνηματικές λήψεις, ξαναπροσπαθήστε με @string/msg_threads = 1 + Δε βρέθηκε + Η μετεπεξεργασία απέτυχε Διακοπή - Μέγιστες επαναπροσπάθειες + Μέγιστος αριθμός προσπαθειών Μέγιστος αριθμός προσπαθειών προτού γίνει ακύρωση της λήψης Διακοπή σε δίκτυα με ογκοχρέωση Χρήσιμο κατά τη μετάβαση σε δεδομένα κινητής τηλεφωνίας, αν και ορισμένες λήψεις δεν μπορούν να ανασταλούν @@ -459,7 +461,7 @@ Χωρίς σχόλια Δεν ήταν δυνατή η φόρτωση σχολίων Κλείσιμο - Συνέχιση αναπαραγωγής + Ανάκτηση αναπαραγωγής Επαναφορά της τελευταίας θέσης αναπαραγωγής Θέσεις στις λίστες Εμφάνιση ενδείξεων θέσης αναπαραγωγής στις λίστες @@ -467,17 +469,17 @@ Οι θέσεις αναπαραγωγής διαγράφηκαν. Το αρχείο μετακινήθηκε ή διαγράφηκε δεν είναι δυνατή η αντικατάσταση του αρχείου - Υπάρχει μια εκκρεμή λήψη με αυτό το όνομα - Το NewPipe έκλεισε, ενώ εργάζονται στο αρχείο - Δεν είναι αρκετός ο χώρος στη συσκευή + Υπάρχει μια εκκρεμής λήψη με αυτό το όνομα + Το NewPipe τερματίστηκε ενώ επεξεργάζονταν το αρχείο + Δεν υπάρχει αρκετός χώρος στη συσκευή Η πρόοδος χάθηκε, επειδή το αρχείο διαγράφηκε Λήξη χρονικού ορίου σύνδεσης Θέλετε να διαγράψετε το ιστορικό λήψεων σας ή να διαγράψετε όλα τα αρχεία που έχετε λάβει; Περιορισμός ουράς λήψης - Μια λήψη θα εκτελεστεί ταυτόχρονα + Μόνο μια λήψη θα εκτελείται κάθε φορά Έναρξη λήψεων Παύση λήψεων - Ερώτηση που να γίνει η λήψη + Ερώτηση πού να γίνει η λήψη Θα ερωτηθείτε πού να αποθηκεύσετε κάθε λήψη Θα σας ζητηθεί πού να αποθηκεύσετε κάθε λήψη. \nΕπιλέξτε SAF αν θέλετε να κατεβάσετε σε μια εξωτερική κάρτα SD @@ -488,7 +490,7 @@ Διαγράφει όλες τις θέσεις αναπαραγωγής Να διαγραφούν όλες οι θέσεις αναπαραγωγής; Αλλαγή των φακέλων λήψης για να τεθούν σε ισχύ - Εναλλαγή υπηρεσιών, επιλεγμένη αυτήν τη στιγμή: + Εναλλαγή υπηρεσιών, επιλεγμένη αυτή τη στιγμή: Κανείς δεν παρακολουθεί %s παρακολουθεί @@ -499,15 +501,15 @@ %s ακροατής %s ακροατές - Η γλώσσα θα αλλάξει μόλις θα επανεκκινηθεί η εφαρμογή. + Η γλώσσα θα αλλάξει αφού επανεκκινηθεί η εφαρμογή. Προεπιλεγμένο περίπτερο - Υποστηρίζονται μόνο HTTPS URLς - Τοπικό - Προστέθηκε πρόσφατα + Μόνο HTTPS σύνδεσμοι υποστηρίζονται + Τοπικά + Προστέθηκαν πρόσφατα Δημιουργήθηκε αυτόματα (δεν βρέθηκε χρήστης μεταφόρτωσης) - Ανάκτηση + σε ανάκτηση Δεν είναι δυνατή η ανάκτηση αυτής της λήψης - Διάρκεια fastforward και rewind + Διάρκεια αναζήτησης fast-forward και rewind Προεπιλογή συστήματος Βίντεο Εμφάνιση αποτελεσμάτων για: %s @@ -520,15 +522,15 @@ Δημιουργήθηκε από %s Σελίδα λίστας αναπαραγωγής Από %s - Διαθέσιμο σε ορισμένες υπηρεσίες, είναι συνήθως πολύ πιο γρήγορο, αλλά μπορεί να επιστρέψει έναν περιορισμένο αριθμό αντικειμένων και συχνά ελλιπείς πληροφορίες (π.χ. χωρίς διάρκεια, τύπο αντικειμένου, χωρίς ζωντανή κατάσταση). + Διαθέσιμο σε ορισμένες υπηρεσίες, είναι συνήθως πολύ πιο γρήγορο, αλλά μπορεί να επιστρέψει έναν περιορισμένο αριθμό αντικειμένων και συχνά ελλειπείς πληροφορίες (π.χ. χωρίς διάρκεια, τύπο αντικειμένου). Λήψη από ειδική ροή όταν είναι διαθέσιμη Να γίνεται πάντα ενημέρωση - Ώρα μετά την τελευταία ενημέρωση πριν από μια συνδρομή θεωρείται ξεπερασμένη — %s + Χρόνος μετά την τελευταία ενημέρωση πριν μια συνδρομή θεωρηθεί ξεπερασμένη — %s Όριο ενημέρωσης ροής Ροή Εμφάνιση μόνο μη ομαδοποιημένων συνδρομών Νέα - Θέλετε να διαγράψετε αυτήν την ομάδα; + Θέλετε να διαγράψετε αυτή την ομάδα; Κενό όνομα ομάδας Δεν έχει επιλεγεί συνδρομή Επιλέξτε συνδρομές @@ -549,13 +551,13 @@ Τραγούδια Αυτό το βίντεο έχει περιορισμό ηλικίας. \n -\nΕνεργοποιήστε το \"Περιεχόμενο περιορισμένης ηλικίας\" στις ρυθμίσεις εάν θέλετε να το δείτε. - Λειτουργία περιορισμένης πρόσβασης στο YouTube +\nΕνεργοποιήστε το \"%1$s\" στις ρυθμίσεις εάν θέλετε να το δείτε. + Λειτουργία περιορισμένης πρόσβασης του YouTube Ειδοποίηση Δεν ήταν δυνατή η αναγνώριση της διεύθυνσης URL. Άνοιγμα με άλλη εφαρμογή; Αυτόματη ουρά - Η ουρά ενεργού παίκτη θα αντικατασταθεί - Η εναλλαγή από έναν παίκτη σε άλλο μπορεί να αντικαταστήσει την ουρά σας + Η ουρά του ενεργού αναπαραγωγού θα αντικατασταθεί + Η εναλλαγή από έναν αναπαραγωγό σε άλλον, μπορεί να αντικαταστήσει την ουρά σας Ζητήστε επιβεβαίωση πριν από την εκκαθάριση μιας ουράς Τίποτα Ανάμιξη @@ -567,7 +569,76 @@ Κουμπί τρίτης ενέργειας Κουμπί δεύτερης ενέργειας Κουμπί πρώτης ενέργειας - Κλιμάκωση της μικρογραφίας βίντεο που εμφανίζεται στην ειδοποίηση από 16:9 σε αναλογία διαστάσεων 1:1 (μπορεί να προκαλέσει στρεβλώσεις) + Κλιμάκωση της μικρογραφίας βίντεο που εμφανίζεται στην ειδοποίηση από 16:9 σε αναλογία διαστάσεων 1:1 (μπορεί να προκαλέσει παραμορφώσεις) Κλιμάκωση μικρογραφίας σε αναλογία διαστάσεων 1:1 Φόρτωση + Πιστεύετε ότι η ροή φορτώνει πολύ αργά; Δοκιμάστε να ενεργοποιήσετε τη γρήγορη φόρτωση (από τις ρυθμίσεις ή το παρακάτω κουμπί). +\n +\nΤο NewPipe προσφέρει δύο στρατηγικές φόρτωσης: +\n- Λήψη ολόκληρου του καναλιού της συνδρομής, η οποία είναι αργή αλλά πλήρης. +\n- Χρήση ενός αποκλειστικού τελικού σημείου υπηρεσίας, η οποία είναι γρήγορη αλλά συνήθως όχι πλήρης. +\n +\nΤο YouTube είναι ένα παράδειγμα χρήσης της δεύτερης μεθόδου. +\n +\nΣυνεπώς επιλέγετε ανάλογα: ταχύτητα ή ακριβείς πληροφορίες. + Η ροή ανανεώθηκε: %s + Φόρτωση ροής… + Επεξεργασία ροής… + + %d επιλέχθηκε + %d επιλέχθηκαν + + Δεν φορτώθηκε: %d + Οι ομάδες του καναλιού + + %d ημέρα + %d ημέρες + + + %d ώρα + %d ώρες + + + %d λεπτό + %d λεπτά + + + %d δευτερόλεπτο + %d δευτερόλεπτα + + Λόγω περιορισμών του ExoPlayer, η διάρκεια αναζήτησης ορίστηκε στα %d δευτερόλεπτα + Ναι. Και τα μερικώς θεαθέντα βίντεο + Τα βίντεο που εθεάθησαν πριν και αφού προστέθηκαν στη λίστα αναπαραγωγής θα απομακρυνθούν +\nΕίστε σίγουρος; Δεν μπορεί να αναιρεθεί! + Απομάκρυνση θεαθέντων βίντεο; + Απομάκρυνση όσων θεάθησαν + Γλώσσα εφαρμογής + Επιλογή μιας instance + Διαγράφηκαν %1$d λήψεις + Διαγραφή ληφθέντων αρχείων + Εκκαθάριση ιστορικού λήψεων + Ποτέ + Μόνο με Wi-Fi + Αυτόματη έναρξη αναπαραγωγής — %s + Τα αυθεντικά κείμενα των υπηρεσιών θα εμφανίζονται στα αντικείμενα ροής + Εμφάνιση διαρροών μνήμης + Αποσίγαση + Προστέθηκε στην ουρά + Προσθήκη στην ουρά + Πιο αγαπημένα + Παρακαλούμε ελέγξτε αν το πρόβλημα σας έχει ήδη αναφερθεί. Οι διπλές αναφορές μας στερούν το χρόνο που θα μπορούσαμε να διαθέσουμε για την επίλυση του προβλήματος. + Εκκαθάριση των cookies που αποθηκεύει η εφαρμογή όταν λύνετε ένα reCAPTCHA + Τα reCAPTCHA cookies εκκαθαρίστηκαν + Εκκαθάριση reCAPTCHA cookies + Το YouTube διαθέτει \"Περιορισμένη Λειτουργία\" η οποία κρύβει πιθανώς ακατάλληλο περιεχόμενο. + Εμφάνιση πιθανώς ακατάλληλου περιεχομένου (18+). + Το instance υπάρχει ήδη + Αδυναμία πιστοποίησης του instance + Προσθέστε την URL του instance + Προσθήκη instance + Βρείτε τα instances που σας αρέσουν στο %s + Επιλογή των αγαπημένων σας PeerTube instances + Εμφάνιση αυθεντικού παρελθόντος χρόνου στα αντικείμενα + PeerTube instances + Χρωματισμός ειδοποιήσεων \ No newline at end of file diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 000000000..a6b3daec9 --- /dev/null +++ b/app/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 7d9d0fd87..2172ec157 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -9,7 +9,7 @@ Elŝuti Serĉi Agordoj - Ĉu vi signifis: %1$s\? + Ĉu vi signifis \"%1$s\"\? Konigi kun Elekti retumilon turno @@ -64,7 +64,8 @@ Signali eraron Filmeto Reprovi - Premi \"Serĉi\" por komenci + Premi \"Serĉi\" por komenci +\n Neniu elsendlflua ludilo trovita (instalu VLC por ludi ĝin). Malfermi en ŝprucfenestran modon Forigas aŭdon ĉe kelkaj rezolucioj @@ -90,13 +91,14 @@ Nur kelkaj aparatoj povas ludi 2K / 4K filmetojn Defaŭlta fomato de filmeto Nigra + Memoru ŝprucfenestran grandecon kaj pozicion + Memoru lastan grandecon kaj pozicion de ŝprucfenestro Uzi rapide, ne precizan serĉon Ne preciza serĉo permesas al la ludanto serĉi poziciojn pli rapide kun malalta precizeco. Serĉi por 5, 15 kaj 25 sekundoj ne funckios kun tio opcio. Ŝarĝi bildetojn Ne povis konstrui la dosierujon de elŝuto Nunaj filmetoj ne estas ankoraŭ subtenataj Enhavo limigita al aĝo - Montri limigitan al aĝo filmeto. Postaj ŝanĝoj eblas ĉe la agordoj. Ne povis tute analizi la retejon Ne povis akiri ajnan torenton Nuna @@ -149,7 +151,7 @@ Kiosko Tendencoj Supro 50 - Nova & varma + Nova kaj varma Montri la indiko « Tenu por aldoni » Montri indikon premante la fona aŭ la ŝprucfenestra butono en filmeta \"Detaloj:\" Viciĝita en la fona ludilo @@ -206,7 +208,10 @@ \n \n1. Iru ĉe tiu retpaĝo: %1$s \n2. Ensalutu kiam oni petas vin -\n3. Elŝuto devus komenci (ĝi estas la dosiero de eksporto) +\n3. Click on \"All data included\", then on \"Deselect all\", then select only \"subscriptions\" and click \"OK\" +\n4. Click on \"Next step\" and then on \"Create export\" +\n5. Click on the \"Download\" button after it appears and +\n6. From the downloaded takeout zip extract the .json file (usually under \"YouTube and YouTube Music/subscriptions/subscriptions.json\") and import it here. Importu Soundcloud-n profilon tajpante ĉu la ligilon, ĉu vian ID : \n \n1. Ebligu komputilon modon en retumilon (la retejo malhaveblas por poŝtelefonoj) @@ -285,9 +290,9 @@ Ĉu vi volas forviŝi ĉiujn ludajn poziciojn \? Ŝanĝu la elŝutojn dosierujojn por efekti Pardonu, eraro okazis. - Pardonu, kelkaj eraroj okazis. + Pardonon, io mizokasis. Kio okazis: - Kio:\\nPeto:\\nEnhavlingvo:\\nServo:\\nGMT Horo:\\nPako:\\nVersio:\\nOperaciumo versio: + Kio:\\nPeto:\\nEnhavlingvo:\\nEnhavlando:\\nAplingvo:\\nServo:\\nGMT Horo:\\nPako:\\nVersio:\\nOperaciumo versio: Aŭdio Permesi la konservadon unue Uzantosignalo @@ -435,7 +440,7 @@ Rifuzi Neniu limo Minimumigi dum la apo ŝanĝo - Ago dum ŝanĝante al alia apo el la ĉefa filmetludilo—%s + Ago dum ŝanĝante al alia apo el la ĉefa filmetludilo — %s Neniu Minimumigi por ludi fone Plirapidigi dum silentoj diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c6e62c9a5..6c4b68795 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -10,7 +10,7 @@ Descargar Buscar Ajustes - ¿Quiso decir \"%1$s\"\? + ¿Quiso decir «%1$s»\? Compartir con Elegir navegador giro @@ -18,13 +18,13 @@ Los archivos de vídeo descargados se almacenan aquí Elija la carpeta de descarga para los archivos de vídeo Cambie las carpetas de descarga para que surtan efecto - Resolución predeterminada + Resolución predefinida Reproducir con Kodi - ¿Instalar la app Kore que falta\? + ¿Instalar la aplicación Kore que falta\? Mostrar opción «Reproducir con Kodi» Mostrar opción para reproducir vídeo a través del centro de medios Kodi Audio - Formato de audio predeterminado + Formato de audio predefinido Descargar No se admite el URL Usar reproductor de vídeo externo @@ -50,7 +50,7 @@ No se pudo descifrar la URL del vídeo No se pudo analizar el sitio web Mostrar vídeos \'Siguientes\' y \'Similares\' - Idioma predeterminado del contenido + Idioma predefinido del contenido Miniatura de previsualización del vídeo Reproducir vídeo; duración: Me gusta @@ -58,12 +58,11 @@ Miniatura del avatar del usuario Las transmisiones en vivo no son soportadas aún Contenido - Contenido restringido por edad - Mostrar vídeo restringido por edad. Se pueden realizar cambios futuros desde los ajustes. + Mostrar contenido con restricción de edad Toque «Buscar» para empezar \n Reproducción automática - Reproducir un vídeo cuando NewPipe es llamado desde otra app + Reproducir un vídeo cuando NewPipe es llamado desde otra aplicación En directo Descargas Descargas @@ -72,7 +71,7 @@ No se pudo configurar el menú de descarga No se pudo obtener ninguna transmisión Lo siento, esto no debería haber ocurrido. - Informar de este error vía email + Informar de este error vía correo electrónico Lo siento, algo salió mal. Informar Información: @@ -80,7 +79,7 @@ Su comentario (en Inglés): Detalles: Informar de un error - Reporte de usuario + Informe de usuario Vídeo Audio Reintentar @@ -88,7 +87,7 @@ Iniciar Pausar Reproducir - Eliminar + Borrar Suma de comprobación Misión nueva Aceptar @@ -102,9 +101,9 @@ Toque para ver detalles Espere, por favor… Copiado en el portapapeles - Defina una carpeta de descargas más tarde en la configuración + Defina una carpeta de descargas más tarde en los ajustes No se pudo cargar la imagen - La interfaz de la app dejó de funcionar + La interfaz de la aplicación dejó de funcionar Lo sucedido:\\nPetición:\\nIdioma del Contenido:\\nPaís del contenido:\\nIdioma de la aplicación:\\nServicio:\\nHora GMT:\\nPaquete:\\nVersión:\\nVersión del SO: Negro Todo @@ -121,23 +120,25 @@ Reto reCAPTCHA requerido Modo emergente Reproduciendo en modo emergente - Formato de vídeo predeterminado + Formato de vídeo predefinido Desactivado Mostrar resoluciones más altas Sólo algunos dispositivos pueden reproducir vídeos en 2K / 4K - Resolución predeterminada de emergente + Resolución predefinida de emergente Segundo plano Emergente Filtro Actualizar Limpiar + Recordar propiedades del reproductor emergente + Recordar el último tamaño y posición del reproductor emergente Emergente Redimensionando - Elimina el audio en algunas resoluciones + Quita el audio en algunas resoluciones Controles del reproductor por gestos Usar gestos para controlar el brillo y volumen del reproductor Sugerencias de búsqueda - Mostrar sugerencias cuando esté buscando + Mostrar sugerencias al buscar Mejor resolución Acerca de NewPipe Ajustes @@ -158,7 +159,7 @@ Suscribirse Suscrito Canal no suscrito - No se pudo cambiar la suscripción + No se puede cambiar la suscripción No se pudo actualizar la suscripción Principal Suscripciones @@ -206,8 +207,8 @@ %s vídeo %s vídeos - Se eliminó el elemento - ¿Quiere eliminar este elemento del historial de búsquedas\? + Elemento borrado + ¿Quieres borrar este elemento del historial de búsquedas\? Contenido de la página principal Página en blanco Página del quiosco @@ -220,7 +221,7 @@ Quiosco Tendencias 50 mejores - Mostrar sugerencia cuando se presiona el botón de segundo plano o emergente en la página \"Detalles:\" del vídeo + Mostrar sugerencia al pulsar el botón de segundo plano o emergente en la página «Detalles:» del vídeo En cola en el reproductor de 2.º plano En cola en el reproductor emergente Reproducir todo @@ -229,20 +230,20 @@ Recuperándose del error del reproductor Quitar Detalles - Configuración de audio + Ajustes del audio [Desconocido] Comenzar a reproducir aquí Comenzar a reproducir en segundo plano Reproducir en modo emergente - Mostrar consejo \"Mantener presionado para añadir\" + Mostrar consejo «Mantener pulsado para añadir» Nuevo y lo mejor - Mantener presionado para agregar a la cola + Mantener pulsado para añadir a la cola Donar NewPipe es desarrollado por voluntarios que emplean su tiempo libre para brindarle la mejor experiencia. Haga una aportación para ayudarlos a crear un NewPipe mejor mientras disfrutan de una taza de café. Dar de vuelta Sitio web Visite el sitio web de NewPipe para más información y noticias. - País predeterminado del contenido + País predefinido del contenido Alternar orientación Cambiar a segundo plano Cambiar a emergente @@ -277,28 +278,28 @@ Añadir a Arrastrar para reordenar Crear - Eliminar uno - Eliminar todos + Borrar uno + Borrar todos Descartar Cambiar nombre - ¿Quiere eliminar este elemento del historial de reproducciones\? - ¿Confirma que quiere eliminar todos los elementos del historial\? + ¿Quieres borrar este elemento del historial de reproducciones\? + ¿Quieres borrar todos los elementos del historial\? Última reproducción Más reproducido Preguntar siempre Lista de reproducción nueva - Eliminar + Borrar Cambiar nombre Nombre Añadir a la lista de reproducción Definir como miniatura de lista de reproducción Marcar lista de reproducción - Eliminar marcador - ¿Quiere eliminar esta lista\? + Quitar marcador + ¿Quieres borrar esta lista\? Lista de reproducción creada Añadido a la lista de reproducción Miniatura de lista de reproducción cambiada. - No se pudo eliminar la lista de reproducción. + No se pudo borrar la lista de reproducción. Algo aparecerá aquí pronto ;D Sin subtítulos Ajustar @@ -306,15 +307,15 @@ Zoom Depuración Auto generados - La monitorización de fugas de memoria puede causar que la app no responda cuando hay Heap Dump - Reportar errores fuera del ciclo de duración - Forzar reporte de excepciones no entregables de RX fuera del fragmento o del ciclo de actividad después del descarte + La monitorización de fugas de memoria puede causar que la aplicación no responda al realizar el volcado de memoria + Informar errores fuera del ciclo de duración + Forzar informe de excepciones no entregables de RX fuera del fragmento o del ciclo de actividad después del descarte Usar búsqueda rápida e inexacta La búsqueda inexacta permite al reproductor buscar posiciones más rápido con menor precisión. Buscar de a 5, 15 o 25 segundos no funciona. Poner en cola vídeo relacionado siguiente Continuar reproducción sin repetir al añadir de forma automática un vídeo relacionado con el último visto Archivo - Archivo movido o eliminado + Archivo movido o borrado La carpeta no existe No existe tal archivo/origen del contenido El archivo no existe o carece de los permisos para leer o escribir en él @@ -335,11 +336,11 @@ \n1. Vaya a esta URL: %1$s \n2. Inicie sesión cuando se le pida \n3. Una descarga debería empezar (ese es el archivo de exportación) - Importe un perfil de SoundCloud escribiendo la URL o su ID: -\n -\n1. Active el \"modo escritorio\" en un navegador web (el sitio no está disponible para dispositivos móviles) -\n2. Vaya a esta URL: %1$s -\n3. Inicie sesión cuando se le pida + Importe un perfil de SoundCloud escribiendo la URL o su ID: +\n +\n1. Active el «modo escritorio» en un navegador web (el sitio no está disponible para dispositivos móviles) +\n2. Vaya a esta URL: %1$s +\n3. Inicie sesión cuando se le pida \n4. Copie la URL del perfil a la que fue redireccionado. suID, soundcloud.com/suID Observe que esta operación puede causar un uso intensivo de la red. @@ -349,7 +350,7 @@ Desactivar para evitar la carga de miniaturas y ahorrar datos y memoria. Se vaciará la caché de imágenes en la memoria volátil y en el disco. Se vació la caché de imágenes Vaciar metadatos en memoria caché - Eliminar todos los datos de páginas web en antememoria + Quitar todos los datos guardados de páginas web Se vació la caché de metadatos Controles de velocidad de reproducción Tiempo @@ -357,31 +358,31 @@ Desvincular (puede causar distorsión) No hay streams disponibles para descargar Acción de apertura preferida - Acción predeterminada al abrir contenido: %s + Acción predefinida al abrir contenido: %s No se encontró ninguna aplicación que reproduzca este archivo Subtítulos - Modificar la escala de texto de los subtítulos y los estilos de fondo. Requiere reiniciar la app para que surta efecto. + Modificar la escala de texto de los subtítulos y los estilos de fondo. Requiere reiniciar la aplicación para que surta efecto. Vaciar historial de reproducciones - Elimina el historial de contenido visto y posiciones de reproducción - ¿Eliminar todo el historial de reproducciones\? - Se eliminó el historial de reproducciones. + Borra el historial de contenido visto y posiciones de reproducción + ¿Borrar todo el historial de reproducciones\? + Historial de reproducciones borrado. Vaciar historial de búsquedas - Elimina el historial de palabras clave de búsqueda - ¿Eliminar todo el historial de búsqueda\? - Historial de búsquedas eliminado. - Se eliminó 1 elemento. + Borra el historial de búsqueda de palabras clave + ¿Borrar todo el historial de búsqueda\? + Historial de búsquedas borrado. + Se ha borrado 1 elemento. NewPipe es un software copyleft libre: puedes usarlo, estudiarlo, compartirlo y mejorarlo a voluntad. Específicamente, puedes redistribuirlo y/o modificarlo bajo los términos de la Licencia Pública General GNU publicada por la Free Software Foundation, ya sea la versión 3 de la Licencia, o (a tu elección) cualquier versión posterior. - ¿Quiere importar también la configuración\? + ¿Quiere importar también los ajustes\? Normativa de privacidad de NewPipe El proyecto NewPipe toma su privacidad muy en serio. Por ello, la aplicación no recopila algún dato sin su consentimiento. -\nLa normativa de privacidad de NewPipe explica en detalle qué datos se envían y almacenan cuando envía un informe de fallo. +\nLa normativa de privacidad de NewPipe explica en detalle qué datos se envían y almacenan al enviar un informe de fallo. Leer la normativa de privacidad - Para cumplir con el Reglamento general europeo de protección de datos (GDPR), atraemos su atención sobre la política de privacidad de NewPipe. Por favor léase cuidadosamente. + Para cumplir con el «Reglamento general europeo de protección de datos (GDPR)», atraemos su atención sobre la política de privacidad de NewPipe. Por favor léase cuidadosamente. \nDebe aceptarlo para enviarnos el informe de error. Aceptar Declinar Sin límite - Limitar la resolución cuando se usen datos móviles + Limitar la resolución al usar datos móviles Minimizar al cambiar de aplicación Acción de cambiar a otra aplicación desde el reproductor principal — %s Ninguna @@ -402,7 +403,7 @@ recuperando Añadir a cola Acción denegada por el sistema - Se eliminó el archivo + Archivo borrado Descarga fallida Descarga finalizada @@ -417,7 +418,7 @@ Hay una descarga pendiente con este nombre Mostrar como grilla Mostrar como lista - ¿Quiere limpiar su historial de descargas o eliminar todos los ficheros descargados\? + ¿Quieres vaciar el historial de descargas o borrar todos los ficheros descargados\? Detener Intentos máximos Cantidad máxima de intentos antes de cancelar la descarga @@ -442,7 +443,7 @@ Falló el posprocesamiento NewPipe se cerró mientras se trabajaba en el archivo No hay suficiente espacio disponible en el dispositivo - Se perdió el progreso porque el archivo fue eliminado + Se perdió el progreso porque el archivo fue borrado Tiempo de espera excedido No se puede recuperar esta descarga Preguntar dónde descargar @@ -465,15 +466,15 @@ Notificaciones de versiones nuevas de NewPipe Almacenamiento externo no disponible No es posible descargar a una tarjeta SD externa. \¿Restablecer la ubicación de la carpeta de descarga\? - No se pudo leer las pestañas guardadas, se usarán las pestañas por defecto - Restaurar valores por defecto - ¿Quieres restaurar los valores por defecto\? + No se pudo leer las pestañas guardadas, se usarán las pestañas predefinidas + Restaurar valores predefinidos + ¿Quieres restaurar los valores predefinidos\? Número de suscriptores no disponible Qué pestañas aparecen en la página principal Selección Conferencias Actualizaciones - Mostrar una notificación para solicitar actualizar la aplicación cuando haya una nueva versión disponible + Mostrar una notificación para solicitar actualizar la aplicación al haber una nueva versión disponible Modo de vista de lista Automático Cambiar vista @@ -490,12 +491,12 @@ Posiciones en listas Mostrar indicador de posición en listas de reproducción Vaciar datos - Se eliminaron las posiciones de reproducción. - Eliminar posiciones de reproducción - Elimina todas las posiciones de reproducción - ¿Quiere eliminar todas las posiciones de reproducción\? + Posiciones de reproducción borradas. + Borrar posiciones de reproducción + Borra todas las posiciones de reproducción + ¿Quieres borrar todas las posiciones de reproducción\? Activar/desactivar servicio, seleccionados actualmente: - Quiosco predeterminado + Quiosco predefinido Nadie está viendo %s viendo @@ -511,30 +512,30 @@ Instancias de PeerTube Selecciona tus instancias favoritas de PeerTube Encuentra las instancias que te gusten en %s - Agregar instancia + Añadir instancia Ingresar URL de la instancia No se pudo validar la instancia Solo se admiten URL HTTPS La instancia ya existe Local - Agregados recientemente + Añadidos recientemente Más gustados Generado automáticamente (no se encontró creador) Elige una instancia Limpiar historial de descargas - Eliminar archivos descargados - Eliminadas %1$d descargas + Borrar archivos descargados + Borradas %1$d descargas Permitir mostrar sobre otras aplicaciones Idioma de aplicación - Predeterminado del sistema - Pulse en «Hecho» cuando esté resuelto + Predefinido del sistema + Pulse en «Hecho» al resolverlo Hecho Vídeos %d segundo %d segundos - Debido a limitaciones de ExoPlayer la duración de la búsqueda fue fijada en %d segundos + Debido a limitaciones de ExoPlayer, la duración de la búsqueda fue definida en %d segundos Silenciar Desactivar silencio Ayuda @@ -572,7 +573,7 @@ Disponible para algunos servicios, suele ser más rápido pero puede mostrar una cantidad limitada de ítems y a menudo información incompleta (por ejemplo falta de duración, tipo de ítem o estado). Activar modo rápido Desactivar modo rápido - ¿Piensas que la carga de contenidos es muy lenta\? Entonces intenta habilitar la carga rápida (puedes cambiarlo en los ajustes o presionando el botón debajo). + ¿Piensas que la carga de contenidos es muy lenta\? Entonces intenta habilitar la carga rápida (puedes cambiarlo en los ajustes o pulsando el botón debajo). \n \nNewpipe ofrece dos formas de cargar los contenidos: \n• Obtener todos los canales con suscripciones, lento pero completo. @@ -593,25 +594,25 @@ Canciones Este video tiene restricción por edades. \n -\nHabilita \"Contenido restringido por edades\" en los ajustes si quieres verlo. +\nHabilita «%1$s» en los ajustes si quieres verlo. Sí, y también videos vistos parcialmente - Los videos que ya se hayan visto luego de agregados a la lista de reproducción, serán eliminados. + Los videos que ya se hayan visto luego de añadidos a la lista de reproducción, serán quitados. \n¿Estás seguro\? ¡Esta acción no se puede deshacer! - ¿Borrar videos ya vistos\? - Borrar videos ya vistos + ¿Quitar videos ya vistos\? + Quitar videos ya vistos Por %s Creado por %s Miniatura de avatar del canal Los textos originales de los servicios serán visibles en los ítems de transmisiones Mostrar tiempo atrás original en ítems - Modo restringido de YouTube + Activar el «Modo restringido» de YouTube Página de lista de reproducción Mostrar sólo suscripciones desagrupadas Sin marcadores de lista de reproducción aún Seleccione una lista de reproducción - Por favor revise si ya existe una discusión sobre su problema. Cuando se crean entradas duplicadas, toma tiempo de nosotros que podríamos usar para arreglar tal problema. - Reportar en Github - Copiar reporte con formato + Por favor revise si ya existe una discusión sobre su problema. Al crear entradas duplicadas, toma tiempo de nosotros que podríamos usar para arreglar tal problema. + Informar en Github + Copiar informe con formato Mostrando resultados para: %s Orden aleatorio Escalar miniatura a relación de aspecto 1:1 @@ -624,7 +625,7 @@ Poner en cola Cambiar de un reproductor a otro puede reemplazar la cola de reproducción La cola de reproducción activa será reemplazada - Pedir confirmación antes de eliminar una cola + Pedir confirmación antes de vaciar una cola Nada Almacenar en memoria (búfer) Repetir @@ -636,4 +637,12 @@ Botón de segunda acción Botón de primera acción Escalar la miniatura del vídeo mostrada en la notificación de relación de aspecto 16:9 a 1:1 (puede ocasionar distorsiones) + Vaciar las cookies que NewPipe guarda al resolver un reCAPTCHA + Mostrar contenido inapropiado para niños porque tiene un limite de edad (como 18+). + Mostrar pérdidas de memoria + Añadido a la cola + Añadir a la cola + Las cookies reCAPTCHA han sido limpiadas + Limpiar las cookies reCAPTCHA + YouTube provee un «Modo restringido», el cual oculta contenido potencialmente sólo apto para adultos. \ No newline at end of file diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index f1b16effd..9f06f3fee 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -57,6 +57,8 @@ Hele Tume Must + Pea hüpikakna suurus ja asukoht meeles + Pea hüpikakna viimane suurus ja asukoht meeles Kasuta ebatäpset kerimist Ebatäpne kerimine lubab pleieril otsida asukohta kiiremini täpsuse arvel Laadi pisipildid @@ -99,7 +101,6 @@ Lisati hüpikpleieri järjekorda Sisu Vanusepiiranguga sisu - Kuva vanusepiiranguga video. Sellist sisu saab lubada seadetes. OTSE Allalaadimised Allalaadimised diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 32d334fab..b45e75de9 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -26,7 +26,7 @@ Erakutsi \'hurrengo\' eta \'antzeko\' bideoak URLak ez du euskarririk Edukiaren hizkuntz lehenetsia - Bideoa eta Audioa + Bideoa eta audioa Bideoaren aurreikuspen argazkitxoa Erreproduzitu bideoa, iraupena: Igotzailearen abatarraren iruditxoa @@ -39,7 +39,8 @@ Erabili kanpo bideo-erreproduzigailua Erabili kanpo audio-erreproduzigailua Atzeko planoan erreproduzitzen - Sakatu \"Bilatu\" hasteko + \"Bilatu\" sakatu hasteko +\n Audioa deskargatzeko karpeta Aukeratu audio fitxategiak deskargatzeko karpeta Deskargatutako audio fitxategiak hemen gordetzen dira @@ -59,6 +60,8 @@ Hobetsitako bideo-formatua Gaia Beltza + Gogoratu laster-leihoaren tamaina eta posizioa + Gogoratu laster-leihoaren azken tamaina eta posizioa Erreproduzigailuaren keinu bidezko kontrola Erabili keinuak erreproduzigailuaren distira eta bolumena kontrolatzeko Bilaketa-iradokizunak @@ -67,8 +70,7 @@ Besteak Laster-leiho moduan erreproduzitzen Edukia - Adinez mugatutako edukia - Erakutsi adinez mugatutako bideoa. Ezarpenetan aldaketak egin daitezke gero. + Adinez mugatutako edukia erakutsi Zuzenean Deskargak Deskargak @@ -97,11 +99,11 @@ Aplikazioa/interfazea kraskatu da Hori ez litzateke gertatu behar. Eman errore honen berri e-posta bidez - Erroreak gertatu dira. + Barkatu, zerbait gaizki atera da. Salatu Informazioa: Zer gertatu da: - Zer:\\nEskaria:\\nEdukiaren hizkuntza:\\nZerbitzua:\\nGMT Ordua:\\nPaketea:\\nBertsioa:\\nSE bertsioa: + Zer:\\nEskaria:\\nEdukiaren hizkuntza:\\nEdukiaren herrialdea:\\nAplikazioaren hizkuntza:\\nZerbitzua:\\nGMT Ordua:\\nPaketea:\\nBertsioa:\\nSE bertsioa: Zure iruzkina (Ingelesez): Xehetasunak: Salatu errorea @@ -176,7 +178,7 @@ NewPipe jakinarazpena Erreproduzigailua Portaera - Historia eta cachea + Historia eta cache-a Erreprodukzio-zerrenda Desegin Atzeko planoko eta laster-leihoko NewPipe erreproduzigailuen jakinarazpenak @@ -245,7 +247,7 @@ Mantendu ilaran jartzeko Hasi hemen erreproduzitzen Hasi erreproduzitzen bigarren planoan - Hasi erreproduzitzen laster-leihoan + Laster-leihoan erreproduzitzen hasi Ireki tiradera Itxi tiradera Ez da jarioen erreproduzigailurik aurkitu (VLC instalatu dezakezu). @@ -419,7 +421,7 @@ Sareta Automatikoa Aldatu ikuspegia - NewPipe eguneraketa eskuragarri! + NewPipe-ren eguneraketa eskuragarri dago! Sakatu deskargatzeko Amaituta Zain @@ -589,5 +591,55 @@ Abestiak Bideo hau adinez mugatua dago. \n -\nIkusi nahi baduzu, gaitu ezazu \"Adinez mugatutako edukia\" ezarpenetan. +\nIkusi nahi baduzu, piztu ezazu \"%1$s\" ezarpenetan. + Egilea: %s + Erreprodukzio zerrendaren orria + %s-k sortua + Kanalaren avatar-earen miniatura + Erakutsi agrupatuta ez dauden harpidetzak bakarrik + Bai, partzialki ikusitako bideoak ere bai + Jada ikusi eta gero erreprodukzio zerrendara gehitu diren bideoak ezabatuak izango dira. +\nJarraitu nahi duzu\? Ekintza hau ezin da desegin! + Ikusitako bideoak ezabatu\? + Ikusitako bideoak ezabatu + Inoiz ez + Bakarrik WiFi-arekin + Erreprodukzioa automatikoki hasi — %s + Erakutsi memoria galerak + Ilara erreproduzitu + Oraindik ez dago erreprodukzio-zerrenda laster-markarik + Playlist bat aukeratu + Mesedez, egiaztatu jada zure arazoarekin diskusiorik sortuta badagoen. Sarrera duplikatuak daudenean, arazoa ebazteko erabili dezakegun denbora galtzen ari gara. + Formatodun erreportea kopiatu + GitHub-en erreportatu + reCAPTCHA bat egiten duzunean NewPipe-k gordetzen dituen kookiak ezabatu + reCAPTCHA kookiak garbitu dira + Ezabatu reCAPTCHA-ren kookiak + Adinez mugatuta dagoen eta haurrentzako desegokia izan daitezkeen edukia erakutsi (+18 adibidez). + YouTube-ren \"Modu Murriztua\" helduentzako edukia izan daitekeen edukia ezkutatzen du. + Piztu YouTube-ren \"Modu Murriztua\" + Jakinarazpena + Ezin izan da URL-a ezagutu. Beste aplikazio batekin ireki\? + Auto-ilara + Erreprodukzio ilara aktiboa ordezkatuko da + Erreproduzitzaile batetik beste batera aldatzeak ilara ordezkatu dezake + Konfirmazioa eskatu ilaratik ezabatu baino lehenago + Ezer ez + Buffering + Aleatorio + Gehienez hiru ekintza aukera ditzakezu jakinarazpenean erakusteko! + Errepikatu + Bostgarren ekintzaren botoia + Laugarren ekintzaren botoia + Hirugarren ekintzaren botoia + Bigarren ekintzaren botoia + Lehenego ekintzaren botoia + Eskalatu jakinarazpenetan erakusten den bideo miniaturaren formatu-ratioa 16:9tik 1:1era (distortsioak sor ditzake) + Miniatura 1:1 formatu-ratiora eskalatu + %s bilaketaren erantzunak erakusten + Ilaran jarri da + Jarri ilaran + Zerbitzuen jatorrizko testuak transmisioko elementuetan ikusgai egongo dira + Erakutsi «orain dela» jatorrizko denbora elementuetan + Editatu beheko jakinarazpen ekintza bakoitza gainean sakatuz. Hautatu horietako hiru gehienez jakinarazpen trinkoan erakusteko eskuineko kontrol laukiak erabiliz. \ No newline at end of file diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 56c638ebf..4e4cc7a1d 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -45,7 +45,6 @@ در حال پخش در پس‌زمینه محتوا محتوای محدود شده بر اساس سن - نمایش ویدیوهای دارای محدودیت سنی. تغییرات آتی از طریق تنظیمات ممکن است. زنده بارگیری‌ها بارگیری‌ها @@ -288,6 +287,8 @@ حالت تصویر در تصویر اندازه پیش فرض پنجره جداگانه تصویر در تصویر + به یاد نگه داشتن خصوصیات + به یاد داشتن آخرین اندازه و موقعیت قبلی پنجره جداگانه زمان فعلی پخش کننده را به صورت تقریبی و سریع جلو ببر این گزینه باعث می شود هنگام جلو/عقب کردن زمان تصویر، به جای زمان دقیق انتخاب شده، به زمان غیر دقیق و نزدیک به مکان انتخاب شده برود که این کار سریع تر انجام می شود. کاره یا رابط کاربری با خطا مواجه شد diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 675ef0f29..e5fb71e5f 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -1,6 +1,7 @@ - Napauta hakua aloittaaksesi + Napauta hakua aloittaaksesi +\n %1$s näyttökertaa Julkaistu %1$s Ei löytynyt suoratoistosoitinta. Asennetaanko VLC\? @@ -12,7 +13,7 @@ Lataus Haku Asetukset - Tarkoititko \"%1$s\"\? + Tarkoititko ”%1$s”\? Jaa Valitse selain kierto @@ -44,7 +45,7 @@ Vain jotkin laitteet voivat toistaa 2K/4K-videota Toista Kodissa Asennetaanko puuttuva Kore-sovellus\? - Näytä \"Toista Kodissa\"-vaihtoehto + Näytä ”Toista Kodissa”-vaihtoehto Näyttää vaihtoehdon videon toistamiseen Kodi-mediasoittimessa Ääni Oletusääniformaatti @@ -53,6 +54,8 @@ Kirkas Tumma Musta + Muista ponnahdusikkunan ominaisuudet + Muista ponnahdusikkunan viimeisin koko ja sijainti Soittimen eleohjaus Käytä eleitä ohjataksesi soittimen kirkkautta ja äänentasoa Hakuehdotukset @@ -76,8 +79,7 @@ Toistaa taustalla Toistetaan ponnahdusikkunatilassa Sisältö - Ikärajoitettu sisältö - Ikärajoitettu video. Muuttaminen on mahdollista asetuksissa. + Näytä ikärajoitettu sisältö Suora Lataukset Lataukset @@ -358,11 +360,14 @@ Edellinen vienti Tilauksia ei voitu tuoda Tilauksia ei voitu viedä - Tuo youtube-tilaukset lataamalla ensin tilauslistatiedostosi: + Tuo YouTube-tilaukset Google Takeoutista: \n \n1. Mene osoitteeseen: %1$s -\n2. Kirjaudu sisään kun niin vaaditaan -\n3. Latauksen pitäisi alkaa (se on se tiedosto) +\n2. Kirjaudu sisään pyydettäessä +\n3. Klikkaa \"Kaikki Youtube-data valittuna\", sitten \"Poista kaikki valinnat\", sitten ainoastaan \"tilaukset\" ja klikkaa \"OK\" +\n4. Klikkaa \"Seuraava vaihe\" ja \"Luo vienti\" +\n5. Klikkaa \"Lataa\" tämän ilmestyessä +\n6. Ladatusta takeoutin zip-tiedostosta pura json-tiedosto (yleensä sijainnissa \"Youtube ja Youtube Musiikki/tilaukset/tilaukset.json\" ja tuo se tänne Tuo SoundCloud-profiili kirjoittamalla joko osoite tai ID:si: \n \n1. Laita päälle työpöytämoodi selaimessasi (tai käytä tietokonetta, tämä sivu ei toimi mobiilisivuna) @@ -405,7 +410,7 @@ Valinta Mitkä välilehdet näytetään pääsivulla Valmis - Paina \"Valmis\", kun ratkaistu + Paina ”Valmis”, kun ratkaistu ∞ videota 100+ videota @@ -438,8 +443,8 @@ Videot Tämä video on ikärajoitettu. \n -\nSalli ikärajoitettu sisältö asetuksissa katsoaksesi. - YouTuben rajoitettu tila +\nSalli ”%1$s” asetuksissa katsoaksesi. + Ota käyttöön YouTuben ”Rajoitettu tila” Päivitykset Instanssi on jo olemassa Vain HTTPS-URL:t ovat tuettuja @@ -485,9 +490,9 @@ \n• Koko tilatun kanavan lataaminen, mikä on hidasta, mutta lataa syötteen kokonaisuudessaan. \n• Erityisen palvelu-endpointin käyttö, mikä on nopeaa, mutta yleensä ei lataa syötettä kokonaisuudessaan. \n -\nNäiden kahden välinen ero on, että nopean lataamista tiedoista yleensä puuttuu esim. sisällön kesto tai tyyppi (ei voi erotella livevideoita ja tavallisia) tai se ei lataa kaikkea sisältöä. +\nNäiden kahden välinen ero on, että nopean tavan lataamista tiedoista yleensä puuttuu esim. sisällön kesto tai tyyppi (ei voi erotella livevideoita ja tavallisia) tai se ei lataa kaikkea sisältöä. \n -\nYouTuve on esimerkki palvelusta, joka tarjoaa nopean tavan RSS-syötteen avulla. +\nYouTube on esimerkki palvelusta, joka tarjoaa nopean tavan RSS-syötteen avulla. \n \nValinta riippuu siitä, mitä halutaan: nopeutta vai tarkkoja tietoja. Oletuskioski @@ -632,4 +637,14 @@ Ensimmäinen toimintopainike Skaalaa ilmoituksessa näytettävä videon esikatselukuva kuvasuhteesta 16:9 kuvasuhteeseen 1:1 (saattaa aiheuttaa vääristymiä) Skaalaa esikatselukuva 1:1-kuvasuhteeseen + Näytä muistivuodot + Lisätty jonoon + Lisää jonoon + Poista evästeet, jotka NewPipe tallentaa, kun ratkaiset reCAPTCHA:n + reCAPTCHA-evästeet on poistettu + Poista reCAPTCHA-evästeet + YouTube tarjoaa ”Rajoitetun tilan”, joka piilottaa aikuisviihdesisällön. + Näytä mahdollisesti lapsille sopimaton sisältö, jolla on ikäraja (esim. 18+). + Anna Androidin muokata ilmoituksen väriä esikatselukuvan päävärin mukaan (tämä ei ole mahdollista kaikilla laitteilla) + Käytä värejä ilmoituksessa \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 836a66529..70498ab90 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -55,7 +55,7 @@ Contenu indisponible Désolé, quelque chose s\'est mal passé. Contenu - Contenu avec limite d’âge + Afficher le contenu avec limite d’âge En direct Impossible de charger toutes les miniatures Impossible de déchiffrer la signature URL de la vidéo @@ -73,9 +73,9 @@ Audio Réessayer Veuillez d’abord accorder l’accès au stockage - Appuyer sur \"Rechercher\" pour commencer + Appuyez sur la loupe pour commencer +\n Lecture automatique - Affiche les vidéos soumises à une limite d’âge. Modifier cette option est possible depuis les paramètres. Rapport utilisateur Signaler Impossible de configurer le menu de téléchargement @@ -125,6 +125,8 @@ Afficher des définitions plus élevées Seuls certains appareils peuvent lire les vidéos 2K et 4K Format vidéo par défaut + Mémoriser les propriétés de la fenêtre flottante + Mémorise les dernières taille et position de la fenêtre flottante Flottant Filtre Rafraîchir @@ -159,7 +161,7 @@ Lettres et chiffres À propos de NewPipe © %1$s par %2$s sous %3$s - Que ce soit pour des idées de traductions, de changements de design, de nettoyage de code ou de gros changements de code, une aide est toujours la bienvenue. Plus on en fera, meilleur il deviendra ! + Que ce soit pour des idées de traductions, de changements de design, de nettoyage de code ou de gros changements de code, une aide est toujours la bienvenue. Plus on en fera meilleur il sera ! Impossible de modifier l’abonnement Impossible d’actualiser l’abonnement Continuer la lecture après les interruptions (ex. : appels téléphoniques) @@ -332,11 +334,14 @@ Exportation précédente Impossible d’importer les abonnements Impossible d’exporter les abonnements - Veuillez importer vos abonnements YouTube en téléchargeant le fichier d’exportation. -\n -\n1. Suivez ce lien : %1$s. -\n2. Connectez-vous à votre compte. -\n3. Un téléchargement va démarrer (celui du fichier d’exportation). + Importez vos abonnements YouTube depuis Google Takeout : +\n +\n1. Suivez ce lien : %1$s +\n2. Connectez-vous à votre compte +\n3. Cliquez sur \"Toutes les données Youtube sont incluses\", puis sur \"Tout désélectionner\", puis sélectionnez uniquement \"abonnements\" et cliquez sur \"OK\" +\n4. Cliquez sur \"Étape suivante\" et ensuite sur \"Créer une exportation\" +\n5. Cliquez sur le bouton \"Télécharger\" après qu\'il apparaisse et +\n6. À partir du fichier zip téléchargé, extrayez le fichier .json (généralement sous \"YouTube et YouTube Music/subscriptions/subscriptions.json\") et importez-le ici. Veuillez importer un profil SoundCloud en saisissant l’URL de votre profil ou votre identifiant. \n \n1. Activez le « mode bureau » dans votre navigateur Web (le site n’est pas disponible pour les appareils mobiles). @@ -587,9 +592,9 @@ 100+ vidéos Artistes Chansons - Cette vidéo est bloquée à cause de la limite d\'âge. + Cette vidéo dispose d\'une limite d\'âge. \n -\nActivez « Contenu avec limite d\'âge » dans les paramètres, rubrique « Contenu » si vous voulez la voir. +\nActivez « %1$s » dans les paramètres si vous voulez la voir. Supprimer les vidéos vues Oui, et des vidéos partiellement regardées Les vidéos qui ont été regardées avant et après avoir été ajoutées à la liste de lecture seront supprimées. @@ -600,7 +605,7 @@ Créé par %s Les textes originaux des services vont être visibles dans les items Afficher la date originelle sur les items - Mode restreint de YouTube + Activer le « Mode restreint » de YouTube Afficher les abonnements sans groupes uniquement Page des listes de lecture Aucune liste de lecture encore enregistrée @@ -632,4 +637,14 @@ Premier bouton d\'action Mettre à l\'échelle la miniature de la vidéo affichée dans la notification du format 16:9 au format 1:1 (peut provoquer des déformations) Dimensionner la miniature au format 1:1 + Afficher les fuites de mémoire + Ajouté à la file d\'attente + Ajouter à la file d\'attente + Effacer les cookies que NewPipe garde lorsque vous résolvez un reCAPTCHA + Les cookies reCAPTCHA ont été effacés + Effacer les cookies reCAPTCHA + YouTube dispose d\'un « Mode restreint » qui cache le contenu potentiellement inapproprié. + Le contenu de cette émission n\'est peut-être pas approprié pour les enfants à cause d\'une limite d\'âge (18 +). + Notification colorée + Demander à Android de personnaliser la couleur de la notification en fonction de la couleur principale de la miniature (noter que cela n’est pas disponible sur tous les appareils) \ No newline at end of file diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index fc1e6cc58..56c3c5165 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -58,6 +58,8 @@ Claro Escuro Negro + Lembrar o tamaño e a posición do «popup» + Lembrar o tamaño e a posición anteriores do «popup» Usar un salto inexacto mais inexacto Busca incorrecta permite ao xogador buscar posicións máis rápidas con precisión reducida. A busca de 5, 15 ou 25 segundos non funciona con isto. Carregar miniaturas @@ -99,7 +101,6 @@ Na cola do reprodutor popup Contido Contido restrinxido para certa idade - Mostrar vídeo restrinxido por idade. Os cambios futuros son posibles na configuración. En directo Descargas Descargas diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 6abe3abbb..2e51f4bd9 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -46,6 +46,8 @@ בהיר כהה שחור + שמירת מאפייני החלון הצף + שמירת המיקום והגודל האחרונים של החלון הצף מחוות מגע לשליטה בנגן שימוש במחוות כדי לשלוט בבהירות ובעצמת השמע של הנגן הצעות חיפוש @@ -61,8 +63,7 @@ מתנגן ברקע מתנגן בחלון צף תוכן - תוכן עם הגבלת גיל - הצגת סרטונים עם הגבלת גיל. ניתן לשנות את זה בעתיד דרך ההגדרות. + הצגת תוכן עם הגבלת גיל חי הורדות הורדות @@ -318,10 +319,13 @@ לא ניתן לייבא את המינויים לא ניתן לייצא את המינויים כדי לייבא את רשימת המינויים שלך מ־YouTube עליך להוריד את קובץ הייצוא: -\n +\n \n1. לעבור לכתובת הזו: %1$s \n2. להיכנס אם נתבקשת -\n3. ההורדה אמורה להתחיל (זה קובץ הייצוא) +\n3. ללחוץ על „All data included”, ואז על „Deselect all”, לאחר מכן לבחור רק את „subscriptions” וללחוץ על „OK” +\n4. ללחוץ על „Next step” ואז על „Create export” +\n5. ללחוץ על כפתור ה־„Download” כשהוא מופיע ואז +\n6. לחלץ את קובץ ה־‎.json מתוך ה־zip (בדרך כלל תחת „YouTube and YouTube Music/subscriptions/subscriptions.json”) ולייבא אותו כאן. קצב שימוש בחיפוש מהיר ולא מדויק חיפוש גס מאפשר לנגן לחפש נקודת זמן מהר יותר, ברמת דיוק נמוכה יותר. חיפוש של 5, 15 או 25 שניות לא עובד עם ההגדרה הזאת. @@ -610,7 +614,7 @@ שירים סרטון זה מוגבל לצפייה מגיל מסוים. \n -\nיש להפעיל את „תוכן עם הגבלת גיל” בהגדרות כדי לצפות בו. +\nיש להפעיל את „%1$s” בהגדרות כדי לצפות בו. כן, לרבות סרטונים שהפסקתי באמצע סרטונים שלאחר שצפית בהם מופיע לרשימת הנגינה יוסרו. \nלהמשיך\? זאת פעולה בלתי הפיכה! @@ -618,7 +622,7 @@ להסיר סרטונים שנצפו\? הטקסט המקורי משירותים יופיע בפריטי התזרים הצגת לפני כמה זמן מקורי על פריטים - מצב מוגבל של YouTube + הפעלת „מצב מוגבל” של YouTube מאת %s נוצר ע״י %s תמונה ממוזערת של הערוץ @@ -653,4 +657,14 @@ כפתור פעולה שני לשנות את יחס התצוגה הממוזערת שמופיעה בהתראות מיחס תצוגה של 16:9 ל־1:1 (עשוי לעוות את התמונה) שינוי גודל התצוגה הממוזערת ליחס תצוגה 1:1 + הצגת דליפות זיכרון + נוסף לתור + הוספה לתור + לנקות עוגיות שנשמרות על ידי NewPipe בעת פתרון reCAPTCHA + העוגיות של ה־reCAPTCHA נמחקו + פינוי עוגיות reCAPTCHA + פלטפורמת YouTube מספקת „מצב מוגבל” שמסתיר תוכן שעשוי להתאים למבוגרים בלבד. + הצגת תוכן שעלול להיות בלתי הולם לילדים עקב מגבלת גיל (כגון 18+). + לאפשר ל־Android להתאים את צבע ההתראה בהתאם לצבע העיקרי של התמונה הממוזערת (לא זמין בכל המכשירים) + צביעת ההתראה \ No newline at end of file diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index fa19e7060..86a111bcf 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -51,7 +51,8 @@ Item हटा दिया गया है फ़िलहाल चर्चा में है ऑडियो सेटिंग - शुरू करने के लिए खोज चिह्न दबाएं + शुरू करने के लिए \"खोज\" चिह्न दबाएं +\n रद्द करें क्या आप का मतलब %1$s है? शेयर करें @@ -81,6 +82,8 @@ डिफ़ॉल्ट विडियो का फॉर्मेट एप्प का नया रूप काला + विडियो पॉपअप की आकर और उसकी स्थति को याद रखे + विडियो पॉपअप के पहले वाली आकर और उसकी स्थिति को याद रखे प्लेयर इशारा नियंत्रण विडियो प्लेयर की ब्राइटनेस और ध्वनी को नियंत्रण के लिए फ़ोन में इशारो का प्रयोग करे खोज के सुझाव देखे @@ -105,7 +108,6 @@ पॉपअप प्लेयर की कतार पर विषयवस्तु उम्र प्रतिबंधित विषय वस्तु - उम्र प्रतिबंदित विडियो है .इस प्रकार की विषयवस्तु को अनुमति देने के लिए सेटिंग से संभव है | लाइव डाउनलोड डाउनलोड diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 23bcb1f57..7ee165faf 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -4,7 +4,7 @@ \n %1$s pregleda Objavljeno %1$s - Reproduktor za stream nije pronađen. Želite li instalirati VLC? + Reproduktor za stream nije pronađen. Instalirati VLC\? Instaliraj Poništi Otvori u pregledniku @@ -13,7 +13,7 @@ Preuzimanje Pretraživanje Postavke - Jeste li mislili: %1$s\? + Jeste li mislili „%1$s”\? Podijeli putem Izaberi pretraživač rotacija @@ -53,30 +53,31 @@ Tema Svijetla Tamna - Crno + Crna + Zapamti veličinu i poziciju skočnog prozora + Zapamti posljednju veličinu i poziciju skočnog prozora Kontroliranje reproduktora gestama Koristi geste za kontrolu svjetline i glasnoće reproduktora - Sugestije pri traženju + Prijedlozi pri traženju Prikaži prijedloge pri traženju Povijest pretraživanja Svaku pretragu spremi lokalno Prati povijest - Pratite pogledane videozapise + Spremaj povijest gledanja Nastavi reprodukciju Nastavi reproducirati nakon prekidanja (npr. telefonski pozivi) Preuzmi Prikaži \'Sljedeće\' i \'Slične\' videozapise URL nije podržan Zadani jezik sadržaja - Video i zvuk + Video i audio Skočni prozor Izgled Drugo Reprodukcija u pozadini - Reproduciram u skočnom prozoru + Reprodukcija u skočnom prozoru Sadržaj - Prikaži eksplicitni sadržaj - Prikaži dobno ograničeni videozapis. Buduće promjene moguće je postaviti u postavkama. + Prikaži dobno ograničeni sadržaj Uživo Preuzimanja Preuzimanja @@ -105,7 +106,7 @@ Aplikacija/UI se srušio Oprostite, ovo se nije trebalo dogoditi. Prijavi pogrešku putem e-maila - Oprostite, neke greške su se dogodile. + Žao nam je, došlo je do neke greške. Prijavi Informacije: Što se dogodilo: @@ -122,11 +123,11 @@ Prijavi grešku Korisničke prijave Nije moguće napraviti direktorij za preuzimanje \'%1$s\' - Napravljen direktorij za preuzimanje \'%1$s\' + Stvorena je mapa za preuzimanje „%1$s” Videozapis Zvuk - Ponovno pokušaj - Omogućite pristup pohrani prvo + Pokušaj ponovo + Najprije odobri pristup spremištu tis. mil mlrd. @@ -168,7 +169,7 @@ O Doprinositelji Licence - Besplatna i lagana YouTube aplikacija za Android. + Besplatna i mala YouTube aplikacija za Android. Pogledaj na GitHubu Licenca za NewPipe Ako imate ideja za prijevod, promjene u dizajnu, čišćenje koda ili neke veće promjene u kodu, pomoć je uvijek dobro došla. Što više radimo, to bolji postajemo! @@ -185,8 +186,8 @@ Obavijesti za NewPipe pozadinske i skočne reproduktore Reproduktor Ponašanje - Povijest & predmemorija - Popis naslova + Povijest i predmemorija + Playlista Poništi Nema rezultata Ovdje nema ništa osim cvrčaka @@ -204,19 +205,19 @@ Nema videozapisa - %s video - %s videa - %s videa + %s videozapis + %s videozapisa + %s videozapisa Stavka je izbrisana - U redu čekanja za reprod. u pozadini + Stavljeno u popis izvođenja playera u pozadini Reproduciraj sve Nije moguće reproducirati ovaj stream Dogodila se neoporavljiva pogreška reproduktora Oporavljanje od pogreške reproduktora Prikaži savjet za držanje Prikažite savjet kada je pritisnut gumb za pozadinsku ili skočnu reprodukciju na stranici detalja videozapisa - U redu čekanja za skočnu reprodukciju + Stavljeno u popis izvođenja playera u skočnom prozoru Želite li izbrisati ovu stavku iz povijesti pretraživanja? Sadržaj Prazna stranica @@ -234,13 +235,13 @@ Ukloni Detalji Postavke zvuka - Zadržite za dodavanje u red čekanja + Drži pritisnuto za dodavanje u popis izvođenja [Nepoznato] Doniraj Web stranica Ovdje započni reprodukciju Započni reprodukciju u pozadini - Započni reprodukciju u skočnom prozoru + Reproduciraj u skočnom prozoru Otvori ladicu Zatvori ladicu Nešto će se uskoro pojaviti :D @@ -250,32 +251,32 @@ Uvjek pitaj Dohvaćam informacije… Odabrani sadržaj se učitava - Nova reprodukcijska lista + Nova playlista Izbriši Preimenuj Ime - Dodaj na reprodukcijsku listu - Postavi kao sliku na listu - Markirajte reprodukcijsku listu - Odmarkirajte - Želite li izbrisati listu? - Reprodukcijska lista je kreirana - Dodano na listu - Slika liste se promjenila. - Greška prilikom brisanja liste. + Dodaj u playlistu + Postavi kao minijaturu playliste + Zabilježi playlistu + Ukloni zabilješku + Izbrisati ovu playlistu\? + Playlista je stvorena + Dodano kao playlistu + Minijatura playliste se promijenila. + Greška prilikom brisanja playliste. Bez naslova Podesno Ispuniti Povećaj Auto generirano Monitoring curenja memorije može uzrokovati greške u radu aplikacije prilikom odlaganje gomile - Izvijestite o pogreškama izvan životnog ciklusa + Izvijesti o krajevima životnog ciklusa Prikaži informacije - Označene Liste za reprodukciju + Zabilježene playliste Dodaj u Učitaj slike Slikovna predmemorija obrisana - Obriši predmemorijsku metupodataka + Izbriši metapodatke iz predmemorije Usluga Kanali Playliste @@ -290,7 +291,7 @@ Uvoz baze podataka Izvoz baze podataka Poništava vašu trenutnu povijest i pretplate - Izvoz povijesti, pretplata i playlisti + Izvezi povijest, pretplate i playliste Očisti povijest gledanja Briše povijest reproduciranih streamova i pozicije reprodukcije Obriši cijelu povijest gledanja\? @@ -353,9 +354,9 @@ Ograniči rezoluciju tijekom korištenja mobilnih podataka Nijedan Reproduktor za stream nije pronađen (možete instalirati VLC za reprodukciju). - Preuzmite datoteku za stream + Preuzmi datoteku streama Koristi brzo netočno premotavanje - Netočno premotavanje omogućava reproduktoru da premota na mjesto brže uz manju preciznost. Premotavanje od 5, 15 ili 25 sekundi s ovime nije moguće. + Netočno premotavanje omogućava reproduktoru da premota brže uz manju točnost. Premotavanje od 5, 15 ili 25 sekundi s ovime nije moguće. Otkaži pretplatu Nova kartica Odaberi karticu @@ -367,21 +368,21 @@ Vanjska pohrana nije dostupna Ažuriranja Prikažite obavijest kada je dostupna nova verzija aplikacije - Lista - Rešetka + Popis + Popločeno Promijeni prikaz - NewPipe dostupno ažuriranje! + Dostupna je nova verzija za NewPipe! Dodirnite za preuzimanje Preuzimanje nije uspjelo - Preuzimanje gotovo + Preuzimanje je gotovo Prikaži pogrešku - Isključite kako biste spriječili učitavanje sličica, spremanje podataka i korištenja memorije. Promjene čiste predmemoriju i predmemoriju slika. - Uklonite sve podatke iz privremenih web-stranica + Isključi, kako bi se spriječilo učitavanje sličica, spremanje podataka i korištenje memorije. Promjene prazne radnu i trajnu predmemoriju slika. + Izbriši sve podatke web-stranica iz predmemorije Metapodaci su izbrisani - Automatski dodaj u red sljedeće strujanje - Automatsko dodavanje povezanog videozapisa tijekom reprodukcije posljednjeg videozapisa u neponavljajućem redu + Automatski dodaj sljedeći stream u popisa izvođenja + Nastavi završavati (ne ponavljajući) popis izvođenja dodavanjem povezanog streama Kontrola glasnoće pomoću gesti - Koristi gesture za kontrolu glasnoće + Koristi geste za kontrolu glasnoće Kontrola svjetline pomoću gesti Koristi gesture za kontrolu svjetline Zadana zemlja sadržaja @@ -391,10 +392,10 @@ Preuzimanje na vanjsku SD karticu nije moguće. Ponovo postaviti lokaciju mape za preuzimanje\? Vanjski playeri ne podržavaju ove vrste veza Nije pronađen nijedan videozapis - Nije pronađen nijedan zvuk + Nije pronađen nijedan audio zapis Nema takve datoteke/izvora sadržaja Datoteka ne postoji ili joj nedostaje dopuštenje za čitanje ili pisanje - Nema dostupnih videozapisa za preuzimanje + Nema dostupnih zapisa za preuzimanje Neuspjelo čitanje spremljenih kartica, stoga se koriste zadane Vratiti zadane Želite li vratiti zadane postavke\? @@ -416,26 +417,26 @@ \n4. Kopirajte URL profila na koji ste preusmjereni. brzina Visina tona - Prekini vezu (može uzrokovati izobličenje) - Minimiziraj prilikom mjenjanje aplikacija - Radnja prilikom prebacivanja na drugu aplikaciju iz glavnog videoplayer-a — %s - Minimiziraj na pozadinski player - Minimiziraj na skočni player + Odspoji (može prouzročiti izobličenje) + Smanji prilikom mijenjanje aplikacije + Radnja prilikom prebacivanja na drugu aplikaciju iz glavnog videoplayera – %s + Smanji na pozadinski player + Smanji na skočni player Način prikaza popisa Automatski Gotovo Na čekanju pauzirano - Na redu za čekanje + stavljeno u popis izvođenja naknadna obrada - Red + Popis izvođenja Sustav je odbio radnju %s preuzimanja dovršeno Generirajte jedinstveni naziv - Piši preko + Prepiši Datoteka s tim nazivom već postoji Preuzeta datoteka s tim nazivom već postoji - U tijeku je preuzimanje s ovim nazivom + Datoteka s ovim nazivom se već preuzima Kod Odredišnu mapu nije moguće stvoriti Datoteku nije moguće stvoriti @@ -451,9 +452,9 @@ Maksimalnih ponovnih pokušaja Maksimalni broj pokušaja prije poništavanja preuzimanja Prekini na mrežama s ograničenim prometom - Preuzimanja koja se ne mogu zaustaviti ponovno će se pokrenuti + Korisno pri prelasku na mobilne podatke, iako se neka preuzimanja ne mogu obustaviti Prikaži komentare - Onemogućite da biste prestali prikazivati komentare + Isključi, kako bi se komentari sakrili Automatska reprodukcija Nema komentara Komentare nije moguće učitati @@ -461,7 +462,8 @@ NewPipe je copyleft libre software: možete ga koristiti, proučavati i poboljšavati po volji. Konkretno, možete ga redistribuirati i / ili modificirati pod uvjetima GNU opće javne licence koju je objavila Free Software Foundation, bilo verzije 3 Licence, ili (po vašem izboru) bilo koje kasnije verzije. Projekt NewPipe ozbiljno shvaća vašu privatnost. Stoga aplikacija ne prikuplja nikakve podatke bez vašeg pristanka. \nNewPipe pravila o privatnosti detaljno objašnjavaju koji se podaci šalju i pohranjuju kada šaljete izvješće o padu aplikacije. - Kako bismo se uskladili s Europskom općom uredbom o zaštiti podataka (GDPR), upozoravamo vas na politiku privatnosti tvrtke NewPipe. Pažljivo ga pročitajte. Morate ga prihvatiti da nam pošaljete izvješća o pogreškama. + Kako bismo se uskladili s Europskom općom uredbom o zaštiti podataka (GDPR), upozoravamo vas na politiku privatnosti tvrtke NewPipe. Pažljivo ga pročitajte. +\nZa slanje izvješća o pogreškama potrebno je prihvatiti politiku privatnosti. Nastavi reprodukciju Vrati zadnji položaj reprodukcije Pozicije na popisima @@ -469,7 +471,7 @@ Obriši podatke Pozicije reprodukcije su izbrisane. Datoteka je premještena ili izbrisana - U tijeku je preuzimanje s ovim nazivom + Datoteka s ovim nazivom već čeka na preuzimanje Vrijeme povezanosti je isteklo Želite li očistiti povijest preuzimanja ili izbrisati sve preuzete datoteke\? Započni preuzimanja @@ -479,13 +481,13 @@ Obriši sve pozicije reprodukcije Obriši sve pozicije reprodukcije\? Nitko ne gleda - NItko ne sluša + Nitko ne sluša Jezik će se promjeniti nakon ponovnog pokretanja aplikacije. Zadani Kiosk Podržani su samo HTTP URL-ovi Lokalno Nedavno dodano - Automatski generirano (nije pronađen nijedan autor) + Automatski generirana (nije pronađen nijedan autor) Očisti povijest preuzimanja Izbriši preuzete datoteke Obrisano %1$d preuzimanja @@ -514,8 +516,8 @@ Pjesme Napravio %s Obavijest - Nikad - Ograniči red preuzimanja + Nikada + Ograniči popis preuzimanja Koristi SAF Ukloni pregledano Ukloni pogledane videozapise\? @@ -542,14 +544,95 @@ Nije učitano: %d Nije odabrana nijedna pretplata Odaberi pretplate - Peti akcijski gumb - Četvrti akcijski gumb - Treći akcijski gumb - Drugi akcijski gumb - Prvi akcijski gumb + Gumb pete radnje + Gumb četvrte radnje + Gumb treće radnje + Gumb druge radnje + Gumb prve radnje Promijenite mape za preuzimanje kako bi stupile na snagu Prikazuju se rezultati za: %s Nije moguće prepoznati URL. Želite li otvoriti u drugoj aplikaciji\? - - Učitavam + Promijeni omjer minijature na 1:1 + Učitavanje u predmemoriju + Istovremeno se pokreće jedno preuzimanje + Dodano u popis izvođenja + Dodaj u popis izvođenja + Popis izvođenja + Automatski popis izvođenja + Popis izvođenja aktivnog playera će se zamijeniti + Prebacivanje s jednog playera na drugi može zamijeniti popisa izvođenja + Pitaj prije pražnjenja popisa izvođenja + + %s slušatelj + %s slušatelja + %s slušatelja + + nije moguće prepisati datoteku + Promijeni omjer prikazane minijature videa u obavijesti iz 16:9 na 1:1 (može prouzročiti izobličenja) + U kompaktnom prikazu obavijesti mogu se odabrati najviše 3 radnje! + Od %s + Minijatura avatara kanala + Dohvati iz određenog feeda kad je dostupno + Vrijeme nakon zadnjeg aktualiziranja prije nego što se pretplata smatra zastarjelom – %s + Prag aktualiziranja feedova + Feed + Prikaži samo negrupirane pretplate + Prazno ime grupe + + %d odabrani + %d odabrana + %d odabranih + + Obrada feeda … + Zadnje aktualiziranje feeda: %s + Grupe kanala + Da, i djelomično pogledane videozapise + Odaberi primjerak + Program će te pitati kamo spremati preuzimanja. +\nOdaberi SAF, ako želiš preuzeti na vanjsku SD karticu + Program će te pitati kamo spremati preuzimanja + Nije moguće obnoviti ovo preuzimanje + Napredak je izgubljen, jer je datoteka izbrisana + NewPipe se zatvorio tijekom rada s datotekom + Stranica playliste + Videzapisi koji su gledani prije i nakon dodavanja u playlistu će se ukloniti. +\nStvarno ih želiš ukloniti\? Ovo je nepovratna radnja! + Još nema zabilježenih playlista + Odaberi playlistu + obnavljanje + Samo na Wi-Fi mreži + Pokreni automatski – %s + Prikaži curenje memorije + + %s gledatelj + %s gledatelja + %s gledatelja + + Uklj/Isklj uslugu, trenutačno odabrana: + Kopiraj formatirani izveštaj + Izbriši riješene reCAPTCHA kolačiće koje NewPipe sprema + reCAPTCHA kolačići su izbrisani + Izbriši reCAPTCHA kolačiće + Ovaj video je dobno ograničen. +\n +\nZa prikaz sadržaja uključi „%1$s” u postavkama. + YouTube nudi postavku „Ograničeni način rada”, čime se skriva sadržaj za odrasle. + Uključi YouTube postavku „Ograničeni način rada” + Prikaži sadržaj koji nije prikladan za određenu dob (kategorija 18). + Primjerak već postoji + Neuspjela provjera primjerka + Upiši URL primjerka + Dodaj primjerak + Pronađi omiljene primjerke na %s + Odaberi tvoje omiljene PeerTube primjerke + PeerTube primjerci + Vrijeme premotavanja prema naprijed ili natrag + Ništa + Promiješaj + Ponovi + Provjeri je li tvoj problem već postoji. Dupla pojava problema krade nam vrijeme koje bismo mogli utrošiti na ispravljanje same greške. + Za uređivanje obavijesti radnji, dodirni ih. Označi do tri radnje za kompaktni prikaz obavijesti. + Zbog ograničenja ExoPlayera, trajanje traženja postavljeno je na %d s + Neka Android prilagodi boju obavijesti prema glavnoj boji minijature (ovo nije dostupno na svim uređajima) + Oboji obavijest \ No newline at end of file diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 0b13e6411..ee11cd5c2 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -57,7 +57,6 @@ Ez egy élő közvetítés, amely még nem támogatott. Automatikus lejátszás Videók automatikus lejátszása, ha a NewPipe egy másik alkalmazásból lett indítva - Korhatáros videó mutatása. Ennek a tartalomnak az engedélyezése a \"Beállítások\"-ban lehetséges. ÉLŐ JELENTÉS Információ: @@ -122,6 +121,8 @@ Csak néhány eszköz tud lejátszani 2K/4K videókat Alapértelmezett videó formátum Fekete + Jegyezze meg a felugró ablak helyét és méretét + Jegyezze meg a felugró ablak előző helyét és méretét Keresési javaslatok Mutasson javaslatokat keresés közben Keresési előzmények @@ -457,4 +458,5 @@ Összes lejátszási pozíció törlése Lejátszási pozíciók törlése Találatok a következőre: %s + Bélyegkép méretezése 1:1 arányra \ No newline at end of file diff --git a/app/src/main/res/values-hy/strings.xml b/app/src/main/res/values-hy/strings.xml index 421f1f74d..f53cfccc1 100644 --- a/app/src/main/res/values-hy/strings.xml +++ b/app/src/main/res/values-hy/strings.xml @@ -54,4 +54,44 @@ Օգտագործել արտաքին աուդիո նվագարկիչ Օգտագործել արտաքին դերակատար Լողացող ռեժիմ NewPipe + Թարմացումներ + Միշտ թարմացնել + + %d նշված + %d նշված + + Նոր + Կայք + Մասին + Մասին + Ալիքներ + Ալիք + Ամենը + Ծանուցում + Տեսք + Թարմացումներ + Այլ + Դիտման պատմություն + Ֆայլը ջնջվեց + Ֆայլ + Երգեր + Այո + Որոնման պատմություն + Փակել + + %d օր + %d օր + + + %d ժամ + %d ժամ + + + %d վրկ + %d վրկ + + + %d րոպե + %d րոպե + \ No newline at end of file diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml index cd7b221d5..01372470d 100644 --- a/app/src/main/res/values-ia/strings.xml +++ b/app/src/main/res/values-ia/strings.xml @@ -171,6 +171,8 @@ Contento del pagina principal Selige un canal Preste + Rememorar ultime grandor e position del reproductor emergente + Rememorar grandor e position del fenestra emergente %s video %s videos diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 3ed14f635..f27c35204 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -41,8 +41,7 @@ Lainnya Memutar di latar belakang Konten - Konten yang dibatasi usia - Tampilkan video yang dibatasi usia. Bisa diubah nanti dari pengaturan. + Tampilkan konten yang dibatasi usia Galat jaringan Tidak bisa memuat semua thumbnail Apakah maksud anda \"%1$s\"\? @@ -130,6 +129,8 @@ Bersihkan Filter Menghapus audio pada beberapa resolusi + Ingat properti popup + Ingat ukuran dan posisi terakhir popup Popup Ubah ukuran Kontrol gestur pemutar @@ -344,11 +345,14 @@ Ekspor sebelumnya Tidak bisa mengimpor langganan Tidak bisa mengekspor langganan - Impor langganan YouTube dengan mengunduh berkas yang diekspor: + Impor langganan YouTube dari Google takeout: \n -\n1. Kunjungi URL ini: %1$s +\n1. Buka URL ini: %1$s \n2. Masuk ketika ditanya -\n3. Unduhan akan dimulai (itulah berkas ekspornya) +\n3. Klik \"Semua data termasuk\", lalu pada \"Batal pilih semua\", lalu pilih hanya \"langganan\" dan klik \"OK\" +\n4. Klik \"Langkah berikutnya\" dan kemudian pada \"Buat ekspor\" +\n5. Klik tombol \"Unduh\" setelah muncul dan +\n6. Dari download takeout zip ekstrak file .json (biasanya di bawah \"YouTube dan YouTube Music / subscriptions /subscriptions.json\") dan impor di sini. Impor profil SoundCloud dengan mengetik URL atau ID anda: \n \n1. Aktifkan \"mode desktop\" di peramban web (situs tidak tersedia untuk perangkat seluler) @@ -566,7 +570,7 @@ Buang ditonton Video ini dibatasi usia. \n -\nAktifkan \"Konten yang dibatasi usia\" di dalam pengaturan jika anda ingin melihatnya. +\nAktifkan \"%1$s\" di dalam pengaturan jika anda ingin melihatnya. Konten ini belum didukung oleh NewPipe. \n \nSemoga akan didukung pada versi berikutnya. @@ -588,7 +592,7 @@ \nJadi pilihlah yang sesuai yang kamu inginkan: kecepatan atau kelengkapan informasi. Teks asli dari layanan akan tampil pada item stream Tampilkan waktu yang lalu sebenarnya pada item - Mode terbatas Youtube + Tampilkan \"Mode Terbatas\" Youtube Oleh %s Dibuat oleh %s Thumbnail avatar kanal @@ -623,4 +627,14 @@ Tombol tindakan pertama Ubah ukuran thumbnail yang ditampilkan di notifikasi dari rasio aspek 16:9 ke 1:1 (mungkin terdistorsi) Ubah ukuran thumbnail ke rasio aspek 1:1 + Tampilkan kebocoran memori + Ditambahkan + Tambahkan + Hapus kuki yang disimpan oleh NewPipe saat anda memecahkan reCAPTCHA + Kuki reCAPTCHA telah dihapus + Hapus kuki reCAPTCHA + YouTube menyediakan \"Mode Terbatas\" yang menyembunyikan video konten dewasa. + Menampilkan konten yang mungkin tidak cocok untuk anak-anak karena memiliki batasan umur (seperti 18+). + Minta Android menyesuaikan warna notifikasi sesuai dengan warna utama di thumbnail (perhatikan bahwa ini tidak tersedia di semua perangkat) + Warnai notifikasi \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index d420cff78..8a4f0134e 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -10,22 +10,22 @@ Scarica Cerca Impostazioni - Forse cercavi \"%1$s\"\? + Forse cercavi «%1$s»\? Condividi con - Scegli Browser + Scegli browser rotazione - Cartella Video Scaricati + Cartella video scaricati I video scaricati saranno salvati qui Scegli la cartella per i video scaricati - Risoluzione Predefinita + Risoluzione predefinita Riproduci con Kodi Installare l\'app Kore\? - Mostra \"Riproduci con Kodi\" + Mostra «Riproduci con Kodi» Mostra l\'opzione per riprodurre video tramite Kodi Audio Formato Audio Predefinito Scarica - Mostra video \"Prossimo\" e \"Simili\" + Mostra video «Prossimo» e «Simili» URL non supportato Lingua Predefinita per Contenuti Video e Audio @@ -36,9 +36,9 @@ Mi piace Impossibile creare la cartella di download \'%1$s\' Creata la cartella per i download \'%1$s\' - Usa Lettore Video Esterno - Usa Lettore Audio Esterno - Cartella Audio Scaricati + Usa lettore video esterno + Usa lettore audio esterno + Cartella audio scaricati Gli audio scaricati saranno salvati qui Scegli la cartella per gli audio scaricati Tema @@ -58,11 +58,10 @@ Impossibile impostare il menu di download I contenuti in diretta non sono ancora supportati Contenuti - Contenuti con Restrizioni di Età - Mostra video con restrizioni di età. È possibile modificare questa opzione nelle Impostazioni. - Tocca \"Cerca\" per iniziare + Mostra Contenuti con Restrizioni di Età + Tocca «Cerca» per iniziare \n - Riproduzione Automatica + Riproduzione automatica Riproduci i video quando NewPipe viene aperto da un\'altra app Diretta Impossibile analizzare completamente il sito web @@ -116,16 +115,18 @@ Più tardi Apri in modalità Popup - Modalità Popup + Modalità popup Riproduzione in modalità Popup Disattivato L\'audio potrebbe non essere disponibile per alcune risoluzioni - In Sottofondo + In sottofondo Popup - Risoluzione Predefinita Popup - Mostra Altre Risoluzioni + Risoluzione predefinita popup + Mostra altre risoluzioni Solo alcuni dispositivi possono riprodurre video 2K/4K Formato Video Predefinito + Ricorda Poprietà Popup + Ricorda dimensione e posizione della finestra Popup Controllo Gesti Lettore Multimediale Usa i gesti per controllare luminosità e volume del lettore multimediale Suggerimenti Ricerca @@ -220,8 +221,8 @@ Tendenze Top 50 Nuovi e Popolari - Mostra suggerimento \"Tieni premuto per accodare\" - Nei \"Dettagli\" dei video, mostra suggerimento alla pressione dei pulsanti per la riproduzione Popup o in Sottofondo + Mostra suggerimento «Tieni premuto per accodare» + Nei «Dettagli» dei video, mostra suggerimento alla pressione dei pulsanti per la riproduzione Popup o in Sottofondo Accoda in Sottofondo Accodato in Popup Riproduci Tutto @@ -271,8 +272,8 @@ Attenzione: Impossibile importare tutti i file. Questa operazione sostituirà le tue impostazioni attuali. Scarica il video - Mostra Informazioni - Playlist Salvate + Mostra informazioni + Playlist salvate Aggiungi a Trascina per riordinare Crea @@ -305,8 +306,8 @@ A breve qualcosa apparirà qui ;D Debug Generato automaticamente - Il monitoraggio delle perdite di memoria potrebbe causare la mancata risposta dell\'applicazione durante il dumping dell\'heap - Segnala Errori \"Out-of-lifecycle\" + Il monitoraggio di memory leak potrebbe causare la mancata risposta dell\'applicazione durante il dumping dell\'heap + Segnala errori «fuori del ciclo di vita» Forza la segnalazione di eccezioni Rx non consegnabili al di fuori del ciclo di vita dell\'attività dopo la chiusura Usa Ricerca Rapida (Imprecisa) Consente al lettore multimediale di spostarsi più velocemente, ma con precisione ridotta. Spostamenti di 5, 15 o 25 secondi non funzionano con questo. @@ -328,16 +329,19 @@ Esportazione precedente Impossibile importare le iscrizioni Impossibile esportare le iscrizioni - Importa le iscrizioni di YouTube scaricando il file d\'esportazione: -\n -\n1. Vai a questo URL: %1$s -\n2. Accedi quando richiesto -\n3. Il download del file d\'esportazione dovrebbe partire in automatico - Importa un profilo SoundCloud inserendo l\'URL o il tuo ID: -\n -\n1. Abilitare la \"modalità desktop\" del browser (il sito non è disponibile per i dispositivi mobili) -\n2. Aprire questo URL: %1$s -\n3. Accedere quando richiesto + Importa le iscrizioni di YouTube scaricando il file d\'esportazione: +\n +\n1. Vai a questo URL: %1$s +\n2. Accedi quando richiesto +\n3. Premi \"Tutti i dati inclusi\", \"Deseleziona tutto\", seleziona solo \"iscrizioni\" e premi \"OK\" +\n4. Premi \"Passaggio successivo\" e poi \"Crea esportazione\" +\n5. Premi il pulsante \"Scarica\" quando compare +\n6. Dall\'archivio ZIP scaricato, estrai il file JSON (solitamente in \"YouTube and YouTube Music/subscriptions/subscriptions.json\") e importalo qui. + Importa un profilo SoundCloud inserendo l\'URL o il tuo ID: +\n +\n1. Abilitare la «modalità desktop» del browser (il sito non è disponibile per i dispositivi mobili) +\n2. Aprire questo URL: %1$s +\n3. Accedere quando richiesto \n4. Copiare l\'URL del profilo a cui si viene indirizzati. iltuoID, soundcloud.com/iltuoid Tieni presente che questa operazione può consumare una grande quantità di traffico dati. @@ -483,7 +487,7 @@ Limita Coda Download Ogni volta verrà chiesta la destinazione dei file. \nScegli SAF se vuoi scaricare su una scheda SD esterna - \"Storage Access Framework\" consente di salvare file su una memoria esterna. + Lo Storage Access Framework consente di salvare file su una memoria esterna. \nAlcuni dispositivi non sono compatibili Elimina posizioni di riproduzione Elimina tutte le posizioni di riproduzione @@ -524,7 +528,7 @@ Consentire la visualizzazione sopra altre applicazioni Lingua Applicazione Predefinita di Sistema - Premere \"Fatto\" quando risolto + Premere «Fatto» quando risolto Fatto Video @@ -590,13 +594,13 @@ Canzoni Questo video ha restrizioni di età. \n -\nAttivare \"Contenuti con Restrizioni di Età\" nelle Impostazioni per poterlo vedere. +\nAttivare «%1$s» nelle Impostazioni per poterlo vedere. Sì, anche quelli visaualizzati parzialmente Saranno rimossi gli elementi della playlist già visualizzati, sia precedenti che successivi. \nSei sicuro\? L\'azione è irreversibile! Rimuovere i gli elementi già visti\? Rimuovi Elementi Visti - Modalità con Restrizioni (YouTube) + Attiva la «Modalità con restrizioni» di YouTube I testi originali dei servizi saranno visibili negli elementi video Mostra i tempi originali degli elementi Immagine del Canale @@ -624,13 +628,23 @@ Azione Pulsante 4 Azione Pulsante 3 Azione Pulsante 2 - Azione Pulsante 1 + Azione primo pulsante Buffering Nella notifica compatta è possibile visualizzare al massimo 3 azioni! Casuale Notifica Niente Ripeti - Ridimensiona Copertina alla Proporzione 1:1 + Ridimensiona copertina alla proporzione 1:1 Modifica la proporzione della copertina del video mostrata nella notifica da 16:9 a 1:1 (può introdurre distorsioni) + Mostra Memory Leak + Aggiunto alla coda + Accoda + Cancella i cookie che NewPipe memorizza quando si risolve un reCAPTCHA + Cookie reCAPTCHA puliti + Pulisci Cookie reCAPTCHA + Consente di usufruire della «Modalità con restrizioni» di YouTube, che esclude contenuti potenzialmente inappropriati per i minori. + Mostra contenuti che hanno un limite di età (es. 18+). Potrebbero essere inadatti ai bambini. + Lascia che Android modifichi il colore della notifica, secondo il colore principale della copertina (funzione non disponibile per tutti i dispositivi) + Colora Notifica \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index def7fb93d..1375253be 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -2,18 +2,18 @@ %1$s に公開 動画プレイヤーが見つかりません。VLC をインストールしますか? - 入手 + インストール キャンセル ブラウザで開く 共有 - 保存 + ダウンロード 検索 設定 - もしかして \"%1$s\" ですか? + もしかして: \"%1$s\" 共有 ブラウザを選択 回転 - 動画を保存する場所 + 動画を保存するフォルダー ダウンロードした動画をここに保存します 動画ファイルをダウンロードするフォルダーを選択して下さい デフォルトの解像度 @@ -23,12 +23,12 @@ Kodi メディアセンター経由で動画を再生するための設定を表示します 音声 デフォルトの音声形式 - 保存 + ダウンロード 「次の動画」と「関連動画」を表示 - URLは使用できません - 優先言語 + 対応していないURLです + デフォルトの言語 動画と音声 - %1$s ビュー + %1$s 再生 動画 プレビュー サムネイル ビデオ再生、時間: 投稿者アイコンのサムネイル @@ -45,21 +45,20 @@ 外観 その他 ネットワークエラー - 音声を保存する場所 + 音声を保存するフォルダー ダウンロードした音楽ファイルをここに保存します 音楽ファイルをダウンロードするフォルダーを選択して下さい 保存場所 \'%1$s\' を作成できません 保存場所 \'%1$s\' を作成しました エラー - 全てのサムネイルを読み込むことができません + 全てのサムネイルを読み込めませんでした 動画のURLを復号できませんでした - Webサイトを解析できませんでした + ウェブサイトを解析できませんでした コンテンツが利用できません 保存メニューを設定できませんでした 生放送にはまだ対応していません コンテンツ 年齢制限のあるコンテンツを表示 - 年齢制限された動画を表示しています。設定から許可することができます。 ウェブサイトを完全には解析できませんでした 動画を取得できませんでした 申し訳ありません。発生すべきでものではありませんでした。 @@ -73,13 +72,14 @@ 動画 音声 再試行 - 初めにストレージへのアクセスを許可する + ストレージへのアクセスを許可してください 自動再生 NewPipe が他のアプリから呼び出された時、動画を再生します。 不具合を報告 利用者レポートを送る 生放送 - 開始するには \"検索\" をタップ + 開始するには \"検索\" をタップ +\n 開始 一時停止 再生 @@ -122,13 +122,15 @@ 無効 デフォルトの動画形式 デフォルトのポップアップ解像度 - 高い解像度で表示 - 2K/4K ビデオの再生は一部のデバイスのみ再生できます + より高い解像度を表示 + 一部のデバイスのみ2K/4K動画を再生できます バックグラウンド ポップアップ フィルター 更新 クリア + ポップアップの属性を記憶 + ポップアップしたサイズと位置を記憶します ポップアップ サイズを変更 一部の解像度では音声がありません @@ -147,22 +149,22 @@ このアプリについて 貢献者 ライセンス - Android 向けの自由で軽量なストリーミング。 + Android 向けのフリーで軽量なストリーミング。 GitHub で表示 NewPipe のライセンス 翻訳、デザインの変更、コードの整理、動作の重いコードの変更など、アイデアをお持ちではありませんか?ヘルプはいつでも歓迎します。より良いものを一緒に作り上げましょう! ライセンスを読む 貢献する チャンネル登録 - チャンネル登録しました + 登録済み チャンネル登録を解除しました チャンネル登録を変更できません チャンネル登録を更新できません メイン - 登録リスト + 登録チャンネル 新着 検索履歴 - 検索した履歴を記憶します + 検索履歴を記憶します 視聴履歴 再生した履歴を記憶します 再生の再開 @@ -174,7 +176,7 @@ プレイリスト 元に戻す すべて再生 - 通知 + NewPipeの通知 [不明] 動画の再生ができませんでした 回復不能な再生エラーが発生しました @@ -188,7 +190,7 @@ 文字と数字 文字と数字と、多くの特殊文字 寄付 - NewPipe は、あなたに最高の体験を味わってもらうために、ボランティアが自分たちの時間を使って開発しています。開発者たちがコーヒーを飲みながら NewPipe を継続的に改良できるよう、あなたのご支援をお願いします。 + NewPipe は、あなたに最高の体験を提供するために、ボランティアが自分たちの時間を使って開発しています。開発者たちがコーヒーを飲みながら NewPipe を継続的に改良できるよう、ご支援をお願いします。 Webサイト NewPipe の詳しい情報や最新情報については、ウェブサイトをご覧ください。 履歴 @@ -196,14 +198,14 @@ 再生履歴 履歴は無効になっています 履歴 - 履歴に何もありません + 履歴なし 履歴を削除しました アイテムを削除しました このアイテムを検索履歴から削除しますか? メインページのコンテンツ 空白ページ Kioskページ - チャンネル登録ページ + 登録チャンネルページ フィードページ チャンネルページ チャンネルを選択 @@ -247,13 +249,13 @@ コンテンツを読み込んでいます 動画ファイルをダウンロード 情報を表示 - プレイリスト + ブックマークしたプレイリスト サムネイルを読み込む 画像キャッシュを消去しました キャッシュを消去 アプリ内のキャッシュデータをすべて削除します キャッシュが消去されました - 関連動画を自動でキューに追加する + 次のを自動でキューに追加する デバッグ ファイル 動画が見つかりません @@ -298,10 +300,10 @@ 登録リストがエクスポートできませんでした 速度 音程 - バックグラウンド再生の順番待ちに追加 - ポップアップ再生の順番待ちに追加 + バックグラウンド再生のキューに追加 + ポップアップ再生のキューに追加 再生履歴を消去 - 再生したストリームの履歴と再生位置を削除します + 再生した動画の履歴と再生位置を削除します 再生履歴を削除しました。 検索履歴を消去 検索キーワードの履歴を削除します @@ -317,16 +319,16 @@ おおまかなシーク おおまかなシークを使用することで精度が下がる代わりに高速にシークができます。5 秒、15 秒または 25 秒間隔のシークはできません。 すべてのサムネイルの読み込みと保存を無効化します。このオプションを切り替えるとメモリおよびディスク上の画像キャッシュがクリアされます。 - キューに関連動画を追加しつつ、再生を続ける(リピートしない場合) + キューに関連動画を追加して再生を続ける すべての再生履歴を削除しますか? すべての検索履歴を削除しますか? このファイル/コンテンツはありません - %s が登録しています + %s人が登録しています - 視聴なし + 再生なし - 視聴回数 %s 回 + 再生回数 %s再生 1 つのアイテムが削除されました。 支援する @@ -334,12 +336,12 @@ \nNewPipe のプライバシー・ポリシーでは、クラッシュリポート送信時にどのような種類のデータが送信・記録されるかを詳細に説明しています。 NewPipe はコピーレフトなソフトウェアです。あなたは自由にそれを使用し、研究し、そして改善することができます。あなたは、GNU フリーソフトウェア財団が公開する GNU General Public ライセンス バージョン3以降の下に、自由に再配布・修正を行うことができます。 最終再生日時 - 最も再生した動画 + 最も再生された動画 ズーム プレイリスト 「長押しして追加」のヒントを表示 トラック - NewPipe バックグラウンドおよびポップアップのプレイヤーの通知 + NewPipeのバックグラウンドおよびポップアッププレイヤーの通知 新着と人気 長押ししてキューに追加 ポップアップで連続再生を開始 @@ -387,7 +389,7 @@ 新しいタブ タブを選択 アプリの更新 - 催し物 + イベント 新しい NewPipe バージョンの通知 外部記憶装置は利用できません 既定値に戻す @@ -414,7 +416,7 @@ 操作がシステムによって拒否されました ダウンロードに失敗しました ダウンロードが完了しました - %s 件のダウンロード終了 + %s件のダウンロード終了 一意の名前を生成します 上書き この名前のファイルは既に存在します @@ -448,9 +450,9 @@ 従量制課金ネットワークの割り込み モバイルデータ通信に切り替える場合に便利ですが、一部のダウンロードは一時停止できません コメントを表示 - 無効にするとコメントの表示を停止します + 無効にするとコメントを非表示にします 自動再生 - コメントはありません + コメントなし コメントを読み込めませんでした 閉じる 接続タイムアウト @@ -458,7 +460,7 @@ 最後に再生した位置を復元します リスト内の位置 リストに再生位置インジケーターを表示します - データをクリア + データを削除 再生位置を削除しました。 ファイルが移動または削除されました ファイルを上書きできません @@ -484,14 +486,14 @@ ダウンロードフォルダーを変更して有効にします サービスの切り替え、現在の選択: - %s つの動画 + %s本の動画 - デフォルトのキオスク + デフォルトのKiosk 誰も見ていません - %s 視聴中 + %s人が視聴中 - 誰も聞いていません + 誰も聴いていません %s リスナー @@ -499,11 +501,11 @@ 高速早送り/巻き戻し時間 PeerTube インスタンス PeerTube インスタンスを選択する - あなたに最適なインスタンスを探せます: %s + あなたに最適なインスタンスを探す: %s インスタンスを追加 インスタンスの URL を入力 インスタンスを検証することができませんでした - HTTPS な URL のみがサポートされています + HTTPS の URL のみに対応しています インスタンスはすでに存在しています ローカル 最近追加された @@ -579,7 +581,7 @@ アルバム この動画には年齢制限があります。 \n -\n閲覧したい場合、設定から \"年齢制限のあるコンテンツを表示する\" を有効化してください。 +\n閲覧したい場合、設定から \"%1$s\" を有効化してください。 プレイリストに追加される前も追加された後も視聴した動画はプレイリストから削除されます。 \nよろしいですか?この操作は元に戻せません! はい、部分的に視聴した動画も削除します @@ -587,7 +589,7 @@ 視聴済みを削除 サービスのオリジナルのテキストが生放送に表示されます アイテムに元の時間を表示 - YouTube 制限付きモード + YouTube 制限付きモードを有効化 %s による %s により作成 チャンネルのサムネイル @@ -617,9 +619,17 @@ 3 番目のアクションボタン 2 番目のアクションボタン 1 番目のアクションボタン - 何もない + なし リピート シャッフル バッファリング 通知 + YouTube は、成人向けの可能性があるコンテンツを除外する「制限付きモード」を提供しています。 + 年齢制限のあるコンテンツを表示します。 + キューに追加 + キューに追加しました + reCAPTCHA を解いたときに NewPipe が保存した Cookie を消去します + reCAPTCHA の Cookie を消去 + reCAPTCHA の Cookie が消去されました + メモリリークを表示 \ No newline at end of file diff --git a/app/src/main/res/values-jv/strings.xml b/app/src/main/res/values-jv/strings.xml index 1fedeed8b..8374c6f7c 100644 --- a/app/src/main/res/values-jv/strings.xml +++ b/app/src/main/res/values-jv/strings.xml @@ -20,6 +20,8 @@ Duduhke komentar Duduhke gambar cilik Durasi cepet maju/mundure + Eling-eling ukuran lan posisi ngambang terakhir + Eling-eling ukuran lan posisi ngambang Ireng Peteng Padhang diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 17b147eda..e0fa9becc 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -55,7 +55,6 @@ NewPipe가 다른 앱에서 호출되었을 때 동영상을 재생합니다 컨텐츠 연령 제한 컨텐츠 - 연령 제한 비디오입니다. 설정 메뉴에서 시청 허용 여부를 변경하실 수 있습니다. 라이브 오류 모든 썸네일을 불러올 수 없습니다 @@ -104,6 +103,8 @@ 일부 기기에서만 2K/4K 해상도 재생이 지원됩니다 기본 비디오 형식 검은 테마 + 팝업 크기 및 위치 기억 + 마지막으로 사용한 팝업 위치 및 크기를 기억합니다 제스처 재생 조작 제스처를 사용해 화면 밝기와 음량을 조절합니다 검색 제안 @@ -547,4 +548,6 @@ 알림에 표시되는 비디오 썸네일을 16:9에서 1:1 비율로 바꿉니다. (왜곡이 생길 수도 있습니다.) 썸네일을 1:1 비율로 하기 %s에 대한 검색 결과 + 셔플 + 연속 재생 \ No newline at end of file diff --git a/app/src/main/res/values-ks/strings.xml b/app/src/main/res/values-ks/strings.xml new file mode 100644 index 000000000..a6b3daec9 --- /dev/null +++ b/app/src/main/res/values-ks/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-ku/strings.xml b/app/src/main/res/values-ku/strings.xml index 79329ddc6..27219649b 100644 --- a/app/src/main/res/values-ku/strings.xml +++ b/app/src/main/res/values-ku/strings.xml @@ -57,6 +57,8 @@ ڕۆشن تاریک ڕه‌ش + بیرهاتنه‌وه‌ی شوێن و قه‌باره‌ی په‌نجه‌ره‌ + بیرهاتنه‌وه‌ی كۆتا قه‌باره‌ و شوێنی په‌نجه‌ره‌ی بچووك باركردنی وێنۆچكه‌كان ناچالاكی بكه‌ بۆ ڕاگرتنی وێنۆچكه‌كان له‌ باركردن و پاشه‌كه‌وتبوون له‌سه‌ر بیرگه‌ی ئامێره‌كه‌ت. \nگۆڕینی ئه‌مه‌ ده‌بێته‌ هۆی سڕینه‌وه‌یان له‌سه‌ر بیرگه‌ی مۆبایله‌كه‌ت. @@ -395,7 +397,6 @@ پیشاندانی ڕێنمایی ”داگرتن تا پاشکۆ” پیشاندانی ڕێنمایی کاتێ لە پاشبنەما یاخوود پەنجەرەدا گرتە دەکرێ لەسەر وردەکاری ڤیدیۆیەک پەرەسەندوو - پیشاندانی ئەو ڤیدیۆیانەی سنوری تەمەنیان بۆ دانراوە. لە ڕێکخستنەکانەوە ڕێگەی پێدەدرێت. ناتوانرێ واژووی بەستەری ڤیدیۆ بخوێنرێتەوە نەگێڕانەوەی کارپێکەر بۆ پێش کێشە ڕوویدا گێڕانەوەی کارپێکەر بۆکاتی پێش کێشە diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 387a3ac23..74dfa50f3 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -1,6 +1,7 @@ - Palieskite „Ieškoti“, kad pradėtumėte + Palieskite „Ieškoti“, kad pradėtumėte +\n %1$s peržiūrų Publikuota %1$s Nerastas srauto grotuvas. Įdiegti VLC\? @@ -17,17 +18,17 @@ Pasirinkti naršyklę sukimas Naudoti išorinį vaizdo grotuvą - Kai kurios raiškos nepalaiko audio, kai ši parinktis įgalinta + Kai kurios raiškos nepalaiko garso, kai ši parinktis įgalinta Naudoti išorinį audio grotuvą iššokančio lango rėžimas Fonas Išokantis langelis Vaizdo įrašų parsisiuntimo aplankas Vieta parsisiųstų vaizdo įrašų laikymui - Įvesti parsiuntimo kelią vaizdo įrašams - Garso įrašų parsiuntimo kelias + Pasirinkite parsiuntimo aplankalą vaizdo įrašams + Garso įrašų parsiuntimo aplankalas Vieta laikyti parsisiųstus garso įrašus - Įveskite atsisiuntimų kelią garso įrašams + Pasirinkite atsisiuntimų aplankalą garso įrašams Automatinis paleidimas Groti vaizdo įrašą, kai NewPipe iškvečiama per kitą programėlę Numatytoji raiška @@ -35,7 +36,7 @@ Rodyti aukštesnes raiškas Tik kai kurie įrenginiai palaiko 2K/4K vaizdo įrašų peržiūrą Groti su Kodi - Kore programėlė nerasta. Įdiegti Kore? + Įdiegti nereastą Kore programėlę\? Rodyti \"Peržiūra su Kodi\" pasirinkimą Rodyti pasirinkimą peržiūrėti vaizdo įrašus per Kodi mediacentrą Garso įrašas @@ -45,12 +46,14 @@ Šviesi Tamsi Juoda + Prisiminti iššokančio lango dydį ir vietą + Prisiminti paskutinį iššokančio lango dydį ir vietą Grotuvo valdymas gestais Naudokite gestus valdyti grotuvo ryškumą ir garsumą Paieškos nuspėjimai Rodyti nuspėjimus, kai ieškoma Atsisiųsti - Rodyti kitus panašius vaizdo įrašus + Rodyti „kitus” ir „panašius” vaizdo įrašus URL nepalaikoma Numatytoji tūrinio kalba Vaizdas ir garsas @@ -60,7 +63,7 @@ Groja fone Grojama iššokančiojo lango rėžime Turinys - Rodyti amžiaus cenzo apribotą turinį + Rodyti amžiumi apribotą turinį Gyvai Atsisiuntimai Atsisiuntimai @@ -83,14 +86,14 @@ Negalima visiškai apdoroti tinklapio Turinys neprieinamas Negalima sutvarkyti atsisiuntimų meniu - Tai gyvas srautas. Tokie kol kas nepalaikomi. + Tiesioginės translecijos yra nepalaikomos Negalima gauti jokio srauto Negalima įkelti jokio paveikslėlio Programėlė/ vartotojo sąsaja nulūžo Atsiprašome, taip neturėjo įvykti. - Raportuoti apie klaidą el. paštu + Pranešti apie šią klaidą el. paštu Atsiprašome, ištiko keletas klaidų. - ATASKAITA + Ataskaita Informacija: Kas nutiko: Kas:\\nUžklausa:\\nTurinio Kalba:\\nTurinio Šalis:\\nProgramėlės Kalba:\\nPaslauga:\\nGMT Laikas:\\nPaketas:\\nVersija:\\nOS versija: @@ -140,7 +143,7 @@ Šis leidimas reikalingas \natidarymui iššokančio lango rėžime reCAPTCHA iššūkis - reCAPTCHA prašomas iššūkis + prašomas reCAPTCHA iššūkis Prenumeruoti Užprenumeruota Kanalas Nebeprenumeruojamas @@ -153,16 +156,15 @@ Saugoti paieškos užklausas vietinėje atmintyje Žiūrėjimo istorija Sekite peržiūrėtus vaizdo įrašus - Atkurti kai dėmesio centre + Paleisti Tęsti grojimą po pertraukčių (pvz. skambučių) - Rodyti laikyti, kad įtraukti patarimą + Rodyti “laikyti kad pakeisti„ patarimą Rodyti patarimą, kai foninis arba langelio rėžimo mygtukas paspaudžiamas vaizdo įrašų detalių puslapyje Grotuvas Elgsena - Istorija + Istorija ir laikmena Foninio grotuvo eilėje Įtraukta į langelio rėžimo grojimo eilę - Apriboto amžiaus vaizdo įrašas. Kad leisti tokius vaizdo įrašus eikite į nustatymus. Grojaraštis Atgal Groti viską @@ -174,7 +176,7 @@ Atstatoma po grotuvo klaidos Nėra rezultatų Čia nieko nėra išskyrus svirplius - Saugyklos prieiga uždrausta + Pirma duokite prieiga prie saugyklos Tūkst. Mln. Mlrd. @@ -241,12 +243,12 @@ Garso nustatymai Laikykite kad įtraukti į eilę Pradėti groti čia - Pradėti groti čia foniniame rėžime - Pradėti groti čia langelio grotuvo rėžime + Pradėti groti foniniame rėžime + Pradėti groti langelio grotuve Nerastas srauto grotuvas (galite įdiegti VLC kad grotumėte). Parsisiųsti srauto failą Rodyti informaciją - Adresynas + Pažymėti grojaraščiai Pridėti į Numatyta tūrinio šalis Paslauga @@ -289,18 +291,18 @@ Visada klausti Gauname informaciją… Įkeliamas pasirinktas turinys - Sukurti naują grojaraštį - Ištrinti grojaraštį - Pervadinti grajaraštį + Naujas grojaraštį + Ištrinti + Pervadinti Pavadinimas Pridėti į grojaraštį Nustatyti kaip grojaraščio paveikslėlį Pridėti grojaraštį į žymes Pašalinti žymes - Ar norite ištrinti šį grojaraštį? - Grojaraštis sėkmingai sukurtas + Ištrinti šį grojaraštį\? + Grojaraštis sukurtas Pridėta į grojaraštį - Grojaraščio paveikslėlis pakeistas + Grojaraščio paveikslėlis pakeistas. Nepavyko ištrinti grojaraščio Nėra antraštės Pritaikyti @@ -310,7 +312,7 @@ Atminties nutekėjimo stebėjimas gali padaryti programėlę nestabilią Pranešti apie Out-of-Lifecycle klaidas Priverstinai pranešti apie \"undeliverable Rx exceptions occurring outside of fragment or activity lifecycle after dispose\" - Išjungti, kad paslėptų komentarai + Išjungti, kad paslėpti komentarus Rodyti komentarus Pasirinkti skirtuką Naujas skirtukas diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 65dc451f0..a450b9503 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -58,6 +58,8 @@ Светла Темна Црна + Запамти го местото и големината на малиот прозорец + Запамти ја последната големина и место на прозорчето Брзо, непрецизно премотување Со непрецизното премотување се пребарува побрзо, но со намалена презицност. Прочитај мали видео-сликички @@ -100,7 +102,6 @@ Ставено на листа, за пуштање во прозорче Содржина Покажи видеа со граница на возрастта - Видео за возрасни. Можете да дозволите вакви видеа преку Поставки. во живо Превземања Превземања diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 982d9e593..84f4f5ff8 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -333,7 +333,6 @@ ഈ വീഡിയോ പ്രായപരിമിതി ഉള്ളതാണ്. \n \nഇത് കാണണമെങ്കിൽ പ്രായനിയന്ത്രണ ക്രമീകരണങ്ങളിൽ മാറ്റം വരുത്തുക. - പ്രായപരിമിതിയുള്ള വീഡിയോ കാണിക്കുന്നു. ഭാവിയിൽ മാറ്റങ്ങൾ വരുത്താനാകും. പ്രായപരിമിതപ്പെടുത്തിയ കന്റെന്റ് കന്റെന്റ് പോപ്പപ്പ് പ്ലേയറിൽ ക്യൂ ചെയ്തിരിക്കുന്നു @@ -398,6 +397,8 @@ ഫാസ്റ്റ്-ഫോർവേർഡ്/റീവൈൻഡ് സമയദൈർഘ്യം Inexact seek ഉപയോഗിക്കുക കുറഞ്ഞ കൃത്യതയോടെ സീക് ചെയ്യാൻ Inexact seek സഹായിക്കുന്നു. 5/15/25 സെക്കൻഡ് സീക്‌ ഈ മോഡിൽ പ്രവർത്തിക്കുകയില്ല. + പോപ്പപ്പിന്റെ അവസാന വലുപ്പവും സ്ഥാനവും ഓർത്തിരിക്കുക + പോപ്പപ്പ് വലുപ്പവും സ്ഥാനവും ഓർത്തിരിക്കുക കട്ട ഇരുട്ട് തീം ഡാർക്ക് തീം ലൈറ്റ് തീം diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 002bdf257..3457fda56 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -61,6 +61,8 @@ Cerah Gelap Hitam + Mengingat saiz dan posisi popup + Mengingat saiz dan posisi popup terakhir Gunakan tinjau laju tidak tepat Membolehkan pemain untuk meninjau ke posisi lebih laju dengan kurang ketepatan Muatkan thumbnail @@ -108,7 +110,6 @@ Beratur pada pemain popup Kandungan Kandungan terhad umur - Tunjukkan video terhad umur. Membenarkan bahan tersebut boleh dilakukan dari Tetapan. LANGSUNG Muat turun Muat turun diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 5ea6d49ef..d4b3ff509 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -50,12 +50,12 @@ (Eksperimentelt) Tving nedlasting av trafikk gjennom Tor for forbedret personvern (strømming av videoer støttes ikke enda). Kan ikke opprette nedlastingsmappe \'%1$s\' Opprettet nedlastingsmappen \'%1$s\' - Trykk «Søk» for å komme i gang + Trykk «Søk» for å begynne +\n Automatisk avspilling Spiller en video når NewPipe blir forespurt av et annet program Innhold Aldersbegrenset innhold - Vis aldersbegrenset video. Å tillate slikt materiale kan gjøres fra innstillingene. Feil Kunne ikke laste inn alle miniatyrbilder Kunne ikke dekryptere signaturen til videoens nettadresse @@ -134,6 +134,8 @@ Hva er nytt Bakgrunn Oppsprett + Husk oppsprettsegenskaper + Husk siste størrelse og posisjon for oppsprettsvinduet Søkeforslag Vis søkeforslag ved søk Søkehistorikk @@ -565,9 +567,7 @@ Strøm sist oppdatert: %s Denne videoen er aldersbegrenset. \n -\nSkru på «Aldersbegrenset innhold» i innstillingene hvis du vil se den. -\n -\nHvis du ønsker å se den, skru på \"Aldersbegrenset innhold\" i innstillingene. +\nSkru på «%1$s» i innstillingene hvis du vil se den. ∞ videoer 100+ videoer Artister @@ -634,4 +634,14 @@ Første handlingstast Skaler miniatyrbilde til 1:1-aspekt Ingenting + Vis minnelekkasjer + Satt i kø + Sett i kø + Tøm reCAPTCHA-kaker + reCAPTCHA-kaker har blitt slettet + Tøm kaker som NewPipe lagrer når du løser en reCAPTCHA + YouTube tilbyr et «Begrenset modus» som skjuler mulig innhold kun for voksne. + Vis innhold som muligens er upassende for barn, siden det har aldersgrense (som 18+). + Få Android til å tilpasse merknadens farge i henhold til hovedfargen på miniatyrbildet (merk at dette ikke støttes på alle enheter) + Fargelegg merknad \ No newline at end of file diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index 02277f32e..8b399ff87 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -60,6 +60,8 @@ प्रकाश गाढा कालो + पपअप आकार र स्थिति सम्झना + पछिल्लो आकार र पपअप को स्थिति सम्झना तेज \'inexact\' खोज्न प्रयोग गर्नुहोस \'Inexact\' प्लेयर कम सटीक छिटो स्थितिहरू गर्न खोज्न अनुमति दिन्छ खोज्छन्। 5, 15 वा 25 सेकेन्ड को लागि खोजी यो काम गर्दैन। थम्बनेल लोड @@ -110,7 +112,6 @@ पपअप प्लेयरमा लामबद्ध सामग्री उमेर प्रतिबन्धित सामग्री - उमेर प्रतिबन्धित भिडियोहरु देखाऊ। भविष्यमा यो सेटिङ परिवर्तन सम्भव छ। प्रत्यक्ष डाउनलोडहरु डाउनलोडहरु diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml index 342decedd..59fb83b9e 100644 --- a/app/src/main/res/values-nl-rBE/strings.xml +++ b/app/src/main/res/values-nl-rBE/strings.xml @@ -1,27 +1,28 @@ - Tik op zoeken voor te beginnen + Tik op \"Zoeken\" om te beginnen +\n %1$s keer bekeken Gepubliceerd op %1$s - Er is geen videospeler met streamondersteuning gevonden. Wilt u VLC installeren\? - Geen speler met streamondersteuning gevonden (je kan VLC installeren om af te spelen). + Er is geen stream videospeler gevonden. Wilt u VLC installeren\? + Geen speler met stream ondersteuning gevonden (je kan VLC installeren om af te spelen). Installeren Annuleren In browser openen - Openen in pop-upmodus + Openen in pop-up modus Delen Downloaden - Streambestand downloaden + Stream bestand downloaden Zoeken Instellingen - Bedoelde je: %1$s\? + Bedoelde je \"%1$s\"\? Delen met Kies browser rotatie Externe videospeler gebruiken Verwijdert het geluid bij sommige resoluties Externe audiospeler gebruiken - Pop-up-modus + Pop-up modus Abonneer Geabonneerd Abonnement opgezegd @@ -35,31 +36,33 @@ Achtergrond Pop-up Toevoegen aan - Downloadlocatie voor video’s - Gedownloade videobestanden worden hier opgeslaan + Download locatie voor video’s + Gedownloade videobestanden worden hier opgeslagen Kies de downloadlocatie voor videobestanden - Downloadmap voor audio - Gedownloade audiobestanden worden hier opgeslaan + Audio download map + Gedownloade audiobestanden worden hier opgeslagen Kies de downloadlocatie voor audiobestanden Automatisch afspelen - Speelt video’s af wanneer dat NewPipe vanuit een anderen app word geopend - Standaardresolutie - Standaardresolutie voor pop-up + Speelt video’s af wanneer NewPipe vanuit een andere app wordt geopend + Standaard resolutie + Standaard resolutie voor pop-up Hogere resoluties weergeven Slechts enkele toestellen kunnen 2K- en 4K-video\'s afspelen Afspelen met Kodi Wilt u de missende Kore-app installeren\? Toon “Afspelen met Kodi”-optie - Toont een optie voor ne video op een Kodi media center af te spelen + Toont een optie om video af te spelen op een Kodi media center Audio - Standaardaudioformaat - Standaardvideoformaat + Standaard audio formaat + Standaard video formaat Thema Licht Donker Zwart + Onthoud grootte en positie van pop-up + Onthoud laatste grootte en positie van pop-up Snel, minder exact spoelen gebruiken - Minder exact spoelen laat de speler sneller posities zoeken met verminderde precisie + Minder exact spoelen laat de speler sneller posities zoeken met verminderde precisie. 5, 15 en 25 seconden werken niet. Miniatuurvoorbeelden laden Schakel dit uit voor het laden van miniatuurvoorbeelden te verhinderen; dit bespaart mobiele gegevens en geheugen. Het wijzigen van deze instelling wist het geheugen en de afbeeldingscache. Afbeeldingscache gewist @@ -67,29 +70,29 @@ Alle gecachete webpagina-gegevens wissen Metagegevens-cache gewist Volgende stream automatisch in wachtrij plaatsen - Automatisch een gerelateerde stream toekennen bij het afspelen van de laatste stream in een niet-herhalende wachtlijst + Automatisch een gerelateerde stream toekennen bij het afspelen van de laatste stream in een niet-herhalende afspeelwachtlijst Veegbesturing - Gebruikt vegen voor de helderheid en het volume van de speler aan te passen + Gebruik gebaren om de helderheid en het volume van de speler aan te passen Zoeksuggesties Toon suggesties bij zoeken Zoekgeschiedenis Sla zoekopdrachten lokaal op - Geschiedenis & cache - Kijkgeschiedenis bijhouden - Hervat bij focus - Ga verder met afspelen na onderbrekingen (zoals telefoongesprekken) + Geschiedenis bekijken + Geschiedenis bekeken video\'s bijhouden + Hervat afspelen + Ga verder met afspelen na onderbrekingen (b.v. telefoongesprekken) Downloaden Toont ‘Volgende’ en ‘Vergelijkbare’ video’s - Toont tip ‘Ingedrukt houden voor toe te voegen’ - Toont tip wanneer dat den achtergrond- of pop-upknop wordt ingedrukt op de videogegevenspagina + Toon tip ‘Ingedrukt houden om toe te voegen’ + Toon tip als de achtergrond- of pop-up knop wordt ingedrukt in de video \"Details:\" URL wordt niet ondersteund - Standaardinhoudsland + Standaard land Dienst - Standaardtaal voor inhoud + Standaard taal voor inhoud Speler Gedrag - Video & audio - Geschiedenis & cache + Video en audio + Geschiedenis en cache Pop-up Uiterlijk Overige @@ -99,9 +102,8 @@ Toegevoegd aan wachtrij voor achtergrondspeler Toegevoegd aan wachtrij voor pop-upspeler Inhoud - Inhoud met leeftijdsbeperking - Toont video met leeftijdsbeperking. Toelaten van deze soort video’s kan ingeschakeld worden in de Instellingen. - LIVE + Toon inhoud met leeftijdsbeperking + Live Downloads Downloads Foutrapport @@ -144,7 +146,7 @@ Kon geen streams vinden Kon afbeelding niet laden App/UI gecrasht - Kon deze stream nie afspelen + Kon deze stream niet afspelen Onherstelbare spelerfout opgetreden Bezig met herstellen van spelerfout Externe spelers ondersteunen deze soorten koppelingen niet @@ -153,21 +155,21 @@ Geen audiostreams gevonden Deze map bestaat niet Bestand/inhoudsbron bestaat niet - Het bestand bestaat niet of ge zijt onvoldoende gemachtigd voor het te lezen/dernaar te schrijven - Den bestandsnaam mag niet blanco zijn - Der is een fout opgetreden: %1$s + Het bestand bestaat niet of u bent onvoldoende gemachtigd om het te lezen of ernaar te schrijven + De bestandsnaam mag niet blanco zijn + Er is een fout opgetreden: %1$s Geen streams beschikbaar voor downloaden Sorry, dit zou niet mogen gebeuren. - Fout melden via e-mail - Sorry, der traden enkele fouten op. - MELDEN + Meld deze fout via e-mail + Sorry, er is iets fout gegaan. + Melden Info: - Wat is der gebeurd: - Wat:\\nVerzoek:\\nTaal van inhoud:\\nDienst:\\nTijd in GMT:\\nPakket:\\nVersie:\\nVersie van besturingssysteem: + Wat er is gebeurd: + Wat:\\nVerzoek:\\nTaal van inhoud:\\nLand:\\nTaal van applicatie:\\nDienst:\\nGMT tijd:\\nPakket:\\nVersie:\\nVersie van besturingssysteem: Uw opmerking (in het Engels): Details: Videovoorbeeldminiatuur - Videovoorbeeldminiatuur + Speel video, tijd: Avatarminiatuur van uploader Duimen Duimen omlaag @@ -176,17 +178,17 @@ Meld een probleem Gebruikersrapport Geen resultaten - Niks te zien - Versleep voor de volgorde te wijzigen - Kan downloadmap ‘%1$s’ niet aanmaken - Downloadmap ‘%1$s’ aangemaakt + Helemaal niets hier te zien + Versleep om de volgorde te wijzigen + Kan download map ‘%1$s’ niet aanmaken + Download map ‘%1$s’ aangemaakt Video Geluid Opnieuw proberen - Toegang tot opslag geweigerd - K + Geef eerst toegang tot opslag + k M - B + mld. Geen abonnees %s abonnee @@ -219,13 +221,14 @@ Fout Server wordt niet ondersteund Bestand bestaat al - Verkeerden URL of internet niet beschikbaar + Verkeerde URL of internet niet beschikbaar NewPipe is aan het downloaden Tik voor meer informatie - Efkens geduld… + Even geduld… Gekopieerd naar klembord - Kies een beschikbare downloadmap - Deze toestemming is vereist voor te openen in pop-upmodus + Kies een beschikbare download map + Deze toestemming is vereist om +\nte openen in pop-up modus reCAPTCHA-uitdaging reCAPTCHA-uitdaging gevraagd Download @@ -234,7 +237,7 @@ Vervangend teken Letters en cijfers Meeste speciale tekens - Genen app gevonden voor dit bestand mee af te spelen + Er is geen app geïnstalleerd die dit bestand kan afspelen Over NewPipe Instellingen Over @@ -247,13 +250,13 @@ Licenties Vrij en licht streamen voor Android. Bijdragen - Hulp is altijd welkom, of ge nu nieuwe ideeën hebt, vertalingen kunt bijdragen, wijzigingen hebt voor het ontwerp, de code kunt opkuisen of der zelfs grote wijzigingen in wilt maken. Hoe meer hulp, hoe beter dat het wordt! + Hulp is altijd welkom, of u nu nieuwe ideeën hebt, vertalingen kunt bijdragen, wijzigingen hebt voor het ontwerp, de code kunt opkuisen of er zelfs grote wijzigingen in wilt maken. Hoe meer hulp, hoe beter dat het wordt! Bekijken op GitHub Doneren - NewPipe word door vrijwilligers in hunne vrijen tijd ontwikkeld voor u de beste ervaring te brengen. Geefd iets terug zodat onze ontwikkelaars NewPipe nóg beter kunnen maken terwijl da’ ze van hun taske koffie genieten. + NewPipe wordt door vrijwilligers in hun vrije tijd ontwikkeld om jou de beste ervaring te brengen. Geef wat terug zodat onze ontwikkelaars NewPipe nóg beter kunnen maken terwijl ze van hun kopje koffie genieten. Teruggeven Website - Bezoekt de website van NewPipe voor meer informatie en ’t laatste nieuws. + Bezoek de website van NewPipe voor meer informatie en het laatste nieuws. Licentie van NewPipe Licentie lezen Geschiedenis @@ -264,12 +267,12 @@ De geschiedenis is leeg Geschiedenis gewist Item verwijderd - Wilt ge dit item uit uw zoekgeschiedenis verwijderen? - Wilt ge dit item uit uw kijkgeschiedenis verwijderen? - Wilt ge alle items uit uw geschiedenis verwijderen? + Wilt u dit item verwijderen uit uw zoekgeschiedenis\? + Wil je dit item uit afspeel geschiedenis verwijderen\? + Wilt u alle items uit uw geschiedenis verwijderen\? Laatst afgespeeld Meest afgespeeld - Content van hoofdpagina + Inhoud van hoofdpagina Blanco pagina Kioskpagina Abonnementenpagina @@ -277,7 +280,7 @@ Kanaalpagina Selecteer een kanaal Nog niet geabonneerd op een kanaal - Selecteer ne kiosk + Selecteer een kiosk Geëxporteerd Geïmporteerd Geen geldig ZIP-bestand @@ -290,22 +293,22 @@ Verwijderen Details Audio-instellingen - Houdt ingedrukt voor toe te voegen aan wachtrij + Houd ingedrukt om toe te voegen aan wachtrij Begint hier met afspelen Begint met afspelen in de achtergrond - Begint met afspelen in nieuwe pop-up + Afspelen in pop-up Menu openen Menu sluiten - Hier zal der binnenkort iets verschijnen ;D + Hier zal binnenkort iets verschijnen ;D Voorkeursactie voor openen - Standaardactie bij openen van inhoud — %s + Standaard actie bij openen van inhoud — %s Videospeler Achtergrondspeler Pop-upspeler Altijd vragen Info ophalen… - Bezig me laden van gevraagden inhoud - Nieuwen afspeellijst + Bezig met laden van gevraagde inhoud + Nieuwe afspeellijst Verwijderen Hernoemen Naam @@ -313,21 +316,21 @@ Instellen als miniatuur voor afspeellijst Afspeellijst toevoegen aan bladwijzers Bladwijzer verwijderen - Dezen afspeellijst verwijderen\? + Deze afspeellijst verwijderen\? Afspeellijst aangemaakt Toegevoegd aan afspeellijst Miniatuur voor afspeellijst gewijzigd. - Den afspeellijst kon niet verwijderd worden. + De afspeellijst kon niet verwijderd worden. Geen bijschriften - Passen + Passend Opvullen Inzoomen Automatisch gegenereerd Bijschriften - Bijschriftgrootte en achtergrondstijlen wijzigen. Vereist nen herstart van den app. - Het monitoren van geheugenlekken kan dervoor zorgen da’ den app nie’ goe meer reageerd + Bijschrift grootte en achtergrond stijlen wijzigen. Vereist herstart van de app. + Het monitoren van geheugenlekken kan ervoor zorgen dat de app niet goed meer reageert Out-of-lifecycle-fouten melden - Forceerd het melden van nie-bezorgbare Rx-uitzonderingen die gebeuren buiten fragments- of activiteitscyclus + Forceer het melden van niet-bezorgbare Rx-uitzonderingen buiten fragment of activiteitscyclus Importeren/exporteren Importeren Importeren uit @@ -336,56 +339,59 @@ Bezig met exporteren… Bestand importeren Vorige exportering - Kon abonnementen nie importeren - Kon abonnementen nie exporteren - Importeerd uw YouTube-abonnementen door het exportbestand te downloaden: + Kon abonnementen niet importeren + Kon abonnementen niet exporteren + Importeer je YouTube-abonnementen vanaf Google Takeout: \n \n1. Ga naar dit adres: %1$s -\n2. Logd in op uwen account -\n3. Den download me het exportbestand zou nu moeten starten - Importeerd een SoundButt-profiel door den URL of ID dervan in te voeren: +\n2. Log in op je account +\n3. Klik op \"Alle YouTube-gegevens inbegrepen\", dan op \"Selectie van alle items ongedaan maken\", dan selecteer alleen \"abonnementen\" en klik op \"OK\" +\n4. Klik op \"Volgende stap\", dan op \"Export maken\" +\n5. Klik op de knop \"Downloaden\" nadat deze verschijnt +\n6. Uit de Takeout zipfile, pak de .json uit (gebruikelijk in de folder \"YouTube en YouTube Music/abonnementen/abonnementen.json\") en importeer deze hier. + Importeer een SoundCloud-profiel door de URL of het ID ervan in te voeren: \n -\n1. Kiesd ne webbrowser en schakeld bureaubladmodus in (de website is nie beschikbaar voor mobiele apparaten) +\n1. Kies een webbrowser en schakel bureaubladmodus in (de website is niet beschikbaar voor mobiele apparaten) \n2. Ga naar dit adres: %1$s -\n3. Logd in op uwen account -\n4. Kopieerd de koppeling van de pagina waarop da’ ge terechtkomd (da’s uwe profiel-URL). - uwenID, soundbutt.com/uwenid - Let op: deze actie kan veel MB’s van uw netwerk gebruiken. -\n -\nWild ge doorgaan? +\n3. Log in op uw account +\n4. Kopieer de koppeling van de pagina waar u op terechtkomt (dat is uw profiel-URL). + uwID, soundbutt.com/uwid + Let op: deze actie kan veel MB’s van uw netwerk gebruiken. +\n +\nWilt u doorgaan\? Afspeelsnelheidsbesturing Tempo Toon Ontkoppelen (kan ruis veroorzaken) - Kijkgeschiedenis wissen - Verwijdert de geschiedenis van afgespeelde streams - De ganse kijkgeschiedenis verwijderen\? - Kijkgeschiedenis verwijderd. + Wissen afspeel geschiedenis + Verwijdert de geschiedenis van bekeken video\'s en afspeelposities + Alle afspeel geschiedenis verwijderen\? + Afspeel geschiedenis verwijderd. Zoekgeschiedenis wissen Verwijdert de gebruikte zoektermen - De ganse zoekgeschiedenis verwijderen\? + De hele zoekgeschiedenis verwijderen\? Zoekgeschiedenis verwijderd. 1 item verwijderd. - NewPipe is vrije software: ge kunt het gebruiken, bestuderen, delen en verbeteren zoveel als dat ge maar wilt. Ge kunt het terug uitgeven en/of aanpassen volgens de voorwaarden van de GNU General Public License, gepubliceerd door de Free Software Foundation, versie 3 van de licentie, of (indien gewenst) eender welke latere versie. - Wild ge d’instellingen ook importeren? + NewPipe is vrije software: u kan het gebruiken, bestuderen, delen en verbeteren zoveel u maar wil. U kan het opnieuw uitgeven en/of aanpassen volgens de voorwaarden van de GNU General Public License, gepubliceerd door de Free Software Foundation, versie 3 van de licentie, of (indien gewenst) om het even welke latere versie. + Wilt u ook de instellingen importeren\? Privacybeleid van NewPipe - ’t NewPipe-project neemt uw privacy ter harte. Daarom verzameld den app geen gegevens zonder uw toestemming. -\n’t Privacybeleid van NewPipe legd in detail uit welke gegevens da’ der worden verzonden en opgeslagen wanneer da’ g’een crashrappor indiend. + Het NewPipe-project neemt privacy serieus. Daarom verzamelt de app geen gegevens zonder uw toestemming. +\nNewPipe\'s privacybeleid legt gedetailleerd uit welke gegevens verstuurd en opgeslagen worden als u een crashrapport verstuurt. Privacybeleid lezen - Voor d’Europese privacywet (ook wel GDPR genoemd) na te leven, wijzen w’u op ’t nieuw privacybeleid van NewPipe. Leesd ’t aandachtig. -\nGe moet ’t aanvaarden voor ons ’t bugrapport te sturen. + Om de Europese Algemene Verordening Gegevensbescherming (ook wel: AVG of GDPR) na te leven, wijzen we u op het nieuwe privacybeleid van NewPipe. Lees dit zorgvuldig. +\nU moet het beleid aanvaarden om ons het foutrapport te kunnen opsturen. Aanvaarden Weigeren Onbeperkt Resolutie beperken bij gebruik van mobiele gegevens - Minimaliseren bij overschakelen naar anderen app - Actie bij overschakelen van videospeler naar anderen app — %s + Minimaliseren bij overschakelen naar andere app + Actie bij overschakelen van videospeler naar andere app — %s Geen Afspelen in achtergrond Afspelen in pop-up Doorspoelen tijdens stilte Stap - Standaardwaarden herstellen + Resetten Kanalen Afspeellijsten Nummers @@ -394,32 +400,32 @@ Nieuw tabblad Kiest een tabblad Veegbesturing voor volume - Gebruikt vegen voor het volume van de speler aan te passen + Gebruik gebaren om het volume van de speler aan te passen Veegbesturing voor helderheid - Gebruikt vegen voor de helderheid van de speler aan te passen + Gebruik gebaren om de helderheid van de speler aan te passen Updates Gebeurtenissen Bestand verwijderd Appupdatemelding Meldingen voor nieuwe versies van NewPipe Externe opslag niet beschikbaar - Downloaden naar externe SD-kaart is nog niet mogelijk. Downloadmap terug instellen\? - Standaardtabbladen worden gebruikt, fout bij het lezen van de opgeslagen tabbladen - Standaardinstellingen herstellen - Wilt ge de standaardinstellingen herstellen\? + Downloaden naar externe SD-kaart is niet mogelijk. Download map opnieuw instellen\? + Fout bij het lezen van de opgeslagen tabbladen, waardoor standaard tabbladen worden gebruikt + Standaard instellingen herstellen + Wil je de standaard instellingen herstellen\? Aantal abonnees niet beschikbaar Welke tabbladen er worden weergegeven op de hoofdpagina Selectie Conferenties Updates - Toont een melding voor den app bij te werken wanneer dat er een nieuwe versie beschikbaar is - Lijstweergavemodus + Toon een melding om de app bij te werken indien er een nieuwe versie beschikbaar is + Lijstweergave modus Lijst Raster Auto Weergave wisselen - NewPipe-update beschikbaar! - Tikt voor te downloaden + NewPipe update is beschikbaar! + Tik om te downloaden Voltooid gepauzeerd toegevoegd aan wachtrij @@ -431,31 +437,214 @@ %s downloads voltooid Unieke naam genereren Overschrijven - Der bestaat al een gedownload bestand met deze naam - Der is al een download met deze naam bezig + Er bestaat al een gedownload bestand met deze naam + Er is al een download met deze naam bezig Foutmelding weergeven Code Het bestand kan niet aangemaakt worden De doelmap kan niet aangemaakt worden Toelating geweigerd door het systeem - Beveiligde verbinding is mislukt + Kon geen beveiligde verbinding opzetten Kon de server niet vinden Kan geen verbinding maken met de server De server verzendt geen gegevens - De server aanvaardt geen meerdradige downloads, probeert het opnieuw met @string/msg_threads = 1 + De server aanvaardt geen multi-threaded downloads, probeer het opnieuw met @string/msg_threads = 1 Niet gevonden Nabewerking mislukt Stoppen Maximaal aantal pogingen Maximaal aantal pogingen vooraleer dat de download wordt geannuleerd - Pauzeren bij overschakelen naar mobiele gegevens + Pauzeren bij mobiele data verbinding Nuttig bij het gebruik van mobiele data, hoewel sommige downloads niet uitgesteld kunnen worden Commentaren weergeven - Schakelt dit uit voor reacties niet meer weer te geven + Schakel dit uit om reacties te verbergen Automatisch afspelen Geen commentaren Kan commentaren niet laden Sluiten Vooruitgang verloren, omdat het bestand gedeletet werd Resultaten aan het tonen voor: %s + Afspeellijst pagina + Door %s + Gemaakt door %s + Kanaal avatar afbeelding + Deze inhoud wordt nog niet ondersteund door NewPipe. +\n +\nHopelijk zal dit bij een toekomstige versie ondersteund worden. + Denk je dat het laden van de feed te sloom is\? Zo ja, probeer snel laden in te schakelen (in de instellingen of door op onderstaande knop te drukken). +\n +\nNewPipe biedt twee strategieën aan voor het laden van de feed: +\n• Het hele abonnementskanaal ophalen, wat sloom maar compleet is. +\n• Een speciale feed ophalen, wat snel maar meestal incompleet is. +\n +\nHet verschil tussen de twee is dat de snelle meestal wat informatie mist, zoals de duur of type (live of een normale video) van het item en dat er mogelijk minder items zijn. +\n +\nYouTube is een voorbeeld van een service die deze snelle methode aanbiedt door zijn RSS-feed. +\n +\nDe keuze komt dus neer op wat je liever hebt: snelheid of precieze informatie. + Snelle modus uitschakelen + Snelle modus inschakelen + Beschikbaar in sommige services, het is meestal veel sneller, maar kan een beperkte hoeveelheid items en vaak onvolledige informatie (bijv. geen duur, item type, of live status) bevatten. + Uit speciale feed ophalen indien beschikbaar + Altijd updaten + Tijd na de laatste update voordat een abonnement als verouderd wordt beschouwd — %s + Drempel voor feed update + Feed + Toon enkel niet gegroepeerde abonnementen + Nieuw + Wilt u deze groep verwijderen\? + Lege groepsnaam + + %d geselecteerd + %d geselecteerd + + Geen abonnement geselecteerd + Selecteer abonnementen + Feed aan het verwerken… + Feed aan het laden… + Niet geladen: %d + Laatste update nieuws­feed: %s + Kanaalgroepen + + %d dag + %d dagen + + + %d uur + %d uren + + + %d minuut + %d minuten + + + %d seconde + %d seconden + + Door beperkingen van ExoPlayer is de zoekduur ingesteld op %d seconden + Ja, en deels bekeken video\'s + Video\'s die zijn bekeken voor, en na, ze werden toegevoegd aan de afspeellijst worden verwijderd. +\nBent u zeker\? Dit kan niet ongedaan gemaakt worden! + Verwijder bekeken video\'s\? + Verwijder bekeken + Systeem standaard + App taal + Kies een instantie + Het \'Storage Access Framework\' laat downloads naar een externe SD kaart toe. +\nNiet alle toestellen zijn compatibel + Gebruik SAF + Je zal gevraagd worden waar elke download op te slaan. +\nKies SAF als je wilt downloaden naar een externe SD-kaart + Vraag waar te downloaden + U wordt gevraagd waar elk bestand wordt opgeslagen + Pauzeer downloads + Downloads starten + Maximaal 1 bestand tegelijk zal worden gedownload + Limiteer de download wachtrij + %1$d downloads verwijderd + Verwijder gedownloade bestanden + Wilt u de downloadgeschiedenis of alle gedownloade bestanden verwijderen\? + Download geschiedenis verwijderen + Kan deze download niet herstellen + Verbinding time-out + Geen vrije ruimte meer op het apparaat + NewPipe werd gesloten terwijl het bezig was met het bestand + Er staat al een download met deze naam in wacht + Kan bestand niet overschrijven + Er bestaat al een bestand met deze naam + aan het herstellen + In afwachting + Nooit + Enkel via Wi-Fi + Automatisch afspelen — %s + Originele teksten van services zijn zichtbaar in stream items + Toon memory leaks + Automatisch gegenereerd (geen uploader gevonden) + Geluid aanzetten + Dempen + In wachtrij geplaatst + In wachtrij plaatsen + Speel wachtrij af + Meest leuk gevonden + Recent toegevoegd + Lokaal + De taal zal veranderen zodra de app opnieuw is opgestart. + Geen afspeellijst bladwijzers + Selecteer een afspeellijst + Standaard kiosk + Klaar + Tik op \"Klaar\" zodra opgelost + ∞ video\'s + 100+ video\'s + + %s luisteraar + %s luisteraars + + Niemand is aan het luisteren + + %s kijker + %s kijkers + + Niemand is aan het kijken + Toggle service, momenteel geselecteerd: + Controleer aub of er al een probleem bestaat dat uw crash beschrijft. Wanneer u dubbele tickets aanmaakt, neemt dit tijd van ons in beslag die we beter kunnen besteden aan het oplossen van het daadwerkelijke probleem. + In GitHub rapporteren + Kopieer opgemaakt rapport + Geef toestemming voor weergave over andere apps + Stream bestand downloaden + Hulp + Afspeelposities verwijderd. + Alle afspeelposities verwijderen\? + Verwijdert alle geschiedenis van afspeelposities + Verwijder geschiedenis afspeelposities + Verwijder cookies die NewPipe opslaat wanneer u een reCAPTCHA oplost + reCAPTCHA cookies zijn verwijderd + Verwijder reCAPTCHA cookies + Artiesten + Albums + Nummers + Video\'s + Deze video heeft een leeftijdsbeperking. +\n +\nSchakel \"%1$s\" in bij de instellingen als u die wilt zien. + YouTube biedt een \"beperkte modes\" aan, dit verbergt mogelijk materiaal voor volwassenen. + YouTube \"beperkte modus\" aanzetten + Toon inhoud die mogelijk niet geschikt is voor kinderen omwille van een leeftijdslimiet (zoals 18+). + Melding + Kanaal bestaat al + Alleen HTTPS URL\'s worden ondersteund + Kon kanaal niet valideren + Kanaal URL invoeren + Kanaal toevoegen + Vind het kanaal dat u leuk vindt op %s + Selecteer je favoriete PeerTube kanaal + PeerTube kanaal + Kon de URL niet herkennen. In een andere app openen\? + Wis data + Laat afspeeltijd in afspeellijst zien + Posities in lijst + Verder afspelen vanaf laatste positie + Afspelen hervatten + Volgende stream automatisch in wachtrij plaatsen + De actieve wachtrij wordt vervangen + Veranderen van één speler naar een andere kan jouw wachtrij vervangen + Vraag bevestiging om wachtrij te wissen + Duur voor-/achteruit spoelen + Niets + Aan het bufferen + Shuffle + Herhaal + Je kan maximaal drie acties selecteren om te tonen in de compacte notificatie! + Pas elke notificatie actie hieronder aan door er op te tikken. Selecteer tot drie acties die getoond worden in de compacte notificatie door gebruik te maken van de selectie vakjes aan de rechterkant. + Vijfde actie knop + Vierde actie knop + Derde actie knop + Tweede actie knop + Eerste actie knop + Schaal de miniatuurafbeelding van de video die getoond wordt in de notificatie van 16:9 naar 1:1 verhouding (kan vervorming creëren) + Schaal miniatuurafbeelding naar verhouding 1:1 + Wijzig de download mappen + Toon oorspronkelijke tijd geleden op items + Laat Android de kleur van de notificatie aanpassen, op basis van de meest voorkomende kleur in de thumbnail (let op: niet beschikbaar op elk apparaat) + Notificatie kleur aanpassen \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 498540de4..1cae07326 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -30,7 +30,7 @@ Standaardtaal voor inhoud Externe videospeler gebruiken Externe audiospeler gebruiken - Audio en video + Video en audio Videovoorbeeldminiatuur Speel video, tijd: Gebruikersafbeelding van uploader @@ -45,8 +45,7 @@ Overig Speelt af op achtergrond Inhoud - Inhoud met leeftijdsbeperking - Toon video met leeftijdsbeperking. Toestaan van dit soort video’s kan worden ingeschakeld in de Instellingen. + Toon inhoud met leeftijdsbeperking Fout Netwerkfout Kan niet alle miniatuurvoorbeelden laden @@ -73,7 +72,8 @@ Video Geluid Opnieuw proberen - Druk op \"zoeken\" om te beginnen + Druk op \"zoeken\" om te beginnen +\n Automatisch afspelen Speelt video’s af als NewPipe vanuit een andere app wordt geopend Live @@ -85,7 +85,7 @@ Wat:\\nVerzoek:\\nTaal van inhoud:\\nTaal van land:\\nTaal van Applicatie:\\nDienst:\\nTijd in GMT:\\nPakket:\\nVersie:\\nVersie van besturingssysteem: Meld een probleem Gebruikersrapport - Geef eerst toegang tot de opslag + Geef eerst toegang tot opslag Begin Pauzeren Afspelen @@ -103,7 +103,7 @@ Druk voor meer informatie Even geduld… Gekopieerd naar klembord - Selecteer een downloadmap in de Instellingen + Selecteer een download map in de Instellingen Zwart reCAPTCHA-uitdaging reCAPTCHA-uitdaging gevraagd @@ -129,6 +129,8 @@ Filter Verversen Wissen + Onthoud de eigenschappen van de pop-up + Onthoud laatste grootte en positie van pop-up Pop-up Bezig met wijzigen van grootte Verwijdert geluid bij sommige resoluties @@ -230,7 +232,7 @@ Verwijderen Details Audio-instellingen - Houd ingedrukt om toe te voegen aan de wachtrij + Houd ingedrukt om toe te voegen aan wachtrij [Onbekend] Begin hier met afspelen Begin hier met afspelen in de achtergrond @@ -327,11 +329,14 @@ Vorige exportering De abonnementen kunnen niet worden geïmporteerd De abonnementen kunnen niet worden geëxporteerd - Importeer je YouTube-abonnementen door het exportbestand te downloaden: -\n + Importeer je YouTube-abonnementen vanaf Google Takeout: +\n \n1. Ga naar dit adres: %1$s \n2. Log in op je account -\n3. De download met het exportbestand zou nu moeten starten +\n3. Klik op \"Alle YouTube-gegevens inbegrepen\", dan op \"Selectie van alle items ongedaan maken\", dan selecteer alleen \"abonnementen\" en klik op \"OK\" +\n4. Klik op \"Volgende stap\", dan op \"Export maken\" +\n5. Klik op de knop \"Downloaden\" nadat deze verschijnt +\n6. Uit de Takeout zipfile, pak de .json uit (gebruikelijk in de folder \"YouTube en YouTube Music/abonnementen/abonnementen.json\") en importeer deze hier. Importeer een SoundCloud-profiel door de URL of het ID ervan in te voeren: \n \n1. Kies een webbrowser en schakel bureaubladmodus in (de website is niet beschikbaar voor mobiele apparaten) @@ -359,7 +364,7 @@ Bijschriftgrootte en -achtergrondstijlen wijzigen. Vereist een herstart van de app. Er is geen app geïnstalleerd die dit bestand kan afspelen Kijkgeschiedenis wissen - Verwijdert de geschiedenis van afgespeelde streams en afspeelposities + Verwijdert de geschiedenis van bekeken video\'s en afspeelposities De gehele kijkgeschiedenis wissen\? Kijkgeschiedenis gewist. Zoekgeschiedenis wissen @@ -403,7 +408,7 @@ Appupdatemelding Meldingen voor nieuwe versies van NewPipe Externe opslag niet beschikbaar - Downloaden naar externe SD-kaart is niet mogelijk. Downloadmap opnieuw instellen\? + Downloaden naar externe SD-kaart is niet mogelijk. Download map opnieuw instellen\? Fout bij het lezen van de opgeslagen tabbladen, waardoor standaardtabbladen worden gebruikt Standaardinstellingen herstellen Wil je de standaardinstellingen herstellen\? @@ -448,7 +453,7 @@ Stop Maximum aantal keer proberen Maximum aantal pogingen voordat de download wordt geannuleerd - Pauzeren bij overschakelen naar mobiele data + Pauzeren bij mobiele data verbinding Handig voor wanneer u naar mobiel internet overschakelt, hoewel sommige downloads niet gepauzeerd kunnen worden Gebeurtenissen Conferenties @@ -464,28 +469,28 @@ Wis data Verander de downloadmappen om effect te bekomen Afspelen hervatten - Herstel vorige afspeelpositie - Posities in afspeellijsten + Verder afspelen vanaf laatste positie + Posities in lijst Laat afspeeltijd in afspeellijst zien Afspeelposities verwijderd. Bestand verplaatst of verwijderd Een bestand met dezelfde naam bestaat al Kan bestand niet overschrijven Er is al een download met deze naam bezig - Geen ruimte meer op het apparaat + Geen vrije ruimte meer op het apparaat Voortgang verloren, omdat bestand was verwijderd Wilt u de downloadgeschiedenis of alle gedownloade bestanden verwijderen\? Limiteer de download wachtrij Er zal maximaal 1 bestand tegelijk worden gedownload - Download starten + Downloads starten Downloads pauzeren - Vraag waar bestanden geplaatst moeten worden - U zal worden gevraagd waar u bestanden wilt opslaan + Vraag waar bestanden gedownload worden + U wordt gevraagd waar het bestand wordt opgeslagen Je zal gevraagd worden waar elke download op te slaan. \nKies SAF als je wilt downloaden naar een externe SD-kaart Gebruik SAF Verwijder afspeelposities - Verwijder alle afspeelposities + Verwijdert alle afspeelposities Alle afspeelposities verwijderen\? Niemand is aan het kijken @@ -498,11 +503,11 @@ %s luisteraars De taal zal veranderen zodra de app opnieuw is opgestart. - Standaardkiosk - Duur van snel voor-/achteruit zoeken - PeerTube instanties - Favoriete PeerTube instanties instellen - Vind het kanaal dat je leuk vind op %s + Standaard kiosk + Duur voor-/achteruit spoelen + PeerTube kanaal + Selecteer je favorite PeerTube kanaal + Vind het kanaal dat je leuk vindt op %s Kanaal toevoegen Kanaal URL invoeren Kon kanaal niet valideren @@ -518,7 +523,7 @@ Verwijder gedownloade bestanden %1$d downloads verwijderd Geef toestemming voor weergave over andere apps - Applicatie taal + App taal Systeem taal gebruiken Druk op \"Klaar\" zodra opgelost Klaar @@ -544,7 +549,7 @@ %d dagen Kanaalgroepen - Nieuws­feed laatste update: %s + Laatste update nieuws­feed: %s Niet geladen: %d Feed aan het laden… Feed aan het verwerken… @@ -586,21 +591,21 @@ \n \nHopelijk zal dit bij een toekomstige versie ondersteund worden. Ja, en deels bekeken video\'s - Video\'s die zijn bekeken voor, en na, dat ze werden toegevoegd aan de playlist worden verwijderd. -\nWeet u het zeker\? Dit kan niet ongedaan gemaakt worden! + Video\'s die zijn bekeken voor, en na, ze werden toegevoegd aan de afspeellijst worden verwijderd. +\nBent u zeker\? Dit kan niet ongedaan gemaakt worden! Verwijder bekeken video\'s\? ∞ video\'s 100+ video\'s Deze video heeft een leeftijdsbeperking. \n -\nSchakel \"leeftijdsbeperkende inhoud\" in bij de instellingen als u die wilt zien. +\nSchakel \"%1$s\" in bij de instellingen als u die wilt zien. Verwijder bekeken Originele teksten van services zijn zichtbaar in stream-items - YouTube beperkte modus - Laat orginele tijd geleden zien - De avatar-miniatuur van het kanaal + YouTube \"beperkte modus\" aanzetten + Laat originele tijd geleden zien + Kanaal avatar afbeelding Door %s - Gecreëerd door %s + Gemaakt door %s Afspeellijst pagina Toon enkel ongegroepeerde abonnementen Geen afspeellijst bookmarks @@ -632,4 +637,14 @@ Schaal de miniatuurafbeelding van de video die getoond wordt in de notificatie van verhouding 16:9 naar 1:1 (dit kan vervorming creëren) Schaal de miniatuurafbeelding tot verhouding 1:1 Auto-wachtrij + Toon memory leaks + In de wachtrij geplaatst + In de wachtrij plaatsen + Verwijder cookies die NewPipe opslaat wanneer u een reCAPTCHA oplost + reCAPTCHA cookies zijn verwijderd + Verwijder reCAPTCHA cookies + YouTube biedt een \"beperkte modes\" aan, dit verbergt mogelijk materiaal voor volwassenen. + Toon inhoud die mogelijk niet geschikt is voor kinderen omwille van een leeftijdslimiet (zoals 18+). + Laat Android de kleur van de notificatie aanpassen, op basis van de meest voorkomende kleur in de thumbnail (let op: niet beschikbaar op elk apparaat) + Notificatie kleur aanpassen \ No newline at end of file diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml index 4363fb57a..9a172ec2d 100644 --- a/app/src/main/res/values-oc/strings.xml +++ b/app/src/main/res/values-oc/strings.xml @@ -62,6 +62,8 @@ Clar Escur Negre + Se remembrar la talha e la posicion del fenestron + Se remembrar las darrièras talha e posicion del fenestron Utilzar la recèrca rapida inexacta La recèrca inexacta permet a l\'utilizaire de recercar mai rapidament una posicion amb mens de precision Durada d\'avançada/reculada rapida diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml new file mode 100644 index 000000000..a6b3daec9 --- /dev/null +++ b/app/src/main/res/values-or/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index dde966622..9f1d56374 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -58,6 +58,8 @@ ਸਫੈਦ ਗੂੜਾ ਕਾਲਾ + ਪੌਪ-ਅਪ ਦਾ ਆਕਾਰ ਅਤੇ ਸਥਿਤੀ ਯਾਦ ਰੱਖੋ + ਪੌਪ-ਅਪ ਦਾ ਆਖਰੀ ਅਕਾਰ ਅਤੇ ਸਥਿਤੀ ਯਾਦ ਰੱਖੋ ਤੇਜ਼ ਪਰ inexact seek ਵਰਤੋ Inexact seek ਵੀਡੀਓ ਨੂੰ ਤੇਜ਼ ਪਰ ਅਣ-ਸਟੀਕ ਢੰਗ ਨਾਲ ਅੱਗੇ-ਪਿੱਛੇ ਲਿਜਾਂਦਾ ਹੈ । ਇਸ ਨਾਲ ਅੱਗੇ-ਪਿੱਛੇ 5,15 ਜਾਂ 25 ਸੈਕੰਡ ਜਾਣਾ ਕੰਮ ਨਹੀਂ ਕਰੇਗਾ। ਥੰਬਨੇਲ ਲੋਡ ਕਰੋ @@ -99,7 +101,6 @@ ਪੌਪ-ਅਪ ਪਲੇਯਰ ਵਿੱਚ ਕਤਾਰਬੱਧ Content ਉਮਰ ਪ੍ਰਤੀਬੰਧਿਤ Content - ਉਮਰ ਪ੍ਰਤੀਬੰਧਿਤ ਵੀਡੀਓ ਦਿਖਾਓ ਸੈਟਿੰਗਸ ਤੋਂ ਅਜਿਹੀ ਸਮੱਗਰੀ ਦੀ ਆਗਿਆ ਦੇਣੀ ਸੰਭਵ ਹੈ. ਲਾਈਵ ਡਾਊਨਲੋਡਸ ਡਾਊਨਲੋਡਸ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 31a7ff591..b94a7e0e8 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1,6 +1,7 @@ - Naciśnij „Szukaj”, aby zacząć + Naciśnij „Szukaj”, aby zacząć +\n Zainstaluj Anuluj Otwórz w przeglądarce @@ -44,8 +45,7 @@ Inne Odtwarzanie w tle Zawartość - Treści z ograniczeniem wiekowym - Pokaż wideo z ograniczeniami wiekowymi. Przyszłe zmiany są możliwe z poziomu ustawień. + Pokaż treści z ograniczeniem wiekowym Na żywo Pobrane Pobrane @@ -113,6 +113,8 @@ Tylko niektóre urządzenia mogą odtwarzać filmy 2K/4K Domyślny format filmu Czarny + Zapamiętaj właściwości wyskakującego okienka + Zapamiętaj ostatni rozmiar i pozycję trybu okienkowego Sterowanie odtwarzaczem za pomocą gestów Użyj gestów, aby sterować jasnością i głośnością odtwarzacza Podpowiedzi wyszukiwania @@ -336,11 +338,14 @@ Poprzedni eksport Import subskrypcji nie powiódł się Eksport subskrypcji nie powiódł się - Aby zaimportować subskrypcje YouTube, potrzebny jest plik eksportu subskrypcji. Możesz go wygenerować w następujący sposób: + Importowanie subskrypcji YouTube z Google Takeout: \n -\n1. Odwiedź stronę: %1$s -\n2. Zaloguj się na swoje konto -\n3. Powinno rozpocząć się pobieranie (to jest twój plik eksportu) +\n1. Przejdź do tego adresu URL: %1$s +\n2. Zaloguj się, gdy zostaniesz o to poproszony +\n3. Kliknij na \"Wybrałeś wszystkie dane z Youtube\", a następnie na \"Odznacz wszystkie\", potem wybierz tylko \"subskrypcje\" i kliknij \"OK\" +\n4. Kliknij na \"Następny krok\", a następnie na \"Utwórz eksport\" +\n5. Kliknij przycisk \"Pobierz\", gdy się pojawi i +\n6. Z pobranego archiwum zip wyodrębnij plik .json (zazwyczaj pod ścieżką \"YouTube i YouTube Music/subskrypcje/subskrypcje.json\") i zaimportuj go tutaj. Ta operacja może wygenerować duże użycie danych. \n \nCzy chcesz kontynuować? @@ -599,7 +604,7 @@ Piosenki Ten film ma ograniczenia wiekowe. \n -\nWłącz „Treści z ograniczeniami wiekowymi” w ustawieniach, jeśli chcesz je zobaczyć. +\nWłącz „%1$s” w ustawieniach, jeśli chcesz je zobaczyć. Tak, i częściowo oglądane filmy Filmy, które zostały obejrzane przed i po dodaniu do playlisty, zostaną usunięte. \nJesteś pewien\? Tego nie da się cofnąć! @@ -607,7 +612,7 @@ Usuń oglądane Oryginalne teksty z usług będą widoczne w elementach strumienia Pokaż oryginalny czas - Tryb ograniczony YouTube + Włącz tryb ograniczonego dostępu YouTube\'a Przez %s Utworzone przez %s Miniatura awatara kanału @@ -642,4 +647,14 @@ Przycisk pierwszej akcji Skaluj miniaturę wideo wyświetlaną w powiadomieniu z proporcji 16: 9 do 1: 1 (może powodować zniekształcenia) Skaluj miniatury do proporcji 1:1 + Dodane do kolejki + Dodaj do kolejki + Pokaż wycieki pamięci + Wyczyść ciasteczka, które NewPipe przechowuje po rozwiązaniu reCAPTCHA + Ciasteczka reCAPTCHA zostały wyczyszczone + Wyczyść ciasteczka reCAPTCHA + YouTube udostępnia \"Tryb ograniczonego dostępu\", który ukrywa treści potencjalnie dla dorosłych. + Pokaż treści nieodpowiednie dla dzieci, ponieważ mają ograniczenia wiekowe (np. 18+). + Niech Android dostosuje kolor powiadomienia zgodnie z głównym kolorem na miniaturze (nie jest to dostępne na wszystkich urządzeniach) + Pokolorowanie powiadomienia \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 7dd570c2d..9c2b28904 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -7,7 +7,6 @@ Informação: %1$s visualizações Reproduzir - Mostrar vídeo com restrição de idade. É possível alterar esta opção no menu de configurações. Vídeo Reproduz um vídeo se NewPipe for chamado por outro app Autoreprodução @@ -61,7 +60,7 @@ Vídeo e áudio Compartilhar Compartilhar com - Conteúdo com restrição de idade + Mostrar conteúdo com restrição de idade Mostrar \'Próximo\' e \'Similares\' Desculpe, isto não devia ter ocorrido. Iniciar @@ -89,7 +88,8 @@ O site não pôde ser analisado totalmente Capa de visualização do vídeo Transmissões ao vivo ainda não são suportadas - Toque em \"Buscar\" para iniciar + Toque em \"Buscar\" para iniciar +\n Arquivo já existe Threads Link inválido ou internet indisponível @@ -129,6 +129,8 @@ Limpar Popup Segundo plano + Lembrar propriedades do popup + Lembra do último tamanho e posição usado no popup Popup Redimensionando Remove o som em algumas resoluções @@ -324,11 +326,14 @@ Exportação anterior Não foi possível importar inscrições Não foi possível exportar inscrições - Importe inscrições do YouTube baixando o arquivo de exportação: + Importe inscrições do YouTubedo pelo Google takeout: \n \n1. Acesse este link: %1$s \n2. Logue quando solicitado -\n3. O download do arquivo de exportação iniciará +\n3. Clique em \"Todos os dados incluídos\", em seguida, em \"Desmarque todos\", em seguida, selecione apenas \"assinaturas\" e clique em \"OK\" +\n4. Clique em \"Próximo passo\" e, em seguida, em \"Criar exportação\" +\n5. Clique no botão \"Baixar\" depois de aparecer e +\n6. A partir do arquivo zip baixado, retire o arquivo .json (geralmente em \"YouTube e YouTube Music/assinaturas/assinaturas.json\") e importe aqui. Importe um perfil do SoundCloud digitando o URL ou seu ID: \n \n1. Ative o \"modo desktop\" no navegador (o site está indisponível em celulares) @@ -587,9 +592,9 @@ Artistas Álbuns Músicas - Este vídeo tem restrisão de idade. + Este vídeo tem restrição de idade. \n -\nAtive o \"Conteúdo com restrição de idade\" nas configurações se quiser vê-lo. +\nAtive \"%1$s\" nas configurações se quiser vê-lo. Sim, e vídeos parcialmente vistos Vídeos vistos antes e depois de adicionar à lista de reprodução serão removidos. \nTem certeza\? Isto não pode ser desfeito! @@ -597,7 +602,7 @@ Remover vistos Textos originais dos serviços serão visíveis nos itens de transmissão Mostrar tempo original nos itens - Modo restrito do YouTube + Ativar o \"Modo Restrito\" do YouTube Por %s Criado por %s Capa do avatar do canal @@ -632,4 +637,14 @@ Primeiro botão de ação Dimensione a miniatura do vídeo mostrada na notificação de proporção 16:9 para 1:1 (pode apresentar distorções) Dimensione a miniatura para a proporção de 1:1 + Mostrar vazamentos de memória + Na fila + Pôr na fila + Apaga os cookies que o NewPipe armazena quando você resolve um reCAPTCHA + Apagar cookies de reCAPTCHA + Os cookies de reCAPTCHA foram apagados + O YouTube oferece um \"Modo Restrito\" que oculta conteúdo potencialmente adulto. + Mostrar conteúdo possivelmente inadequado para crianças porque tem um limite de idade (como +18). + Permite o Android personalizar a cor da notificação de acordo com a cor principal da miniatura (note que isso não está disponível em todos os dispositivos) + Colorir notificação \ No newline at end of file diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 7cb0075bb..590642311 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -83,9 +83,9 @@ Descargas A processar… Gestos para controlo de volume - Este vídeo é de idade restrita. + Este vídeo é de restrita à idade. \n -\nPara o poder ver, tem que ativar \"Conteúdo com restrição de idade\" nas definições. +\nPara o poder ver, tem que ativar \"%1$s\" nas definições. Ver licença Ajuda Apenas pode usar URL no formato HTTPS @@ -95,7 +95,7 @@ Remover todas as posições de reprodução\? Trocar de vista Limitar fila de descargas - Conteúdo com restrição de idade + Mostrar conteúdo com restrição de idade O projeto NewPipe leva a sua privacidade muito a sério. Sendo assim, não recolhe quaisquer dados sem o seu consentimento. \nA polícia de privacidade do NewPipe explica, em detalhe, os tipos de dados enviados sempre que submete um relatório de erro. Ficheiro @@ -103,7 +103,6 @@ Adicionar a Remove todas as posições de reprodução Criar - Mostrar vídeo com restrição de idade. Alterações serão possíveis nas definições. Alternar orientação Subscrever Artistas @@ -142,7 +141,7 @@ Partilhar Ver política de privacidade Ocorreu um erro compulsivo do reprodutor - Modo restrito do YouTube + Ligar o \"Modo Restringido\" do YouTube Pasta inexistente Tudo Bolas, isto não deveria ter acontecido. @@ -193,11 +192,14 @@ País padrão para conteúdo Aplicação livre de reprodução de emissões para Android. Idioma padrão para conteúdo - Importe subscrições do YouTube descarregando o ficheiro de exportação: + Importar subscrições do YouTube do Google Takeout: \n -\n1. Aceda a este URL: %1$s -\n2. Inicie a sessão -\n3. A descarga será iniciada (esse é o ficheiro de exportação) +\n1. Vá para este URL: %1$s +\n2. Faça o login quando solicitado +\n3. Clique em \"Todos os dados incluídos\", depois em \"Desmarcar todos\", depois selecione apenas \"subscrições\" e clique em \"OK\". +\n4. Clique em \"Próximo passo\" e depois em \"Criar exportação\". +\n5. Clique no botão \"Descarregar\" após aparecer e +\n6. A partir do zip do takeout descarregado extraia o ficheiro .json (normalmente em \"YouTube e YouTube Music/subscriptions/subscriptions.json\") e importe-o aqui. Ativar reprodutor em segundo plano Mais tarde Desafio reCAPTCHA solicitado @@ -635,4 +637,14 @@ Primeiro botão de ação Ajustar miniatura de vídeo mostrada na notificação de 16:9 para 1:1 (pode introduzir distorções) Ajustar miniatura à proporção de 1:1 + Mostrar vazamentos de memória + Enfileirado + Pôr na fila + Limpar cookies que NewPipe armazena quando resolve um reCAPTCHA + Os cookies reCAPTCHA foram limpos + Limpar cookies reCAPTCHA + O YouTube fornece um \"Modo Restrito\" que oculta conteúdo potencialmente para adultos. + Mostrar conteúdo possivelmente impróprio para crianças porque tem um limite de idade (como 18+). + Fazer com que o Android personalize a cor da notificação de acordo com a cor principal na miniatura (note que esta não está disponível em todos os aparelhos) + Colorir a notificação \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 133571cd1..860dc20b5 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -56,8 +56,7 @@ Não foi possível processar o site Conteúdo indisponível Conteúdo - Conteúdo com restrição de idade - Mostrar vídeo com restrição de idade. Alterações serão possíveis nas definições. + Mostrar conteúdo com restrição de idade Não foi possível processar totalmente o site Não foi possível configurar o menu de descargas As emissões em direto ainda não são suportadas @@ -126,12 +125,14 @@ Mostrar resoluções mais altas Apenas alguns dispositivos conseguem reproduzir vídeos em 2K/4K Popup + Lembrar propriedades de popup Popup Filtrar Recarregar Limpar Segundo plano Remove o áudio em algumas resoluções + Lembrar do último tamanho e posição do popup Redimensionar Controlo de reprodução por gestos Utilizar gestos para controlar o brilho e o volume do reprodutor @@ -322,11 +323,14 @@ Exportação anterior Não foi possível importar as subscrições Não foi possível exportar as subscrições - Importe subscrições do YouTube descarregando o ficheiro de exportação: + Importar subscrições do YouTube do Google Takeout: \n -\n1. Aceda a este URL: %1$s -\n2. Inicie a sessão -\n3. A descarga será iniciada (esse é o ficheiro de exportação) +\n1. Vá para este URL: %1$s +\n2. Faça o login quando solicitado +\n3. Clique em \"Todos os dados incluídos\", depois em \"Desmarcar todos\", depois selecione apenas \"subscrições\" e clique em \"OK\". +\n4. Clique em \"Próximo passo\" e depois em \"Criar exportação\". +\n5. Clique no botão \"Descarregar\" após aparecer e +\n6. A partir do zip do takeout descarregado extraia o ficheiro .json (normalmente em \"YouTube e YouTube Music/subscriptions/subscriptions.json\") e importe-o aqui. Importe o seu perfil SoundCloud digitando o URL ou a ID.: \n \n1. Ative o modo desktop do seu navegador web (o site não está disponível para aparelhos móveis) @@ -588,9 +592,9 @@ Artistas Álbuns Músicas - Este vídeo é de idade restrita. + Este vídeo é de restrita à idade. \n -\nPara o poder ver, tem que ativar \"Conteúdo com restrição de idade\" nas definições. +\nPara o poder ver, tem que ativar \"%1$s\" nas definições. Os vídeos que tenham sido vistos antes e depois de serem adicionados à lista de reprodução serão removidos. \nTem a certeza\? Esta ação não pode ser revertida! Sim e também os vídeos parcialmente vistos @@ -598,7 +602,7 @@ Remover visualizados Os textos originais dos serviços serão visíveis nos itens de fluxo Mostrar antiguidade nos itens - Modo restrito do YouTube + Ligar o \"Modo Restringido\" do YouTube Por %s Criado por %s Miniatura do avatar do canal @@ -633,4 +637,14 @@ Pode selecionar, no máximo, três ações para mostrar na notificação compacta! Repetir Quinto botão de ação + Mostrar vazamentos de memória + Enfileirado + Pôr na fila + Limpar cookies que NewPipe armazena quando resolve um reCAPTCHA + Os cookies reCAPTCHA foram limpos + Limpar cookies reCAPTCHA + O YouTube fornece um \"Modo Restrito\" que oculta conteúdo potencialmente para adultos. + Mostrar conteúdo possivelmente impróprio para crianças porque tem um limite de idade (como 18+). + Fazer com que o Android personalize a cor da notificação de acordo com a cor principal na miniatura (note que esta não está disponível em todos os aparelhos) + Colorir a notificação \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index e3a47ae38..fe201910c 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -43,7 +43,6 @@ Redare în fundal Conținut Conținut restricționat în funcție de vârstă - Afișează videoclipuri restricționate în funcție de vârstă. Permiterea vizionării este posibilă din Setări. Eroare Eroare de rețea Nu s-au putut încărca toate thumbnail-urile @@ -127,6 +126,8 @@ pentru a deschide în mod pop-up Sunetul poate lipsi la unele rezoluții Fundal Pop-up + Reține dimensiunea și poziția pop-up-ului + Reține ultima dimensiune și poziție a pop-up-ului Gesturi player Folosește gesturile pentru a controla luminozitatea și volumul player-ului Arată sugestii diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9122f15fd..5c3ef988d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -65,14 +65,13 @@ Подробнее Скопировано в буфер обмена Выберите папку для загрузки позже в настройках - Контент 18+ + Показать контент с возрастным ограничением Ошибка Ваш комментарий (English): Не удалось создать папку для загрузки \"%1$s\" Автовоспроизведение Воспроизводить видео при вызове NewPipe из другого приложения Контент - Видео с возрастными ограничениями. Разрешить подобный контент можно в \"Настройках\" Трансляция Загрузки Загрузки @@ -108,6 +107,7 @@ Только некоторые устройства поддерживают видео в 2K/4K Формат видео по умолчанию Чёрная + Запомнить параметры всплывающего окна Изменять яркость и громкость жестами Всплывающее окно Воспроизведение во всплывающем окне @@ -132,6 +132,7 @@ " млрд" " тыс." Разрешение всплывающего окна + Помнить последние размер и позицию всплывающего окна Поисковые предложения Лучшее разрешение Запрос reCAPTCHA @@ -592,7 +593,7 @@ Период актуальности подписок после обновления — %s Это видео имеет возрастное ограничение. \n -\nВключите \"Контент с возрастным ограничением\" в настройках, если хотите его видеть. +\nВключите \"%1$s\" в настройках, если хотите его видеть. NewPipe не поддерживает этот контент. \n \nВозможно, поддержка появится в следующих версиях. @@ -609,7 +610,7 @@ Удалить просмотренные видео\? Отображать сообщённое сервисом время с момента публикации Исходное время публикации - Безопасный режим YouTube + Включить \"Безопасный режим\" YouTube От %s Создано %s Миниатюра значка канала @@ -644,4 +645,12 @@ Кнопка первого действия Масштабировать эскиз видео, отображаемый в уведомлении, с соотношением сторон 16:9 до 1:1 (может привести к искажениям) Масштабировать эскиз до соотношения сторон 1: 1 + Показать утечки памяти + Файлы cookie reCAPTCHA были удалены + Очистить файлы cookie reCAPTCHA + Показывать контент, который, возможно, не подходит для детей, потому что он имеет возрастное ограничение (например, 18+). + YouTube предоставляет \"Ограниченный режим\", который скрывает потенциально взрослый контент. + Добавлено в очередь + Добавить в очередь + Очистить cookie, которые NewPipe сохраняет при решении reCAPTCHA \ No newline at end of file diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index a6b3daec9..4de232988 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -1,2 +1,38 @@ - \ No newline at end of file + + ᱟᱹᱪᱩᱨ + ᱵᱨᱟᱣᱡᱚᱨ ᱪᱚᱭᱚᱱ ᱢᱮ + ᱥᱟᱞᱟᱜ ᱦᱟᱹᱴᱤᱧ ᱢᱮ + ᱥᱟᱡᱟᱣᱠᱚ + ᱥᱮᱸᱫᱽᱨᱟ + ᱰᱟᱩᱱᱞᱚᱰ + ᱦᱟᱹᱴᱤᱧ + ᱵᱨᱟᱣᱡᱟᱚᱨ ᱨᱮ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ + ᱵᱟᱹᱰᱨᱟᱹ + ᱵᱚᱦᱟᱞ + %1$s ᱧᱮᱞᱠᱚ + ᱮᱦᱲᱵ ᱞᱟᱹᱜᱤᱫ \"ᱥᱮᱸᱫᱽᱨᱟ\" ᱨᱮ ᱚᱛᱟᱭ ᱢᱮ +\n + ᱵᱟᱦᱨᱮ ᱣᱤᱰᱤᱭᱚ ᱯᱞᱮᱭᱟᱹᱨ ᱵᱮᱵᱷᱟᱨ ᱢᱮᱸ + ᱛᱮᱞᱟ ᱫᱮᱠᱷᱟᱣᱜᱽ ᱠᱟᱱᱟᱺ%s + ᱟᱢᱟᱜ ᱚᱨᱛᱷᱚ \"%1$s\" ᱥᱮ\? + ᱥᱴᱨᱤᱢ ᱨᱮᱫ ᱰᱟᱩᱱᱞᱳᱰ ᱢᱮᱸ + ᱯᱚᱯᱚᱯ ᱢᱳᱰ ᱨᱮ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮᱸ + ᱚᱠᱟ ᱥᱴᱨᱤᱢ ᱯᱞᱮᱭᱟᱹᱨ ᱵᱟᱭ ᱧᱟᱢ ᱞᱮᱱᱟ (ᱟᱢ VLC ᱯᱞᱮᱭᱟᱹᱨ ᱵᱚᱦᱟᱞ ᱠᱟᱛᱮ ᱚᱱᱟ ᱨᱮ ᱯᱞᱮ ᱫᱟᱲᱮᱭᱟᱜᱼᱟᱢ)᱾ + ᱚᱠᱟ ᱥᱴᱨᱤᱢ ᱯᱞᱮᱭᱟᱹᱨ ᱵᱟᱭ ᱧᱟᱢ ᱞᱮᱱᱟ ᱾ VLC ᱵᱚᱦᱟᱞ ᱟᱢ ᱥᱮ\? + %1$s ᱨᱮ ᱩᱪᱷᱟᱹᱱ ᱮᱱᱟ + ᱥᱟᱹᱵᱥᱠᱨᱟᱭᱤᱵᱽ + ᱟᱹᱱᱥᱟᱹᱵᱥᱠᱨᱟᱭᱤᱵᱽ + ᱥᱟᱹᱵᱥᱠᱨᱭᱤᱵᱽ ᱮᱱᱟ + ᱥᱟᱹᱵᱥᱠᱨᱭᱤᱯᱥᱚᱱ ᱵᱚᱫᱚᱞ ᱵᱟᱭ ᱜᱟᱱᱟᱜᱽ ᱠᱟᱱᱟ + ᱪᱟᱱᱱᱮᱹᱞ ᱟᱹᱱᱥᱟᱹᱵᱥᱠᱨᱟᱭᱤᱵᱽ ᱮᱱᱟ + ᱯᱚᱯᱟᱹᱯ ᱢᱳᱰ + ᱵᱟᱦᱨᱮ ᱟᱹᱰᱤᱭᱚ ᱯᱞᱮᱭᱟᱹᱨ ᱵᱮᱵᱽᱦᱟᱨ ᱢᱮᱸ + ᱱᱟᱶᱟ ᱴᱮᱵ + ᱵᱩᱻᱠᱢᱟᱨᱠ ᱯᱞᱮᱞᱤᱥᱴ ᱠᱚ + ᱥᱟᱹᱵᱥᱠᱨᱤᱯᱥᱚᱱ ᱠᱚ + ᱢᱩᱞ + ᱤᱱᱯᱷᱚ ᱫᱮᱠᱷᱟᱣ ᱢᱮᱸ + ᱥᱟᱹᱵᱥᱠᱨᱤᱯᱥᱚᱱ ᱵᱟᱝ ᱟᱹᱯᱰᱮᱴ ᱜᱟᱱᱚᱜᱽ ᱱᱟᱠᱟ + ᱥᱟᱲᱮ ᱠᱚ ᱛᱤᱱᱟᱹᱝ ᱜᱟᱱ ᱨᱤᱡᱚᱞᱭᱩᱥᱚᱱ ᱠᱚ ᱴᱷᱮᱱ ᱚᱪᱚᱜᱽ ᱟᱭ + \ No newline at end of file diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index c78968c34..f7c357f7f 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -1,10 +1,13 @@ - Importa sas iscritziones iscarrighende su documentu de esportatzione: + Importa sas iscritziones dae Google takeout: \n \n 1. Bae a custu URL: %1$s \n 2. Intra cando ti l\'ant a pedire -\n 3. Diat dèpere incumintzare un\'iscarrigamentu (cussu est su documentu de esportatzione) +\n 3. Incarca in \"Totu sos datos incluidos\", a pustis in \"Boga sa seletzione a totus\" e a pustis galu ischerta petzi \"iscritziones\" e incarca \"AB\" +\n4. Incarca in \"Passu imbeniente\" e a pustis in \"Crea un\'esportatzione\" +\n5. Cando aparit, incarca in su butone \"Iscarrigamentu\" e +\n6. Dae s\'archìviu zip iscarrigadu estrai su documentu .json (de sòlitu tenet su nùmene\"\"YouTube and YouTube Music/subscriptions/subscriptions.json\") e importa·lu inoghe. Esportatzione de s\'iscritzione fallida Importatzione de s\'iscritzione fallida Esportatzione anteposta @@ -269,7 +272,7 @@ Cronologia de chirca iscantzellada. Custu vìdeu tenet unu lìmite de edade. \n -\nAllughe \"Cuntenutos limitados pro edade\" in sas impostatziones si lu cheres pompiare. +\nAllughe \"%1$s\" in sas impostatziones si lu cheres pompiare. Perunu riproduidore de flussos agatadu (pro lu riproduire podes installare VLC). Pàgina de s\'iscalita Ammustra petzi sas iscritziones no agrupadas @@ -483,9 +486,8 @@ Iscarrigamentos Iscarrigamentos In direta - Modalidade limitada de YouTube - Ammustra su vìdeu limitadu pro edade. Podes mudare custa optzione dae sas impostatziones. - Cuntenutu limitadu pro edade + Allughe sa modalidade cun restritziones de YouTube + Ammustra sos cuntenutos limitados pro edade Cuntenutos Postu in lista in su riproduidore a ventanedda Pone in lista in s\'isfundu @@ -549,6 +551,7 @@ Longària de s\'avantzamentu e de sa torrada in segus lestros Su moimentu inesatu permitit a su riproduidore de si mòere cara a una positzione in manera prus lestra ma prus pagu pretzisa. Su de si mòere de 5, 15 o 25 segundos non funtzionat, cun custa optzione. Imprea su moimentu inesatu lestru + Ammenta sas propriedades de sa ventanedda Nieddu Iscuru Craru @@ -608,7 +611,8 @@ Perunu riproduidore de flussos agatadu. Cheres installare VLC\? Publicadu su %1$s %1$s visualizatziones - Toca \"Chirca\" pro incumintzare + Toca \"Chirca\" pro incumintzare +\n Mai Cun su Wi-Fi ebbia Incumintza cun sa riprodutzione automaticamente — %s @@ -632,4 +636,15 @@ Su de duos butones de atzione Su de unu butone de atzione Pone in iscala sa miniadura in formadu 1:1 + Ammustra sas pèrdidas de memòria + Annànghidu a sa lista + Pone in lista + Iscantzella sos testimòngios chi NewPipe sarvat cando risolves unu reCAPTCHA + As isboidadu sos testimòngios reCAPTCHA + Isbòida sos testimòngios reCAPTCHA + YouTube frunit una \"Modalidade cun restritziones\" chi cuat sos cuntenudos chi diant pòdere èssere pro adultos. + Ammustra sos cuntenutos chi diant pòdere no èssere adatos pro sos pitzinnos ca tenent unu lìmite de edade (che a 18+). + Pedi a Android de personalizare su colore de sa notìfica sighende su colore printzipale de sa miniadura (ammenta·ti chi custu no est a disponimentu pro totu sos dispositivos) + Colora sas notìficas + Ammenta s\'ùrtima mannària e sa positzione in sa ventanedda \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 598af3883..9d70413d1 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -55,12 +55,12 @@ (Experimentálne) Vyžadovať preberanie cez Tor pre väčšie súkromie (streamovanie videa momentálne nie je podporované). Nemožno vytvoriť adresár na preberanie \'%1$s\' Adresár na preberanie bol vytvorený \'%1$s\' - Ťuknite na \"Vyhľadávanie\" pre začatie + Ťuknite na \"Vyhľadávanie\" +\n Automatické prehrávanie Prehrá video pri zavolaní NewPipe inou aplikáciou Obsah - Vekovo obmedzený obsah - Zobrazit video s vekovým obmezením. Zmenit túto voľbu môžete v nastaveniach. + Zobraziť vekovo obmedzený obsah Naživo Nemožno kompletne zanalyzovať web Nemožno nastaviť menu preberania @@ -130,6 +130,8 @@ Obnoviť Vyčistiť Odoberie audio pri niektorých rozlíšeniach + Zapamätať si parametre mini okna + Zapamätať si posledné nastavenie veľkosti a pozície mini okna Ovládanie prehrávača gestami Používať gestá pre kontrolu jasu a hlasitosti prehrávača Hľadať návrhy @@ -342,11 +344,14 @@ Predchádzajúci export Nemožno importovať odbery Nemožno exportovať odbery - Import odberov služby YouTube pomocou exportovaného zoznamu -\n -\n1. Prejdite na túto adresu URL: %1$s -\n2. Po výzve sa prihláste do svojho účtu -\n3. Sťahovanie by malo začať (to je exportovaný zoznam) + Import odberov služby YouTube pomocou Google takeout: +\n +\n1. Prejdite na túto adresu URL: %1$s +\n2. Po výzve sa prihláste do svojho účtu +\n3. Kliknite na \"Zahrnuté sú všetky údaj služby YouTube\", tam kliknite na \"Zrušiť výber\", zaškrtnite \"odbery\" a potom kliknite na OK +\n4. Kliknite na \"Ďaľší krok\" a potom na \"Vytvoriť export\" +\n5. Po chvíli sa objaví tlačidlo s nápisom \"Stiahnuť\" +\n6. Stiahnutý súbor otvorte a extraktujte .json súbor (nachádza sa v \"/Takeout/YouTube a YouTube Music/odbery/\"). Tento súbor importujte do NewPipe. Importovať SoundCloud profil zadaním URL adresy alebo vášho ID: \n \n1. Prepnite režim na \"desktop\" (web nie je dostupný pre mobilné zariadenia) @@ -606,8 +611,8 @@ Albumy Vekovo obmedzené video. \n -\nPre zobrazenie videa povoľte \"Vekovo obmedzený obsah\" v nastaveniach. - YouTube v obmedzenom režime +\nPre zobrazenie videa povoľte \"%1$s\" v nastaveniach. + Zapnúť \"Obmedzený režim YouTube\" %s Vytvoril %s Minuatúrny avatar kanála @@ -642,4 +647,14 @@ Akčné tlačidlo jedna Zmeniť pomer strán videa zobrazovaného v miniatúre z 16:9 na 1:1 (čo môže spôsobovať skreslenie) Zmenšiť pomer strán miniatúry na 1: 1 + Zobraziť memory leaks + Zaradené do poradia + Zaradiť do poradia + Vymazať cookies, ktoré NewPipe ukladá, keď vyriešite reCAPTCHA + reCAPTCHA cookies boli vymazané + Vymazať cookies reCAPTCHA + YouTube poskytuje \"Obmedzený režim\", ktorý skrýva potenciálny obsah pre dospelých. + Zobraziť obsah, ktorý je možno nevhodný pre deti, pretože má vekovú hranicu (napríklad 18+). + Nechajte Android, aby prispôsobil farbu upozornenia podľa hlavnej farby v miniatúre (nemusí to fungovať na všetkých zariadeniach) + Farby upozornení \ No newline at end of file diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 15781aa36..83aa56fa2 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -59,7 +59,6 @@ Posnetek je pretok v živo. Ta vrsta prenosa še ni podprta. Vsebina Pokaži starostno omejeno vsebino - Starostna omejitev ogleda. Pred ogledom tovrstnih posnetkov, je treba ustrezno nastaviti omejitve. Ni mogoče dokončno razčleniti spletišča. Ni mogoče pridobiti pretoka. Do te napake naj ne bi prišlo. @@ -129,6 +128,8 @@ odpiranje v pojavnem načinu Filter Osveži Počisti + Zapomni si položaj in velikost pojavnega okna + Zapomni si položaj in velikost pojavnega okna Pojavno okno Prilagajanje velikosti Pri nekaterih ločljivostih bo posnetek brez zvoka, če je ta možnost omogočena diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 2fe7b6fda..1d3daea95 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -521,7 +521,6 @@ Kjo video ka kufizime moshe. \n \nAktivizoni \"Përmbajtje me moshë të kufizuar\" tek aranzhimet nëse doni ta shihni. - Shfaq videot me moshë të kufizuar. Ndryshime të tjera janë të mundura nga aranzhimet. Përmbajtje me moshë të kufizuar U radhit në luajtësin popup U radhit në luajtësin në sfond @@ -571,6 +570,8 @@ Kohëzgjatja e kërkimit me shtytje-përpara/-pas Kërkuesi i pasaktë e lejon luajtësin që të kërkojë pozicionet më shpejt më saktësi të reduktuar. Kërkimi për 5, 15 ose 25 sekonda nuk punon me këtë. Përdor kërkuesin e pasaktë por të shpejtë + Mbaj mend madhësinë e fundit dhe pozicionin e popup + Mbaj mend popup E errët E bardhë Tema diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 988365715..1bec9b822 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -57,7 +57,6 @@ Садржај није доступан Садржај Прикажи старосно-ограничени садржај - Старосно-ограничени видео. Премошћавање је доступно у поставкама. Не могу да поставим мени преузимања Ово је ТОК УЖИВО, ово још није подржано. Не могу да рашчланим веб-сајт у целости @@ -130,6 +129,8 @@ Позадина Прозорче Уклања звук на неким резолуцијама + Упамти величину и позицију искачућег прозора + Памти последњу величину и позицију искачућег прозорчета Контроле прејера потезом Користите потезе за управљање осветљајем у јачином звука Предлози у претрази diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index c0b53e546..b4620a3f8 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -43,6 +43,8 @@ Ljust Mörkt Svart + Kom ihåg popupstorlek och position + Kom ihåg popup-rutans senaste storlek och position Gestkontroller för spelare Använd gester för att kontrollera spelarens ljusstyrka och volym Sökförslag @@ -59,7 +61,6 @@ Spelar upp i popup-läge Innehåll Åldersbegränsat innehåll - Videon är åldersbegränsad. Du kan aktivera åldersbegränsade videor i inställningar. Live Nedladdningar Nedladdningar @@ -144,9 +145,9 @@ Ljud Försök igen Bevilja åtkomst till lagringsutrymme först - K + T. mn - B + Mrd. Inga prenumeranter %s prenumerant diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index be7974840..f889ea2bf 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -55,6 +55,8 @@ வெளிர் அடர் கருப்பு + திரைமேல் அளவையும் இடத்தையும் நினைவுகொள் + திரைமேல் நிலையின் கடைசி அளவையும் இடத்தையும் நினைவுகொள் வில்லைப்படத்தைக் காண்பி பட பதுக்ககம் அழிக்கப்பட்டது மேல்நிலைத்தரவின் பதுக்ககம் அழிக்கப்பட்டது @@ -122,7 +124,6 @@ வயது வரம்புக்கு உட்பட்டது அடுத்த தாரையில் தானாக சேர் பின்னணி இயக்கியின் வரிசையில் சேர்க்கப்பட்டது - அமைப்புகள் மூலம் வயது வரையறுக்கப்பட்ட வீடியோக்கலை காணலாம். பிழை அறிக்கை சேனல் சேனல்கள் diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 844fe672e..fb92d9150 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -61,6 +61,8 @@ สว่าง มืด สีดำ + จำขนาดและตำแหน่งของป๊อปอัพ + จำขนาดและตำแหน่งสุดท้ายของป๊อปอัพ ใช้การข้ามที่ไม่แม่นยำ การข้ามช่วงที่ไม่แม่นยำจะทำให้เลื่อนไปยังตำแหน่งเวลาที่ต้องการได้เร็วขึ้น แต่จะลดความแม่นยำในการลากตำแหน่งลง โหลดภาพขนาดย่อ @@ -108,7 +110,6 @@ จัดคิวลงในการเล่นโหมดป๊อปอัพแล้ว เนื้อหา เนื้อหาที่จำกัดอายุ - แสดงวิดีโอที่จำกัดอายุ การอนุญาตดังกล่าวเป็นไปได้จากการตั้งค่า สด ดาวน์โหลด ดาวน์โหลด diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 9f1c69f00..5a1fc3b28 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -26,27 +26,26 @@ Ses dosyaları için indirme dizinini seçin Kendiliğinden oynat NewPipe başka uygulamadan çağrıldığında video oynatır - Öntanımlı çözünürlük + Varsayılan çözünürlük Kodi ile oynat Eksik Kore uygulaması yüklensin mi\? \"Kodi ile oynat\" seçeneğini göster Kodi ortam merkezi üzerinden video oynatmak için bir seçenek göster - Öntanımlı ses biçimi + Varsayılan ses biçimi Tema Koyu Açık İndir \'Sonraki\' ve \'Benzer\' videoları göster Desteklenmeyen URL - Öntanımlı içerik dili + Varsayılan içerik dili Ses Video ve ses Görünüm Diğer Arka planda oynatılıyor İçerik - Yaş kısıtlamalı içerik - Yaş kısıtlamalı videoyu göster. Daha sonra ayarlardan değiştirilebilir. + Yaş kısıtlı içeriği göster Canlı İndirilenler İndirilenler @@ -87,7 +86,7 @@ Duraklat Oynat Sil - Sağlama + Doğrulama Yeni görev Tamam Dosya adı @@ -102,14 +101,14 @@ Panoya kopyalandı Lütfen daha sonra ayarlardan uygun bir indirme dizini belirleyin İndirme menüsü ayarlanamadı - Açılır pencere kipinde aç + Açılır pencere modunda aç Açılır pencere modu - Öntanımlı açılır pencere çözünürlüğü + Varsayılan açılır pencere çözünürlüğü Yüksek çözünürlükleri göster Yalnızca bazı aygıtlar 2K/4K videoları oynatabilir - Öntanımlı video biçimi + Varsayılan video biçimi Siyah - Açılır pencere kipinde oynatılıyor + Açılır pencere modunda oynatılıyor Tümü Kanal Evet @@ -121,7 +120,7 @@ b M B - Bu izin, açılır pencere kipinde + Bu izin, açılır pencere modunda \naçmak için gereklidir reCAPTCHA formu reCAPTCHA formu istendi @@ -130,11 +129,13 @@ Filtrele Yenile Temizle + Açılır pencere özelliklerini hatırla + Açılan pencerenin son boyutunu ve konumunu hatırla Açılır pencere Yeniden boyutlandırılıyor Bazı çözünürlüklerde sesi kaldırır - Oynatıcının parlaklığını ve sesini kontrol etmek için hareketleri kullanın - Hareketli oynatıcı kontrolü + Oynatıcının parlaklığını ve sesini denetlemek için hareketleri kullan + Hareketli oynatıcı denetimi Arama önerileri Arama yaparken önerileri göster En iyi çözünürlük @@ -175,7 +176,7 @@ Oynatmaya devam et Kesintilerden sonra (örneğin telefon çağrısı) oynatmaya devam et Oynatıcı - Davranış + Tercihler Geçmiş ve önbellek Oynatma Listesi Geri al @@ -237,7 +238,7 @@ Bağışta bulunun Web sitesi Daha çok bilgi ve haber için NewPipe web sitesini ziyaret edin. - Öntanımlı içerik ülkesi + Varsayılan içerik ülkesi Hizmet Yönlendirmeyi Değiştir Arka Plana Geç @@ -285,7 +286,7 @@ Sil Yeniden adlandır Ad - Oynatma Listesine Ekle + Oynatma listesine ekle Oynatma listesi küçük resmi olarak ayarla Oynatma listesini yer imlerine ekle Yer imini kaldır @@ -294,7 +295,7 @@ Oynatma listesine eklendi Oynatma listesinin küçük resmi değiştirildi. Oynatma listesi silinemedi. - Altyazı yok + Alt yazı yok Sığdır Doldur Yakınlaştır @@ -329,11 +330,11 @@ \n1. Şu adrese gidin: %1$s \n2. Sorulduğunda hesabınıza giriş yapın \n3. İndirme başlamalı (bu, dışa aktarma dosyasıdır) - URL\'yi veya ID\'nizi yazarak SoundCloud profilini içe aktarın: -\n -\n1. Web tarayıcısında \"masaüstü kipi\" etkinleştirin (site, mobil aygıtlar için uygun değildir) -\n2. Şu adrese gidin: %1$s -\n3. Sorulduğunda giriş yapın + URL\'yi veya ID\'nizi yazarak SoundCloud profilini içe aktarın: +\n +\n1. Web tarayıcısında \"masaüstü modu\" etkinleştirin (site, mobil aygıtlar için uygun değildir) +\n2. Şu adrese gidin: %1$s +\n3. Sorulduğunda giriş yapın \n4. Yönlendirildiğiniz profil URL\'sini kopyalayın. kimliginiz, soundcloud.com/kimliginiz Bu sürecin ağa yük olabileceğini unutmayın. @@ -350,10 +351,10 @@ Ses yüksekliği Ayır (bozulmaya neden olabilir) İndirilebilecek akış yok - Yeğlenen \'aç\' eylemi - İçerik açılırken öntanımlı eylem — %s - Altyazılar - Oynatıcı altyazı metin ölçeğini ve arka plan biçimini değiştirin. Etkili olması için uygulamayı yeniden başlatma gerektirir. + Tercih edilen \'aç\' eylemi + İçerik açılırken varsayılan eylem — %s + Alt yazılar + Oynatıcı alt yazı metin ölçeğini ve arka plan biçimini değiştirin. Etkili olması için uygulamayı yeniden başlatma gerektirir. Bu dosyayı oynatmak için herhangi bir uygulama yüklü değil İzleme geçmişini temizle Oynatılan akışların geçmişini ve kalınan oynatım konumlarını siler @@ -380,7 +381,7 @@ Ana video oynatıcıdan diğer uygulamaya geçiş yaparken eylem — %s Yok Arka plan oynatıcısını küçült - Açılır pencere oynatıcına küçült + Açılır pencere oynatıcısına küçült Sessizlik sırasında hızlı ileri Adım Sıfırla @@ -391,25 +392,25 @@ Abonelikten çık Yeni Sekme Sekmeyi Seçin - Hareketli ses kontrolü - Oynatıcının sesini kontrol etmek için hareketleri kullanın - Hareketli parlaklık kontrolü - Oynatıcının parlaklığını kontrol etmek için hareketleri kullanın + Hareketli ses denetimi + Oynatıcının sesini denetlemek için hareketleri kullan + Hareketli parlaklık denetimi + Oynatıcının parlaklığını denetlemek için hareketleri kullan Güncellemeler Dosya silindi Uygulama Güncelleme Bildirimi Yeni NewPipe sürümü için bildirimler Harici depolama kullanılamıyor Harici SD karta indirmek mümkün değil. İndirme dizini konumu sıfırlansın mı\? - Kayıtlı sekmeler okunamadı, bu nedenle öntanımlılar kullanılıyor - Öntanımlıları geri yükle - Öntanımlıları geri yüklemek istiyor musunuz\? + Kayıtlı sekmeler okunamadı, bu nedenle varsayılanlar kullanılıyor + Varsayılanları geri yükle + Varsayılanları geri yüklemek istiyor musunuz\? Abone sayısı mevcut değil Ana sayfada hangi sekmeler gösterilir Seçim Güncellemeler Yeni bir sürüm mevcut olduğunda uygulama güncellemesi için bir bildirim göster - Liste görünümü kipi + Liste görünümü modu Liste Izgara Otomatik @@ -490,7 +491,7 @@ %s video %s video - Öntanımlı Kiosk + Varsayılan Kiosk Kimse izlemiyor %s izliyor @@ -523,7 +524,7 @@ %1$d indirme silindi Diğer uygulamaların üzerinde görüntüleme izni ver Uygulama dili - Sistem öntanımlısı + Sistem varsayılanı Çözüldüğünde \"Bitti\" düğmesine basın Bitti Videolar @@ -547,37 +548,37 @@ %d gün %d gün - Kanal kümeleri - Besleme en son güncellendi: %s + Kanal grupları + Akış en son güncellendi: %s Yüklenmedi: %d - Besleme yükleniyor… - Besleme işleniyor… + Akış yükleniyor… + Akış işleniyor… Abonelikleri seç Abonelik seçilmedi %d seçildi %d seçildi - Boş küme adı - Bu kümeyi silmek istiyor musunuz\? + Boş grup adı + Bu grubu silmek istiyor musunuz\? Yeni - Besleme - Besleme güncelleme eşiği + Akış + Akış güncelleme eşiği Bir aboneliğin eski sayılmadan önce son güncellemeden sonra geçen zaman — %s Her zaman güncelle - Uygunken adanmış beslemeden edin + Uygunken özel akıştan edinin Bazı servislerde kullanılabilir, genellikle daha hızlıdır ancak kısıtlı sayıda öge ve eksik bilgi (örn. süre, öge türü, canlı durumu olmaksızın) getirilebilir. - Hızlı kipi etkinleştir - Hızlı kipi devre dışı bırak - Beslemenin çok yavaş yüklendiğini mi düşünüyorsunuz\? Öyleyse, hızlı yüklemeyi etkinleştirin (ayarlardan değiştirebilir veya aşağıdaki düğmeye dokunabilirsiniz). + Hızlı modu etkinleştir + Hızlı modu devre dışı bırak + Akışın çok yavaş yüklendiğini mi düşünüyorsunuz\? Öyleyse, hızlı yüklemeyi etkinleştirin (ayarlardan değiştirebilir veya aşağıdaki düğmeye dokunabilirsiniz). \n -\nNewPipe iki besleme yükleme yordamı sunar: +\nNewPipe iki akış yükleme yordamı sunar: \n• Tüm abonelik kanalını edinme, bu yavaş ancak tamdır. \n• Adanmış hizmet son noktası kullanır, bu hızlıdır ancak genellikle tam değildir. \n \nİkisinin arasındaki fark, hızlı olanın genellikle ögenin süresi veya türü (canlı ve sıradan videoları ayrımsayamaz) gibi bazı bilgilerden yoksunluğu ve daha az öge getirmesidir. \n -\nYouTube, RSS beslemesiyle bu hızlı yöntemi sunan servislerden biridir. +\nYouTube, RSS akışıyla bu hızlı yöntemi sunan servislerden biridir. \n \nSeçim, sizin neyi yeğlediğinize kalmış: hız veya kusursuz bilgi. Bu içerik henüz NewPipe tarafından desteklenmiyor. @@ -588,9 +589,9 @@ Sanatçılar Albümler Şarkılar - Bu video yaş kısıtlamalı. + Bu video yaş kısıtlıdır. \n -\nGörmek istiyorsanız ayarlarda \"Yaş kısıtlamalı içerik\" seçeneğini açın. +\nGörmek istiyorsanız ayarlarda \"%1$s\" seçeneğini açın. Oynatma listesine eklendikten önce ve sonra izlenen videolar kaldırılacak. \nEmin misiniz\? Bu geri döndürülemez! Evet ve kısmen izlenmiş videolar @@ -598,17 +599,17 @@ İzleneni kaldır Akış ögelerinde hizmetlerden alınan özgün metinler görünecektir Ögelerde özgün \'… önce\'yi göster - YouTube kısıtlı kip + YouTube\'un \"Kısıtlı Mod\"unu aç %s tarafından %s tarafından oluşturuldu Kanalın avatar küçük resmi - Yalnızca kümelenmemiş abonelikleri göster + Yalnızca gruplanmamış abonelikleri göster Oynatma listesi sayfası Oynatma listesi seç GitHub\'da bildir Biçimlendirilmiş raporu kopyala Sonuçlar gösteriliyor: %s - Lütfen kilitlenmenizi tartışan bir sorunun zaten var olup olmadığını kontrol edin. Yinelenen talepler oluştururken, bizden asıl hatayı düzeltmek için harcayabileceğimiz zamanı alırsınız. + Lütfen hatanızı tartışan sorunun var olup olmadığını kontrol edin. Yinelenen istekler oluştururken, bizden asıl hatayı düzeltmek için harcayabileceğimiz zamanı alırsınız. Henüz oynatma listesi yer imleri yok Asla Yalnızca Wi-Fi\'de @@ -624,8 +625,8 @@ Ara belleğe alınıyor Karıştır Tekrarla - Sıkı bildirimde gösterilecek en fazla üç eylem seçebilirsiniz! - Aşağıdaki her bildirim eylemini üzerine dokunarak düzenleyin. Sağdaki onay kutularını kullanarak sıkı bildirimde gösterilmek üzere en fazla üç tanesini seçin. + Bildirim sekmesinde gösterilecek en fazla üç eylem seçebilirsiniz! + Aşağıdaki her bildirim eylemini üzerine dokunarak düzenleyin. Sağdaki onay kutularını kullanarak bildirim sekmesinde gösterilmek üzere en fazla üç tanesini seçin. Beşinci eylem düğmesi Dördüncü eylem düğmesi Üçüncü eylem düğmesi @@ -633,4 +634,12 @@ Birinci eylem düğmesi Bildirimde gösterilen video küçük resmini 16:9\'dan 1:1 en/boy oranına ölçeklendir (bozulmalara neden olabilir) Küçük resmi 1:1 en/boy oranına ölçeklendir + Bellek sızıntılarını göster + Sıraya eklendi + Kuyruğa ekle + reCAPTCHA çözdüğünüzde NewPipe\'ın sakladığı çerezleri temizle + reCAPTCHA çerezleri temizlendi + reCAPTCHA çerezlerini temizle + YouTube, olası yetişkin içeriği gizleyen \"Kısıtlı Mod\" sağlamaktadır. + Yaş kısıtı (18+ gibi) nedeniyle çocuklara uygun olmayabilecek içeriği göster. \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 7f18c0cc1..996be7347 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -10,7 +10,7 @@ Завантажити Шукати Налаштування - Чи ви мали на увазі: %1$s\? + Чи ви мали на увазі: \"%1$s\"\? Поширити через Оберіть переглядач обертання @@ -45,7 +45,6 @@ Немає доступу до накопичувача Контент Контент з віковими обмеженнями - Показувати відео з віковими обмеженнями. Надалі дозволити програвання таких відео можна у налаштуваннях. Наживо Помилка Помилка мережі @@ -63,7 +62,8 @@ Звіт Інформація: Що сталося: - Натисніть на «пошук» щоб почати + Натисніть на «пошук», аби почати +\n Чорна Завантаження Завантаження @@ -124,6 +124,8 @@ Лише деякі пристрої можуть відтворювати 2K/4K-відео Показувати вищі роздільні здатності Типовий формат відео + Пам\'ятати розмір і позицію вікна + Пам\'ятати останній розмір і позицію вікна Жести керування програвачем Контролювати яскравость та гучність програвача жестами Пошукові пропозиції @@ -619,4 +621,14 @@ Вибрати плейліст Жоден плейліст ще не додано Сторінка плейлісту + Нічого + Буферизація + Перемішати + Повтор + Кнопка п\'ятої дії + Кнопка четвертої дії + Кнопка третьої дії + Кнопка другої дії + Кнопка першої дії + Збільшити мініатюру до масштабу 1:1 \ No newline at end of file diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index a759e397c..cd6866e2e 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -58,6 +58,8 @@ روشن تاریک سیاہ + پاپ اپ جسامت اور مقام کو یاد رکھیں + پچھلی جسامت اور پوپ اپ کا مقام یاد رکھیں بالواسطہ رسائی استعمال کریں بالواسطہ تلاش مشکلات کو کم کر کے پلیئر کو تیز رفتاری سے مقامات تک رسائی کرنے دیتی ہے نظرِ انگشتی لوڈ کریں @@ -99,7 +101,6 @@ پاپ اپ پلیئر میں شامل ہوئی مشمول نازیبا مشمولات - نازیبا ویڈیو دکھائی دے گی۔ ترتیبات سے اس طرح کی مشمولات کی اجازت ممکن ہے۔ براہ راست ڈاؤن لوڈز ڈاؤن لوڈز diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 006be9a08..286a37dc6 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -44,6 +44,8 @@ Sáng Tối Đen + Nhớ kích thước và vị trí của popup + Nhớ kích thước và vị trí lần trước của popup Điều khiển cử chỉ trình phát Sử dụng cử chỉ để điều chỉnh độ sáng và âm lượng Đề xuất tìm kiếm @@ -57,7 +59,6 @@ Phát ở chế độ popup Nội dung Cho phép nội dung có giới hạn độ tuổi - Hiện video có giới hạn độ tuổi. Có thể thay đổi trong phần Cài đặt. Trực tiếp Tải xuống Tải xuống diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index f778b8070..a2bccae5a 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -167,7 +167,6 @@ 在后台播放 内容 受年龄限制的内容 - 显示受年龄限制的视频。可从设置允许此类内容。 直播 下载 下载 @@ -209,6 +208,8 @@ 使用更高的分辨率 仅某些设备支持播放2K / 4K视频 清除 + 记住悬浮窗的尺寸与位置 + 记住最后一次使用悬浮窗的大小和位置 悬浮窗 调整大小 隐藏部分没有音频的分辨率 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index c70419092..de10f58b9 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -42,7 +42,7 @@ 使用 Tor (測試中)為加強私隱,要求通過 Tor 傳送下載流量(暫時不支援串流影片)。 觀看次數:%1$s - 找不到任何串流播放器,您要安裝 VLC 嗎? + 找不到任何串流播放器,要安裝 VLC 嗎? 使用瀏覽器開啟 分享影片 聲音下載路徑 @@ -50,12 +50,12 @@ 選擇聲音檔案的下載路徑 未能建立下載路徑「%1$s」 已建立下載路徑「%1$s」 - 點擊 \"搜索\" 以開始使用 + 點擊 \"搜索\" 以開始使用 +\n 自動撥放 當 NewPipe 被其他程式調用時播放視頻 內容 顯示已設年齡限制的影片 - 此影片設有年齡限制。若要觀看,請先在設定中解除年齡限制。 直播 問題 無法載入全部縮圖 @@ -124,6 +124,8 @@ 移除某些解像度的影片的聲音 背景播放 畫中畫播放 + 記住畫中畫大小及位置 + 記住最近設定的畫中畫大小及位置 以動作控制播放器 使用動作以控制播放器的亮度及音量 搜尋建議 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8127cabb5..37fbbf749 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -63,6 +63,8 @@ 僅部份裝置可播放 2K/4K 影片 預設影片格式 純黑 + 記住懸浮視窗屬性 + 記住上次使用時懸浮視窗的大小和位置 播放器手勢控制 使用手勢來控制播放器的亮度及音量 搜尋建議 @@ -70,8 +72,7 @@ 懸浮視窗 以懸浮視窗播放中 內容 - 年齡限制內容 - 顯示有年齡限制的影片。未來仍可從設定中變更。 + 顯示年齡限制內容 下載 下載 錯誤回報 @@ -322,11 +323,14 @@ 無法匯出訂閱 之前的匯出 檔案不存在或讀取或寫入權限不足 - 透過下載匯出檔來匯入您的 YouTube 訂閱: -\n -\n1. 移至此網址:%1$s + 從 Google Takeout 匯入您的 YouTube 訂閱: +\n +\n1. 移至此網址:%1$s \n2. 當被提示時登入帳號 -\n3. 應該會開始下載(這就是匯出檔 ) +\n3. 點擊「包含所有資料」,然後「取消選取全部」,然後僅選取「訂閱」並點擊「確定」 +\n4. 點擊「下一步」然後「建立匯出」 +\n5. 在「下載」按鈕出現後點擊它,然後 +\n6. 從已下載的 takeout zip 解壓縮 .json 檔(通常會在「YouTube 與 YouTube Music/subscriptions/subscriptions.json」)然後匯入它。 yourID, soundcloud.com/yourid 請記住,此操作可造成昂貴網路花費。 \n @@ -580,7 +584,7 @@ 歌曲 此影片有年鈴限制。 \n -\n如果您想要觀看,請在設定中開啟「年齡限制的內容」。 +\n如果您想要觀看,請在設定中開啟「%1$s」。 是的,以及部份觀看的影片 在新增到播放清單前後的影片將被移除。 \n您確定嗎?此動作無法復原! @@ -588,7 +592,7 @@ 移除已觀看 來自服務的原始文字將在串流項目中可見 在項目上顯示原始時間 - YouTube 受限模式 + 開啟 YouTube 的「受限模式」 由 %s 由 %s 建立 頻道大頭貼縮圖 @@ -623,4 +627,14 @@ 第一動作按鈕 將通知中顯示的影片縮圖從 16:9 縮放到 1:1(可能會導致失真) 把縮圖縮放到 1:1 的長寬比 + 顯示記憶體洩漏 + 已加入佇列 + 加入佇列 + 清除 NewPipe 在您解決 reCAPTCHA 時儲存的 cookie + reCAPTCHA cookies 已被清除 + 清除 reCAPTCHA cookies + YouTube 提供了「受限模式」,可以隱藏潛在的成人內容。 + 顯示可能不適於兒童的內容(因為其有年齡限制,如18歲以上等)。 + 讓 Android 根據縮圖中的主要色彩來自訂通知的顏色(請注意,此功能不是在所有裝置上都能正常運作) + 彩色通知 \ No newline at end of file diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index c0c60e306..851ce1e78 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -21,7 +21,6 @@ use_external_video_player use_external_audio_player - autoplay_through_intent use_oldplayer volume_gesture_control @@ -132,6 +131,8 @@ notification_slot_compact_1_key notification_slot_compact_2_key + notification_colorize_key + video_mp4 video_webm video_3gp @@ -1069,7 +1070,6 @@ lt mk ms - nap nb-no ne nl @@ -1082,6 +1082,7 @@ pt-pt ro ru + sat sc sk sl @@ -1092,8 +1093,10 @@ te th tr + tzm uk ur + uz vi zh-cn zh-hk @@ -1144,7 +1147,6 @@ Lietuvių kalba македонски јазик Bahasa Melayu - napulitano Norsk bokmål Nनेपाली Nederlands (NL) @@ -1157,6 +1159,7 @@ Português (PT) Română русский язык + ᱥᱟᱱᱛᱟᱲᱤ sardu Slovenčina Slovenščina @@ -1167,8 +1170,10 @@ తెలుగు ไทย Türkçe + Tamaziɣt українська мова اردو + O‘zbek Tiếng Việt 简体中文 繁體中文(廣東話) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d8c5c31af..8b53c48df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -72,6 +72,8 @@ Shuffle Buffering Nothing + Colorize notification + Have Android customize the notification\'s color according to the main color in the thumbnail (note that this is not available on all devices) Audio Default audio format Default video format @@ -79,6 +81,8 @@ Light Dark Black + Remember popup properties + Remember last size and position of popup Use fast inexact seek Inexact seek allows the player to seek to positions faster with reduced precision. Seeking for 5, 15 or 25 seconds doesn\'t work with this. Fast-forward/-rewind seek duration @@ -533,7 +537,7 @@ Previous export Could not import subscriptions Could not export subscriptions - Import YouTube subscriptions by downloading the export file:\n\n1. Go to this URL: %1$s\n2. Log in when asked\n3. A download should start (that\'s the export file) + Import YouTube subscriptions from Google takeout:\n\n1. Go to this URL: %1$s\n2. Log in when asked\n3. Click on \"All data included\", then on \"Deselect all\", then select only \"subscriptions\" and click \"OK\"\n4. Click on \"Next step\" and then on \"Create export\"\n5. Click on the \"Download\" button after it appears and \n6. From the downloaded takeout zip extract the .json file (usually under \"YouTube and YouTube Music/subscriptions/subscriptions.json\") and import it here. Import a SoundCloud profile by typing either the URL or your ID:\n\n1. Enable \"desktop mode\" in a web-browser (the site is not available for mobile devices)\n2. Go to this URL: %1$s\n3. Log in when asked\n4. Copy the profile URL you were redirected to. yourID, soundcloud.com/yourid Keep in mind this operation can be network expensive.\n\nDo you want to continue? diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml index 9532ab74d..fc6dfe138 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -105,7 +105,8 @@ + android:title="@string/settings_category_feed_title" + app:iconSpaceReserved="false"> + + + + + + + + + + + + diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml index b0d9070a3..35f3359da 100644 --- a/app/src/main/res/xml/video_audio_settings.xml +++ b/app/src/main/res/xml/video_audio_settings.xml @@ -84,7 +84,8 @@ + android:title="@string/settings_category_player_behavior_title" + app:iconSpaceReserved="false"> + + + lines="221,293"/> + lines="281,313"/>