mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-10-02 15:00:51 +02:00
Compare commits
225 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4e633504a8 | ||
![]() |
144a10f7a6 | ||
![]() |
72a2644f25 | ||
![]() |
ff8868f6a3 | ||
![]() |
8c6e37d1d1 | ||
![]() |
c90237c14c | ||
![]() |
3750561b4d | ||
![]() |
6b026557d4 | ||
![]() |
1ee137bbda | ||
![]() |
2c88e9d068 | ||
![]() |
4825a0a35f | ||
![]() |
122b0b0de4 | ||
![]() |
7dc85af5fb | ||
![]() |
c7daf32904 | ||
![]() |
4c8dca5300 | ||
![]() |
ef91214085 | ||
![]() |
dc09a4621b | ||
![]() |
2f99a217c3 | ||
![]() |
6992b2c308 | ||
![]() |
0d51eefbb9 | ||
![]() |
aa28a85747 | ||
![]() |
f18ee8e83d | ||
![]() |
fb58967766 | ||
![]() |
c3f1478fde | ||
![]() |
e5c00a7ef4 | ||
![]() |
769791af7a | ||
![]() |
e632fab4d0 | ||
![]() |
6cd25d7e55 | ||
![]() |
c9488eb042 | ||
![]() |
c8516a04dc | ||
![]() |
02d1b98b1c | ||
![]() |
d8236bbedd | ||
![]() |
1de21fb0c2 | ||
![]() |
13cac07b8d | ||
![]() |
bd9dcfb28a | ||
![]() |
d5199eac3e | ||
![]() |
7638d229c0 | ||
![]() |
a641c5bb58 | ||
![]() |
1e0c9f46ad | ||
![]() |
4eb02f584e | ||
![]() |
700c1b4b25 | ||
![]() |
4b4337e078 | ||
![]() |
38ce800685 | ||
![]() |
2310e8c1d6 | ||
![]() |
1b2b3a4f88 | ||
![]() |
d11129a76b | ||
![]() |
02789122a0 | ||
![]() |
676bc02d52 | ||
![]() |
8b807b0706 | ||
![]() |
72dfe974ab | ||
![]() |
316db0e4c6 | ||
![]() |
010c607e40 | ||
![]() |
3e099fb2a3 | ||
![]() |
9c9730b152 | ||
![]() |
9e44053e22 | ||
![]() |
dee32c3dc5 | ||
![]() |
344fbff59a | ||
![]() |
e39a816bdc | ||
![]() |
605b8fac5e | ||
![]() |
dfba10f8ae | ||
![]() |
48a1ab64b0 | ||
![]() |
dd2cde3c1a | ||
![]() |
1b9c2b37c5 | ||
![]() |
eae1f8b597 | ||
![]() |
18ce86c2ed | ||
![]() |
d5f25e05d9 | ||
![]() |
53303ac5d3 | ||
![]() |
90cc8e2144 | ||
![]() |
adf9badbf6 | ||
![]() |
c35fe4f3f1 | ||
![]() |
63291f8101 | ||
![]() |
62efb588ef | ||
![]() |
203ca9afc6 | ||
![]() |
a23f941ac8 | ||
![]() |
b0a10f0542 | ||
![]() |
478ad42977 | ||
![]() |
0764983ac6 | ||
![]() |
2b2f1ee8f5 | ||
![]() |
28f167fd99 | ||
![]() |
272be36dd9 | ||
![]() |
f933db8117 | ||
![]() |
cddb9bccb9 | ||
![]() |
b5ad24eb47 | ||
![]() |
ad8f791f71 | ||
![]() |
2e862b4ccc | ||
![]() |
ecac897e7b | ||
![]() |
702adb53a7 | ||
![]() |
4ea962f523 | ||
![]() |
acaf92d671 | ||
![]() |
c673cb6157 | ||
![]() |
c0f7b123a3 | ||
![]() |
e9e2afa61a | ||
![]() |
403154b2e1 | ||
![]() |
e5fd24b0d1 | ||
![]() |
8dc34274a1 | ||
![]() |
467bd21de2 | ||
![]() |
5c9705d94e | ||
![]() |
85fb5827aa | ||
![]() |
0bcc9bd3ba | ||
![]() |
25e120bec1 | ||
![]() |
7067deb328 | ||
![]() |
f6efd302dc | ||
![]() |
61972141ae | ||
![]() |
af936bc646 | ||
![]() |
d66f933c69 | ||
![]() |
cf81c37683 | ||
![]() |
d2306b0fd7 | ||
![]() |
94dfabf3dc | ||
![]() |
5522dc10b8 | ||
![]() |
0ae04b8ead | ||
![]() |
44cad27d0a | ||
![]() |
5d59025b3c | ||
![]() |
768bb0bbcd | ||
![]() |
ac071b383f | ||
![]() |
e0b1a6b88b | ||
![]() |
ed86b1c572 | ||
![]() |
b6c2bade73 | ||
![]() |
b6b19b474e | ||
![]() |
231b7492fb | ||
![]() |
b4950fcb2e | ||
![]() |
b79ea7b51b | ||
![]() |
28c72e7f63 | ||
![]() |
5fcc3b4dab | ||
![]() |
51837ce36f | ||
![]() |
ddaafb68c8 | ||
![]() |
a744775fe7 | ||
![]() |
50b85a7734 | ||
![]() |
aab09c0c65 | ||
![]() |
3ded6feddb | ||
![]() |
c8802fe5d0 | ||
![]() |
411b3129f9 | ||
![]() |
a55acd38df | ||
![]() |
e7773d8807 | ||
![]() |
7edef8d5a2 | ||
![]() |
03d2ca9f9f | ||
![]() |
2271ea4281 | ||
![]() |
afc8db8f81 | ||
![]() |
4af49ee5a6 | ||
![]() |
d7b29aae5c | ||
![]() |
9f7a8407ca | ||
![]() |
7eb13a9b93 | ||
![]() |
7c9896beaf | ||
![]() |
54d3bff26d | ||
![]() |
a2050a5211 | ||
![]() |
048743c062 | ||
![]() |
e9bd2934c3 | ||
![]() |
50634eb2b3 | ||
![]() |
08489b81fb | ||
![]() |
a2ff770afc | ||
![]() |
e0ba9b3902 | ||
![]() |
f11b5ae7a1 | ||
![]() |
7baeb6eca7 | ||
![]() |
658d988254 | ||
![]() |
9d7e9289bb | ||
![]() |
4e8519a1b9 | ||
![]() |
12aac09c7b | ||
![]() |
d7d87691cb | ||
![]() |
731640997e | ||
![]() |
64d7432852 | ||
![]() |
e6fffc0d5b | ||
![]() |
1c9f68bcae | ||
![]() |
4fde62ff89 | ||
![]() |
4c5fc7fa7c | ||
![]() |
b633108a4c | ||
![]() |
ceb55d0ede | ||
![]() |
87c958b2e7 | ||
![]() |
d844e0aba6 | ||
![]() |
3d42da5ff5 | ||
![]() |
1b869199f4 | ||
![]() |
f3cd2f6c9d | ||
![]() |
2e3e7f9bf2 | ||
![]() |
92327dd9e3 | ||
![]() |
d40b432f46 | ||
![]() |
5b3137093f | ||
![]() |
4fc9f2e5fd | ||
![]() |
ce592f4baf | ||
![]() |
2b3edcf2d1 | ||
![]() |
f165f97bd9 | ||
![]() |
4ec572372e | ||
![]() |
a953aab9b4 | ||
![]() |
672eb34049 | ||
![]() |
a0b042091b | ||
![]() |
b753705a84 | ||
![]() |
f48ff610a3 | ||
![]() |
93aed9f34c | ||
![]() |
3cf94382e6 | ||
![]() |
f52cb3bbe0 | ||
![]() |
d45182cb5c | ||
![]() |
22847c6c92 | ||
![]() |
a70c51b71c | ||
![]() |
02d417476e | ||
![]() |
bc3139e5f9 | ||
![]() |
c1f7b2653c | ||
![]() |
72dbb9441e | ||
![]() |
bbc13756f3 | ||
![]() |
ba0876b43b | ||
![]() |
c0d41661e8 | ||
![]() |
b2e2551e33 | ||
![]() |
ac371e6fb4 | ||
![]() |
108af48b76 | ||
![]() |
a225ac5deb | ||
![]() |
920695f90a | ||
![]() |
49fc57eee9 | ||
![]() |
b61d44aaa6 | ||
![]() |
f36fd2f7b2 | ||
![]() |
7e26748dc4 | ||
![]() |
dd7f914b8d | ||
![]() |
8272b2508b | ||
![]() |
70354eb73e | ||
![]() |
63083ac0c3 | ||
![]() |
672fcb9ce3 | ||
![]() |
870d50ebcd | ||
![]() |
b62b3e91a0 | ||
![]() |
b022d90303 | ||
![]() |
02af529551 | ||
![]() |
dd9cc619ed | ||
![]() |
75c9e959de | ||
![]() |
fb8afec1bf | ||
![]() |
fc52a6e871 | ||
![]() |
669a35bc78 | ||
![]() |
36e72d5a41 | ||
![]() |
f8297a8a9b | ||
![]() |
4d50a66e40 | ||
![]() |
e6c56cacc6 | ||
![]() |
79102a20d2 |
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -5,10 +5,9 @@ labels: enhancement
|
|||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
<!-- IF YOU DON'T FILL IN THE TEMPLATE PROPERLY, YOUR ISSUE IS LIABLE TO BE CLOSED. If you feel tired/lazy right now, open your issue some other time. We'll wait. -->
|
<!-- IF YOU DON'T FILL IN THE TEMPLATE PROPERLY, YOUR ISSUE IS LIABLE TO BE CLOSED. If you are currently unable to do so for any reason, open your issue some other time. We'll wait. -->
|
||||||
|
|
||||||
|
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the Preview tab). -->
|
||||||
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the Preview). -->
|
|
||||||
|
|
||||||
### Checklist
|
### Checklist
|
||||||
<!-- This checklist is COMPULSORY. The first box has been checked for you to show you how it is done. -->
|
<!-- This checklist is COMPULSORY. The first box has been checked for you to show you how it is done. -->
|
||||||
@@ -17,30 +16,9 @@ assignees: ''
|
|||||||
- [ ] I have read the contribution guidelines given at https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md.
|
- [ ] I have read the contribution guidelines given at https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md.
|
||||||
- [ ] This issue contains only one feature request. I will open one issue for every feature I want to request.
|
- [ ] This issue contains only one feature request. I will open one issue for every feature I want to request.
|
||||||
|
|
||||||
|
#### What feature do you want?
|
||||||
#### Describe the feature you want
|
<!-- Explain how you want the app's look or behavior to change to suit your needs. -->
|
||||||
<!-- A clear and concise description of what you wish should happen.
|
|
||||||
Example: *I think it would be nice if you add feature Y which makes X possible.*
|
|
||||||
|
|
||||||
Optionally, also describe alternatives you've considered.
|
|
||||||
Example: *Z is also a good alternative. Not as good as Y, but at least...* or *I considered Z, but that didn't turn out to be a good idea because...* -->
|
|
||||||
|
|
||||||
|
|
||||||
|
#### Why do you want this feature?
|
||||||
#### Is your feature request related to a problem? Please describe it
|
<!-- Describe any problem or limitation you come across while using the app which would be solved by this feature. -->
|
||||||
<!-- A clear and concise description of what the problem is. Maybe the developers and the community could brainstorm and come up with a better solution to your problem. If they exist, link to related Issues and/or PRs for developers to keep track easier.
|
|
||||||
Example: *I want to do X, but there is no way to do it.* -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Additional context
|
|
||||||
<!-- Add any other context, like screenshots, about the feature request here.
|
|
||||||
Example: *Here's a photo of my cat!* -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### How will you/everyone benefit from this feature?
|
|
||||||
<!-- Convince us! How does it change your NewPipe experience and/or your life?
|
|
||||||
The better this paragraph is, the more likely a developer will think about working on it.
|
|
||||||
Example: *This feature will help us colonize the galaxy! -->
|
|
||||||
|
|
||||||
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -33,10 +33,10 @@ jobs:
|
|||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
run: git checkout -B ${{ github.head_ref }}
|
run: git checkout -B ${{ github.head_ref }}
|
||||||
|
|
||||||
- name: set up JDK 8
|
- name: set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: 8
|
java-version: 11
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
cache: 'gradle'
|
cache: 'gradle'
|
||||||
|
|
||||||
@@ -59,10 +59,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: set up JDK 8
|
- name: set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: 8
|
java-version: 11
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
cache: 'gradle'
|
cache: 'gradle'
|
||||||
|
|
||||||
|
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,15 +1,15 @@
|
|||||||
.gitignore
|
.gradle/
|
||||||
.gradle
|
local.properties
|
||||||
/local.properties
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
build/
|
||||||
/captures
|
captures/
|
||||||
/app/app.iml
|
.idea/
|
||||||
/.idea
|
*.iml
|
||||||
/*.iml
|
|
||||||
*~
|
*~
|
||||||
.weblate
|
.weblate
|
||||||
*.class
|
*.class
|
||||||
|
**/debug/
|
||||||
|
**/release/
|
||||||
|
|
||||||
# vscode / eclipse files
|
# vscode / eclipse files
|
||||||
*.classpath
|
*.classpath
|
||||||
|
125
README.es.md
125
README.es.md
@@ -1,30 +1,30 @@
|
|||||||
<p align="center"><a href="https://newpipe.net"><img src="assets/new_pipe_icon_5.png" width="150"></a></p>
|
<p align="center"><a href="https://newpipe.net"><img src="assets/new_pipe_icon_5.png" width="150"></a></p>
|
||||||
<h2 align="center"><b>NewPipe</b></h2>
|
<h2 align="center"><b>NewPipe</b></h2>
|
||||||
<h4 align="center">Una interfaz de streaming lijera y libre para Android.</h4>
|
<h4 align="center">Una interfaz de streaming ligera y libre para Android.</h4>
|
||||||
|
|
||||||
<p align="center"><a href="https://f-droid.org/packages/org.schabi.newpipe/"><img src="https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png"></a></p>
|
<p align="center"><a href="https://f-droid.org/packages/org.schabi.newpipe/"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on-es.svg"></a></p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/TeamNewPipe/NewPipe/releases" alt="GitHub release"><img src="https://img.shields.io/badge/Lanzamiento-v0.20.11-blue.svg" ></a>
|
<a href="https://github.com/TeamNewPipe/NewPipe/releases" alt="Lanzamientos GitHub"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe.svg" ></a>
|
||||||
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/Licencia-GPL%20v3-blue.svg"></a>
|
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="Licencia: GPLv3"><img src="https://img.shields.io/badge/Licencia-GPL%20v3-blue.svg"></a>
|
||||||
<a href="https://github.com/TeamNewPipe/NewPipe/actions" alt="Build Status"><img src="https://github.com/TeamNewPipe/NewPipe/workflows/CI/badge.svg?branch=dev&event=push"></a>
|
<a href="https://github.com/TeamNewPipe/NewPipe/actions" alt="Estado del Build"><img src="https://github.com/TeamNewPipe/NewPipe/workflows/CI/badge.svg?branch=dev&event=push"></a>
|
||||||
<a href="https://hosted.weblate.org/engage/newpipe/es/" alt="Estado de la traducción"><img src="https://hosted.weblate.org/widgets/newpipe/es/svg-badge.svg"></a>
|
<a href="https://hosted.weblate.org/engage/newpipe/es/" alt="Estado de la Traducción"><img src="https://hosted.weblate.org/widgets/newpipe/es/svg-badge.svg"></a>
|
||||||
<a href="https://web.libera.chat/#newpipe" alt="IRC channel: #newpipe"><img src="https://img.shields.io/badge/Canal%20de%20IRC%20-%23newpipe-brightgreen.svg"></a>
|
<a href="https://web.libera.chat/#newpipe" alt="Canal de IRC: #newpipe"><img src="https://img.shields.io/badge/Canal%20de%20IRC%20-%23newpipe-brightgreen.svg"></a>
|
||||||
<a href="https://www.bountysource.com/teams/newpipe" alt="Bountysource bounties"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f"></a>
|
<a href="https://www.bountysource.com/teams/newpipe" alt="Recompensas en Bountysource"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f"></a>
|
||||||
</p>
|
</p>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<p align="center"><a href="#capturas-de-pantalla">Capturas de pantalla</a> • <a href="#descripción">Descripción</a> • <a href="#características">Características</a> • <a href="#installación-y-actualizaciones">Installación y actualizaciones</a> • <a href="#contribución">Contribución</a> • <a href="#donar">Donar</a> • <a href="#licencias">Licencias</a></p>
|
<p align="center"><a href="#capturas-de-pantalla">Capturas de Pantalla</a> • <a href="#descripción">Descripción</a> • <a href="#características">Características</a> • <a href="#instalación-y-actualizaciones">Instalación y Actualizaciones</a> • <a href="#contribución">Contribución</a> • <a href="#donar">Donar</a> • <a href="#licencia">Licencia</a></p>
|
||||||
<p align="center"><a href="https://newpipe.net">Sitio web</a> • <a href="https://newpipe.net/blog/">Blog</a> • <a href="https://newpipe.net/FAQ/">Preguntas Frecuentes</a> • <a href="https://newpipe.net/press/">Prensa</a></p>
|
<p align="center"><a href="https://newpipe.net">Sitio Web</a> • <a href="https://newpipe.net/blog/">Blog</a> • <a href="https://newpipe.net/FAQ/">Preguntas Frecuentes</a> • <a href="https://newpipe.net/press/">Prensa</a></p>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
*Lea esto en otros idiomas: [English](README.md), [한국어](README.ko.md), [Soomaali](README.so.md), [Português Brasil](README.pt_BR.md), [日本語](README.ja.md), [Română](README.ro.md), [Türkçe](README.tr.md).*
|
*Lea esto en otros idiomas: [English](README.md), [한국어](README.ko.md), [Soomaali](README.so.md), [Português Brasil](README.pt_BR.md), [日本語](README.ja.md), [Română](README.ro.md), [Türkçe](README.tr.md).*
|
||||||
|
|
||||||
<b>AVISO: ESTA ES UNA VERSIÓN BETA, POR LO TANTO, PUEDE ENCONTRAR BUGS (ERRORES). SI ENCUENTRA UNO, ABRA UN ISSUE A TRAVÉS DE NUESTRO REPOSITORIO GITHUB.</b>
|
<b>AVISO: ESTA ES UNA VERSIÓN BETA, POR LO TANTO, PUEDE ENCONTRAR BUGS. SI ENCUENTRA UNO ABRA UN ISSUE A TRAVÉS DE NUESTRO REPOSITORIO DE GITHUB.</b>
|
||||||
|
|
||||||
<b>COLOCAR NEWPIPE O CUALQUIER FORK (BIFURCACIÓN) REALIZADO DE ELLO EN GOOGLE PLAY STORE VIOLA SUS TÉRMINOS Y CONDICIONES.</b>
|
<b>COLOCAR NEWPIPE O CUALQUIER FORK DE NEWPIPE EN LA GOOGLE PLAY STORE VIOLARÁ SUS TÉRMINOS Y CONDICIONES.</b>
|
||||||
|
|
||||||
## Capturas de pantalla
|
## Capturas de Pantalla
|
||||||
|
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png)
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png)
|
||||||
@@ -40,39 +40,43 @@
|
|||||||
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png)
|
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png)
|
||||||
|
|
||||||
## Descripción
|
## Descripción
|
||||||
NewPipe no usa ninguna librería de framework de Google, ni la API de YouTube. Los sitios web solamente se analizan para extraer la información requerida, asi que esta app se puede usar sin los servicios de Google instalados. Además, no se necesita una cuenta de YouTube para usar NewPipe, lo cual es un software libre de copyleft.
|
|
||||||
|
NewPipe no usa ninguna librería del framework de Google, ni la API de YouTube. Los sitios web solamente se analizan para extraer la información requerida, por lo que esta app se puede usar sin los servicios de Google instalados. Además, no se necesita una cuenta de YouTube para usar NewPipe, lo cual es un software libre de copyleft.
|
||||||
|
|
||||||
### Características
|
### Características
|
||||||
|
|
||||||
* Buscar videos
|
* Buscar videos
|
||||||
|
* No requiere inicio de sesión
|
||||||
* Mostrar información general sobre videos
|
* Mostrar información general sobre videos
|
||||||
* Mirar videos de YouTube
|
* Mirar videos de YouTube
|
||||||
* Escuchar audio de YouTube
|
* Modo de solo audio en videos de YouTube
|
||||||
* Modo popup (reproductor flotante)
|
* Modo pop-up (reproductor flotante)
|
||||||
* Elegir reproductor para mirar el video
|
* Elegir un reproductor de video externo para mirar videos
|
||||||
* Descargar videos
|
* Descargar videos
|
||||||
* Descargar solamente audio
|
* Descargar solo audio
|
||||||
* Abrir video en Kodi
|
* Abrir videos en Kodi
|
||||||
* Mostrar videos próximos/relacionados
|
* Mostrar videos próximos/relacionados
|
||||||
* Buscar a través de YouTube en un idioma específico
|
* Buscar a través de YouTube en un idioma específico
|
||||||
* Mirar/Bloquear materiales restringidas por edad.
|
* Mirar/Bloquear videos restringidos por edad
|
||||||
* Mostrar información general sobre canales
|
* Mostrar información general sobre canales
|
||||||
* Buscar canales
|
* Buscar de canales
|
||||||
* Mirar videos de un canal
|
* Mirar videos de un canal
|
||||||
* Apoyo Orbot/Tor (todavía no directamente)
|
* Soporte Orbot/Tor (todavía no directamente)
|
||||||
* Apoyo 1080p/2K/4K
|
* Soporte para videos en 1080p/2K/4K
|
||||||
* Ver historias
|
* Historial de videos vistos
|
||||||
* Subscribirse a canales
|
* Suscripción a canales
|
||||||
* Buscar historias
|
* Historial de búsquedas
|
||||||
* Buscar/mirar listas de reproducción
|
* Buscar/Mirar listas de reproducción
|
||||||
* Mirar listas de reproducción en fila
|
* Mirar listas de reproducción en cola
|
||||||
* Poner videos en fila
|
* Poner videos en cola
|
||||||
* Listas locales de reproducción
|
* Listas de reproducción locales
|
||||||
* Subtítulos
|
* Subtítulos
|
||||||
* Apoyo de medios en directo
|
* Soporte para transmisiones en vivo
|
||||||
* Mostrar comentarios
|
* Mostrar comentarios
|
||||||
|
|
||||||
### Servicios apoyados
|
### Servicios Soportados
|
||||||
NewPipe apoya varios servicios. Nuestras [documentaciones](https://teamnewpipe.github.io/documentation/) proveen más información en como se puede agregar un servicio nuevo a la app y el extractor. Por favor contáctenos si pretende agregar uno nuevo. Actualmente los servicios apoyados son:
|
|
||||||
|
NewPipe soporta varios servicios. Nuestras [documentaciones](https://teamnewpipe.github.io/documentation/) ofrecen más información sobre cómo se puede agregar un servicio nuevo a la app y al extractor. Por favor ponte en contacto con nosotros si tienes pensado agregar uno nuevo. Actualmente los servicios soportados son:
|
||||||
|
|
||||||
* YouTube
|
* YouTube
|
||||||
* SoundCloud \[beta\]
|
* SoundCloud \[beta\]
|
||||||
@@ -80,61 +84,60 @@ NewPipe apoya varios servicios. Nuestras [documentaciones](https://teamnewpipe.g
|
|||||||
* PeerTube instances \[beta\]
|
* PeerTube instances \[beta\]
|
||||||
* Bandcamp \[beta\]
|
* Bandcamp \[beta\]
|
||||||
|
|
||||||
<!-- Brecha escondida para mantener compatibles los enlaces viejos. -->
|
## Instalación y Actualizaciones
|
||||||
<span id="actualizaciones"></span>
|
|
||||||
|
|
||||||
## Installación y actualizaciones
|
|
||||||
Se puede instalar NewPipe usando uno de los métodos siguientes:
|
Se puede instalar NewPipe usando uno de los métodos siguientes:
|
||||||
1. Agregar nuestro repositorio personalizado a F-Droid e instalarlo desde allí. Las instrucciones están aquí: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
|
||||||
2. Descargar el archivo APK del enlace [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases) e instalarlo.
|
|
||||||
3. Actualizar a través de F-Droid. Este es el método más lento para obtener la actualización, como F-Droid debe reconocer cambios, construir el APK aparte, firmarlo con una clave, y finalmente empujar la actualización a los usuarios.
|
|
||||||
4. Construir un APK de depuración por si mismo. Este es el modo más rápido para realizar nuevas características en su dispositivo, pero es mucho más complicado, asi que recomendamos uno de los otros métodos.
|
|
||||||
|
|
||||||
Recomendamos el método 1 para la mayoría de usuarios. Los APKs instalados usando método 1 o 2 son compatibles el uno con el otro, pero no con las instalaciones usando método 3. Esta es debida a la misma clave digital (la nuestra), siendo utilizado en los métodos 1 y 2, pero una clave digital diferente (la de F-Droid) siendo utilizado en el método 3. Construir un APK de depuración usando método 4 excluye una clave enteramente. Firmando con claves digitales ayuda a asegurar de que un
|
1. Agregando nuestro repositorio personalizado a F-Droid e instalarlo desde allí. Las instrucciones están [aquí](https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/).
|
||||||
usuario no esté engañado para instalar una actualización maliciosa a una app.
|
2. Descargando el archivo APK de [aquí](https://github.com/TeamNewPipe/NewPipe/releases) y posteriormente instalarlo.
|
||||||
|
3. Usando el repositorio oficial de F-Droid. Este es el método más lento para obtener actualizaciones, ya que F-Droid debe reconocer los cambios, construir el APK aparte, firmarlo con una clave, y finalmente publicar la actualización.
|
||||||
|
4. Construyendo la app usted mismo. Este es el modo más rápido para obtener nuevas características en su dispositivo, pero es mucho más complicado, así que recomendamos uno de los otros métodos.
|
||||||
|
|
||||||
Mientras tanto, si quiere cambiar los fuentes por alguna razón (por ejemplo, la funcionalidad del nucleo de NewPipe se rompe y F-Droid aun no tiene la actualización), recomendamos el siguiente procedimiento:
|
Recomendamos el método 1 para la mayoría de usuarios. Los APKs instalados usando método 1 y 2 son compatibles entre sí, pero no lo son con los instalados usando el método 3. Esto es debido a la clave de firmado, ya que los métodos 1 y 2 usan la misma clave (la nuestra), pero el método 3 usa una clave diferente (la de F-Droid). El método 4 excluye totalmente una clave de firmado. Las claves de firmado aseguran que el usuario no esté siendo engañado para instalar/actualizar una APK maliciosa.
|
||||||
1. Repaldear sus datos a través de Ajustes > Contenido > Exporta base de datos para guardar su historia, subscripciones, y listas de reproducción
|
|
||||||
2. Desinstalar NewPipe
|
Además, si quiere cambiar el método de instalación por alguna razón (por ejemplo: la funcionalidad del núcleo de NewPipe se rompe o F-Droid aún no publica la actualización), recomendamos el siguiente procedimiento:
|
||||||
3. Descargar el APK del nuevo fuente e instalarlo.
|
1. Respalde su información a través de Ajustes > Contenido > Exportar base de datos; esto guardará su historial (videos vistos y búsquedas), suscripciones, listas de reproducción y ajustes.
|
||||||
4. Importar los datos del paso 1 a través de Ajustes > Contenido > Importa base de datos.
|
2. Desinstale NewPipe.
|
||||||
|
3. Descargue el APK con un método distinto e instálelo.
|
||||||
|
4. Importe la información (la base de datos extraída del paso 1) a través de Ajustes > Contenido > Importar base de datos. Tenga en cuenta que esta opción superpondrá su historial actual (tanto de vídeos como de búsquedas), sus listas de reproducción y (opcionalmente) sus configuraciones.
|
||||||
|
|
||||||
## Contribución
|
## Contribución
|
||||||
Si tiene ideas, traducciónes, cambios de diseño, limpieza de código, o cambios grandes de código, su ayuda es siempre bienvenida.
|
|
||||||
Cuanto más realizamos, mejor se pone la aplicación!
|
Si tiene ideas, traducciones, cambios de diseño, limpieza de código o cambios grandes de código, su ayuda es siempre bienvenida. ¡Cuanto más hagamos, NewPipe será mucho mejor!
|
||||||
|
|
||||||
Si quiere involucrarse, fíjese en nuestras [notas de contribución](.github/CONTRIBUTING.md).
|
Si quiere involucrarse, fíjese en nuestras [notas de contribución](.github/CONTRIBUTING.md).
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/newpipe/es/">
|
<a href="https://hosted.weblate.org/engage/newpipe/es/">
|
||||||
<img src="https://hosted.weblate.org/widgets/newpipe/es/287x66-grey.png" alt="Estado de la traducción" />
|
<img src="https://hosted.weblate.org/widgets/newpipe/es/287x66-grey.png" alt="Estado de la Traducción" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## Donar
|
## Donar
|
||||||
Si le gusta el NewPipe estaremos felices con una donación. O puede enviar bitcoin o donar a través de Bountysource o Liberapay. Para obtener más información sobre como donar a NewPipe, por favor visita nuestro [sitio web](https://newpipe.net/donate).
|
Si te gusta NewPipe, estaremos felices con una donación. Puede enviar bitcoin o donar a través de Bountysource o Liberapay. Visita nuestro [sitio web](https://newpipe.net/donate) para más información.
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><img src="https://bitcoin.org/img/icons/logotop.svg" alt="Bitcoin"></td>
|
<td><img src="https://bitcoin.org/img/icons/logotop.svg" alt="Bitcoin"></td>
|
||||||
<td><img src="assets/bitcoin_qr_code.png" alt="Bitcoin QR code" width="100px"></td>
|
<td><img src="assets/bitcoin_qr_code.png" alt="Código QR del Bitcoin" width="100px"></td>
|
||||||
<td><samp>16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh</samp></td>
|
<td><samp>16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh</samp></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="https://upload.wikimedia.org/wikipedia/commons/2/27/Liberapay_logo_v2_white-on-yellow.svg" alt="Liberapay" width="80px" ></a></td>
|
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="https://upload.wikimedia.org/wikipedia/commons/2/27/Liberapay_logo_v2_white-on-yellow.svg" alt="Liberapay" width="80px" ></a></td>
|
||||||
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="assets/liberapay_qr_code.png" alt="Visit NewPipe at liberapay.com" width="100px"></a></td>
|
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="assets/liberapay_qr_code.png" alt="Visita NewPipe en liberapay.com" width="100px"></a></td>
|
||||||
<td><a href="https://liberapay.com/TeamNewPipe/donate"><img src="assets/liberapay_donate_button.svg" alt="Donate via Liberapay" height="35px"></a></td>
|
<td><a href="https://liberapay.com/TeamNewPipe/donate"><img src="assets/liberapay_donate_button.svg" alt="Dona vía Liberapay" height="35px"></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Bountysource.png/320px-Bountysource.png" alt="Bountysource" width="190px"></a></td>
|
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Bountysource.png/320px-Bountysource.png" alt="Bountysource" width="190px"></a></td>
|
||||||
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="assets/bountysource_qr_code.png" alt="Visit NewPipe at bountysource.com" width="100px"></a></td>
|
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="assets/bountysource_qr_code.png" alt="Visita NewPipe en bountysource.com" width="100px"></a></td>
|
||||||
<td><a href="https://www.bountysource.com/teams/newpipe/issues"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f" height="30px" alt="Check out how many bounties you can earn."></a></td>
|
<td><a href="https://www.bountysource.com/teams/newpipe/issues"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f" height="30px" alt="Revisa cuántas recompensas puedes obtener."></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
## Política de privacidad
|
## Política de Privacidad
|
||||||
El proyecto NewPipe tiene como objetivo proveer una experience privada y anónima para usar servicios de medios web.
|
|
||||||
Por lo tanto, la app no colecciona ningunos datos sin su consentimiento. La politica de privacidad de NewPipe explica en detalle los datos enviados y almacenados cuando envia un informe de error, o comentario en nuestro blog. Puede encontrar el documento [aqui](https://newpipe.net/legal/privacy/).
|
El proyecto NewPipe tiene como objetivo ofrecer una experience privada y anónima al usar servicios web multimedia. Por lo tanto, la app no recoleta ningún tipo de información sin su consentimiento. La politica de privacidad de NewPipe explica en detalle qué información es enviada y almacenada cuando envía un informe de error o comenta en [nuestro blog](https://newpipe.net/blog/). Puede encontrar el documento [aquí](https://newpipe.net/legal/privacy/).
|
||||||
|
|
||||||
## Licencia
|
## Licencia
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
|
||||||
|
|
||||||
NewPipe es Software Libre: Puede usar, estudiar, compartir, y mejorarlo a su voluntad. Especificamente puede redistribuir y/o modificarlo bajo los términos de la [GNU General Public License](https://www.gnu.org/licenses/gpl.html) como publicado por la Free Software Foundation, o versión 3 de la licencia, o (en su opción) cualquier versión posterior.
|
[](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||||
|
|
||||||
|
NewPipe es Software Libre: Puede usarlo, estudiarlo, compartirlo y mejorarlo a su voluntad. Más específicamente, puede redistribuirlo y/o modificarlo bajo los términos de la [GNU General Public License](https://www.gnu.org/licenses/gpl.html) publicada por la Free Software Foundation tanto si usa la versión 3 o posterior de la licencia.
|
||||||
|
@@ -89,7 +89,7 @@ NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로
|
|||||||
따라서 우리는 다른 방법들 중 하나를 사용하는 것을 추천합니다.
|
따라서 우리는 다른 방법들 중 하나를 사용하는 것을 추천합니다.
|
||||||
2. 우리의 커스텀 저장소를 F-Droid에 추가하고 우리가 릴리즈를 게시하는 대로 저곳에서 릴리즈를 설치할 수 있습니다.
|
2. 우리의 커스텀 저장소를 F-Droid에 추가하고 우리가 릴리즈를 게시하는 대로 저곳에서 릴리즈를 설치할 수 있습니다.
|
||||||
이에 대한 설명서는 이곳에서 확인할 수 있습니다: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
이에 대한 설명서는 이곳에서 확인할 수 있습니다: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
||||||
3. 우리가 릴리즈를 게시하는 대로 [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases)에서 APK를 다운받고 이것을 설치할 수 있습니다.
|
3. 우리가 릴리즈를 게시하는 대로 [GitHub Releases](https://github.com/TeamNewPipe/NewPipe/releases)에서 APK를 다운받고 이것을 설치할 수 있습니다.
|
||||||
4. F-Droid를 통해 업데이트 할 수 있습니다. F-Droid는 변화를 인식하고, 스스로 APK를 생성하고, 이것에 서명하고, 사용자들에서 업데이트를 전달해야만 하기 때문에,
|
4. F-Droid를 통해 업데이트 할 수 있습니다. F-Droid는 변화를 인식하고, 스스로 APK를 생성하고, 이것에 서명하고, 사용자들에서 업데이트를 전달해야만 하기 때문에,
|
||||||
이것은 업데이트를 받는 가장 느린 방법입니다.
|
이것은 업데이트를 받는 가장 느린 방법입니다.
|
||||||
|
|
||||||
|
@@ -89,7 +89,7 @@ NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/doc
|
|||||||
## Installation and updates
|
## Installation and updates
|
||||||
You can install NewPipe using one of the following methods:
|
You can install NewPipe using one of the following methods:
|
||||||
1. Add our custom repo to F-Droid and install it from there. The instructions are here: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
1. Add our custom repo to F-Droid and install it from there. The instructions are here: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
||||||
2. Download the APK from [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it.
|
2. Download the APK from [GitHub Releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it.
|
||||||
3. 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.
|
3. 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.
|
||||||
4. 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.
|
4. 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.
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -89,7 +89,7 @@ NewPipe suportă servicii multiple. [Documentele](https://teamnewpipe.github.io/
|
|||||||
## Instalare şi actualizări
|
## Instalare şi actualizări
|
||||||
Puteţi instala NewPipe folosind una dintre următoarele metode:
|
Puteţi instala NewPipe folosind una dintre următoarele metode:
|
||||||
1. Adăugaţi depozitul nostru F-droid personalizat. Instrucţiunile sunt aici: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
1. Adăugaţi depozitul nostru F-droid personalizat. Instrucţiunile sunt aici: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
||||||
2. Descărcaţi APK-ul din [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases) şi instalaţi-l.
|
2. Descărcaţi APK-ul din [GitHub Releases](https://github.com/TeamNewPipe/NewPipe/releases) şi instalaţi-l.
|
||||||
3. Actualizaţi via F-Droid. Aceasta este cea mai lentă metodă de a obţine actualizări, deoarece F-Droid trebuie să recunoască schimbările, să constriască APK-ul, să îl semneze, iar apoi să îl trimită utilizatorilor.
|
3. Actualizaţi via F-Droid. Aceasta este cea mai lentă metodă de a obţine actualizări, deoarece F-Droid trebuie să recunoască schimbările, să constriască APK-ul, să îl semneze, iar apoi să îl trimită utilizatorilor.
|
||||||
4. Construiţi un APK de depanare. Aceasta este cea mai rapidă metodă de a primi funcţii noi, dar este mult mai complicată, aşa că vă recomandăm să folosiţi una dintre celelalte metode.
|
4. Construiţi un APK de depanare. Aceasta este cea mai rapidă metodă de a primi funcţii noi, dar este mult mai complicată, aşa că vă recomandăm să folosiţi una dintre celelalte metode.
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<b>GOOGLE PLAY STORE'A NEWPIPE VEYA BAŞKA BİR KOPYASINI KOYMAK, PLAY STORE ŞARTLARINI VE KOŞULLARINI İHLAL EDER.</b>
|
<b>GOOGLE PLAY STORE'A NEWPIPE VEYA BAŞKA BİR KOPYASINI KOYMAK, PLAY STORE ŞARTLARINI VE KOŞULLARINI İHLAL EDER.</b>
|
||||||
|
|
||||||
## Ekran fotoğrafları
|
## Ekran görüntüleri
|
||||||
|
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png)
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png)
|
||||||
@@ -88,7 +88,7 @@ NewPipe birden fazla hizmeti destekler. Uygulamaya ve ayıklayıcıya yeni bir h
|
|||||||
## Kurulum ve güncellemeler
|
## Kurulum ve güncellemeler
|
||||||
Aşağıdaki yöntemlerden birini kullanarak NewPipe'ı kurabilirsiniz:
|
Aşağıdaki yöntemlerden birini kullanarak NewPipe'ı kurabilirsiniz:
|
||||||
1. Özel depomuzu F-Droid'e ekleyin ve oradan yükleyin. Kılavuzu şurada bulabilirsiniz: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
1. Özel depomuzu F-Droid'e ekleyin ve oradan yükleyin. Kılavuzu şurada bulabilirsiniz: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
|
||||||
2. APK'yı [Github sürümlerinden](https://github.com/TeamNewPipe/NewPipe/releases) indirin ve kurun.
|
2. APK'yı [GitHub sürümlerinden](https://github.com/TeamNewPipe/NewPipe/releases) indirin ve kurun.
|
||||||
3. F-Droid ile güncelleyin. Bu, güncellemeleri almanın en yavaş yöntemidir, çünkü F-Droid değişiklikleri tanımalı, APK'yı kendisi oluşturmalı, imzalamalı ve ardından güncellemeyi kullanıcılara dağıtmalıdır.
|
3. F-Droid ile güncelleyin. Bu, güncellemeleri almanın en yavaş yöntemidir, çünkü F-Droid değişiklikleri tanımalı, APK'yı kendisi oluşturmalı, imzalamalı ve ardından güncellemeyi kullanıcılara dağıtmalıdır.
|
||||||
4. Kendiniz bir APK derleyin. Bu yöntem, cihazınızda yeni özellikler edinmenin en hızlı yoludur, ancak çok daha karmaşıktır, bu nedenle diğer yöntemlerden birini kullanmanızı öneririz.
|
4. Kendiniz bir APK derleyin. Bu yöntem, cihazınızda yeni özellikler edinmenin en hızlı yoludur, ancak çok daha karmaşıktır, bu nedenle diğer yöntemlerden birini kullanmanızı öneririz.
|
||||||
|
|
||||||
|
3
app/.gitignore
vendored
3
app/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
.gitignore
|
|
||||||
/build
|
|
||||||
*.iml
|
|
@@ -17,8 +17,8 @@ android {
|
|||||||
resValue "string", "app_name", "NewPipe"
|
resValue "string", "app_name", "NewPipe"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 976
|
versionCode 982
|
||||||
versionName "0.21.10"
|
versionName "0.21.16"
|
||||||
|
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
@@ -54,6 +54,11 @@ android {
|
|||||||
// debug build. This seems to be a Gradle bug, therefore
|
// debug build. This seems to be a Gradle bug, therefore
|
||||||
// TODO: update Gradle version
|
// TODO: update Gradle version
|
||||||
release {
|
release {
|
||||||
|
if (System.properties.containsKey('packageSuffix')) {
|
||||||
|
applicationIdSuffix System.getProperty('packageSuffix')
|
||||||
|
resValue "string", "app_name", "NewPipe " + System.getProperty('packageSuffix')
|
||||||
|
archivesBaseName = 'NewPipe_' + System.getProperty('packageSuffix')
|
||||||
|
}
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources false // disabled to fix F-Droid's reproducible build
|
shrinkResources false // disabled to fix F-Droid's reproducible build
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
@@ -100,9 +105,9 @@ ext {
|
|||||||
androidxRoomVersion = '2.3.0'
|
androidxRoomVersion = '2.3.0'
|
||||||
|
|
||||||
icepickVersion = '3.2.0'
|
icepickVersion = '3.2.0'
|
||||||
exoPlayerVersion = '2.12.3'
|
exoPlayerVersion = '2.14.2'
|
||||||
googleAutoServiceVersion = '1.0'
|
googleAutoServiceVersion = '1.0'
|
||||||
groupieVersion = '2.8.1'
|
groupieVersion = '2.10.0'
|
||||||
markwonVersion = '4.6.2'
|
markwonVersion = '4.6.2'
|
||||||
|
|
||||||
leakCanaryVersion = '2.5'
|
leakCanaryVersion = '2.5'
|
||||||
@@ -184,7 +189,7 @@ dependencies {
|
|||||||
// name and the commit hash with the commit hash of the (pushed) commit you want to test
|
// name and the commit hash with the commit hash of the (pushed) commit you want to test
|
||||||
// This works thanks to JitPack: https://jitpack.io/
|
// This works thanks to JitPack: https://jitpack.io/
|
||||||
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
|
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
|
||||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.21.10'
|
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.21.13'
|
||||||
|
|
||||||
/** Checkstyle **/
|
/** Checkstyle **/
|
||||||
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||||
@@ -203,14 +208,17 @@ dependencies {
|
|||||||
implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}"
|
implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}"
|
implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}"
|
||||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
||||||
implementation 'androidx.media:media:1.3.1'
|
implementation 'androidx.media:media:1.4.3'
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
implementation 'androidx.preference:preference:1.1.1'
|
implementation 'androidx.preference:preference:1.1.1'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
implementation "androidx.room:room-runtime:${androidxRoomVersion}"
|
implementation "androidx.room:room-runtime:${androidxRoomVersion}"
|
||||||
implementation "androidx.room:room-rxjava3:${androidxRoomVersion}"
|
implementation "androidx.room:room-rxjava3:${androidxRoomVersion}"
|
||||||
kapt "androidx.room:room-compiler:${androidxRoomVersion}"
|
kapt "androidx.room:room-compiler:${androidxRoomVersion}"
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
|
// Newer version specified to prevent accessibility regressions with RecyclerView, see:
|
||||||
|
// https://developer.android.com/jetpack/androidx/releases/viewpager2#1.1.0-alpha01
|
||||||
|
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||||
implementation 'androidx.webkit:webkit:1.4.0'
|
implementation 'androidx.webkit:webkit:1.4.0'
|
||||||
implementation 'com.google.android.material:material:1.2.1'
|
implementation 'com.google.android.material:material:1.2.1'
|
||||||
|
|
||||||
|
@@ -43,10 +43,11 @@ class LocalPlaylistManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createPlaylist() {
|
fun createPlaylist() {
|
||||||
|
val NEWPIPE_URL = "https://newpipe.net/"
|
||||||
val stream = StreamEntity(
|
val stream = StreamEntity(
|
||||||
serviceId = 1, url = "https://newpipe.net/", title = "title",
|
serviceId = 1, url = NEWPIPE_URL, title = "title",
|
||||||
streamType = StreamType.VIDEO_STREAM, duration = 1, uploader = "uploader",
|
streamType = StreamType.VIDEO_STREAM, duration = 1, uploader = "uploader",
|
||||||
uploaderUrl = "https://newpipe.net/"
|
uploaderUrl = NEWPIPE_URL
|
||||||
)
|
)
|
||||||
|
|
||||||
val result = manager.createPlaylist("name", listOf(stream))
|
val result = manager.createPlaylist("name", listOf(stream))
|
||||||
|
@@ -146,6 +146,7 @@
|
|||||||
<data android:pathPrefix="/embed/" />
|
<data android:pathPrefix="/embed/" />
|
||||||
<data android:pathPrefix="/watch" />
|
<data android:pathPrefix="/watch" />
|
||||||
<data android:pathPrefix="/attribution_link" />
|
<data android:pathPrefix="/attribution_link" />
|
||||||
|
<data android:pathPrefix="/shorts/" />
|
||||||
<!-- channel prefix -->
|
<!-- channel prefix -->
|
||||||
<data android:pathPrefix="/channel/" />
|
<data android:pathPrefix="/channel/" />
|
||||||
<data android:pathPrefix="/user/" />
|
<data android:pathPrefix="/user/" />
|
||||||
@@ -224,6 +225,7 @@
|
|||||||
|
|
||||||
<data android:scheme="http" />
|
<data android:scheme="http" />
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
|
<data android:host="tubus.eduvid.org" />
|
||||||
<data android:host="invidio.us" />
|
<data android:host="invidio.us" />
|
||||||
<data android:host="dev.invidio.us" />
|
<data android:host="dev.invidio.us" />
|
||||||
<data android:host="www.invidio.us" />
|
<data android:host="www.invidio.us" />
|
||||||
@@ -254,6 +256,21 @@
|
|||||||
<data android:pathPrefix="/" />
|
<data android:pathPrefix="/" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- y2u.be filter -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
|
||||||
|
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="http" />
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:host="y2u.be" />
|
||||||
|
<data android:pathPrefix="/" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<!-- Soundcloud filter -->
|
<!-- Soundcloud filter -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -309,6 +326,7 @@
|
|||||||
<data android:scheme="http" />
|
<data android:scheme="http" />
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
|
|
||||||
|
<data android:host="eduvid.org" />
|
||||||
<data android:host="framatube.org" />
|
<data android:host="framatube.org" />
|
||||||
<data android:host="media.assassinate-you.net" />
|
<data android:host="media.assassinate-you.net" />
|
||||||
<data android:host="peertube.co.uk" />
|
<data android:host="peertube.co.uk" />
|
||||||
@@ -322,8 +340,12 @@
|
|||||||
<data android:host="skeptikon.fr" />
|
<data android:host="skeptikon.fr" />
|
||||||
|
|
||||||
<data android:pathPrefix="/videos/" /> <!-- it contains playlists -->
|
<data android:pathPrefix="/videos/" /> <!-- it contains playlists -->
|
||||||
|
<data android:pathPrefix="/w/" /> <!-- short video URLs -->
|
||||||
|
<data android:pathPrefix="/w/p/" /> <!-- short playlist URLs -->
|
||||||
<data android:pathPrefix="/accounts/" />
|
<data android:pathPrefix="/accounts/" />
|
||||||
|
<data android:pathPrefix="/a/" /> <!-- short account URLs -->
|
||||||
<data android:pathPrefix="/video-channels/" />
|
<data android:pathPrefix="/video-channels/" />
|
||||||
|
<data android:pathPrefix="/c/" /> <!-- short video-channels URLs -->
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<!-- Bandcamp filter for tracks, albums and playlists -->
|
<!-- Bandcamp filter for tracks, albums and playlists -->
|
||||||
@@ -358,6 +380,9 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".RouterActivity$FetcherService"
|
android:name=".RouterActivity$FetcherService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<service
|
||||||
|
android:name=".CheckForNewAppVersion"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- opting out of sending metrics to Google in Android System WebView -->
|
<!-- opting out of sending metrics to Google in Android System WebView -->
|
||||||
<meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" />
|
<meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" />
|
||||||
|
@@ -51,8 +51,12 @@ import java.util.ArrayList;
|
|||||||
* <li>{@link #saveState()}</li>
|
* <li>{@link #saveState()}</li>
|
||||||
* <li>{@link #restoreState(Parcelable, ClassLoader)}</li>
|
* <li>{@link #restoreState(Parcelable, ClassLoader)}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
*
|
||||||
|
* @deprecated Switch to {@link androidx.viewpager2.widget.ViewPager2} and use
|
||||||
|
* {@link androidx.viewpager2.adapter.FragmentStateAdapter} instead.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@Deprecated
|
||||||
public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapter {
|
public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapter {
|
||||||
private static final String TAG = "FragmentStatePagerAdapt";
|
private static final String TAG = "FragmentStatePagerAdapt";
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
@@ -86,9 +90,10 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
|
|||||||
private final int mBehavior;
|
private final int mBehavior;
|
||||||
private FragmentTransaction mCurTransaction = null;
|
private FragmentTransaction mCurTransaction = null;
|
||||||
|
|
||||||
private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
|
private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
|
||||||
private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
|
private final ArrayList<Fragment> mFragments = new ArrayList<>();
|
||||||
private Fragment mCurrentPrimaryItem = null;
|
private Fragment mCurrentPrimaryItem = null;
|
||||||
|
private boolean mExecutingFinishUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for {@link FragmentStatePagerAdapterMenuWorkaround}
|
* Constructor for {@link FragmentStatePagerAdapterMenuWorkaround}
|
||||||
@@ -208,7 +213,7 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
|
|||||||
mFragments.set(position, null);
|
mFragments.set(position, null);
|
||||||
|
|
||||||
mCurTransaction.remove(fragment);
|
mCurTransaction.remove(fragment);
|
||||||
if (fragment == mCurrentPrimaryItem) {
|
if (fragment.equals(mCurrentPrimaryItem)) {
|
||||||
mCurrentPrimaryItem = null;
|
mCurrentPrimaryItem = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +252,19 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
|
|||||||
@Override
|
@Override
|
||||||
public void finishUpdate(@NonNull final ViewGroup container) {
|
public void finishUpdate(@NonNull final ViewGroup container) {
|
||||||
if (mCurTransaction != null) {
|
if (mCurTransaction != null) {
|
||||||
mCurTransaction.commitNowAllowingStateLoss();
|
// We drop any transactions that attempt to be committed
|
||||||
|
// from a re-entrant call to finishUpdate(). We need to
|
||||||
|
// do this as a workaround for Robolectric running measure/layout
|
||||||
|
// calls inline rather than allowing them to be posted
|
||||||
|
// as they would on a real device.
|
||||||
|
if (!mExecutingFinishUpdate) {
|
||||||
|
try {
|
||||||
|
mExecutingFinishUpdate = true;
|
||||||
|
mCurTransaction.commitNowAllowingStateLoss();
|
||||||
|
} finally {
|
||||||
|
mExecutingFinishUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
mCurTransaction = null;
|
mCurTransaction = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,6 @@ import android.content.SharedPreferences;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.app.NotificationChannelCompat;
|
import androidx.core.app.NotificationChannelCompat;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
import androidx.multidex.MultiDexApplication;
|
import androidx.multidex.MultiDexApplication;
|
||||||
@@ -37,7 +36,6 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
|
||||||
import io.reactivex.rxjava3.exceptions.CompositeException;
|
import io.reactivex.rxjava3.exceptions.CompositeException;
|
||||||
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
|
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
|
||||||
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
|
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
|
||||||
@@ -68,9 +66,6 @@ public class App extends MultiDexApplication {
|
|||||||
private static final String TAG = App.class.toString();
|
private static final String TAG = App.class.toString();
|
||||||
private static App app;
|
private static App app;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Disposable disposable = null;
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static App getApp() {
|
public static App getApp() {
|
||||||
return app;
|
return app;
|
||||||
@@ -116,16 +111,10 @@ public class App extends MultiDexApplication {
|
|||||||
&& prefs.getBoolean(getString(R.string.show_image_indicators_key), false));
|
&& prefs.getBoolean(getString(R.string.show_image_indicators_key), false));
|
||||||
|
|
||||||
configureRxJavaErrorHandler();
|
configureRxJavaErrorHandler();
|
||||||
|
|
||||||
// Check for new version
|
|
||||||
disposable = CheckForNewAppVersion.checkNewVersion(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTerminate() {
|
public void onTerminate() {
|
||||||
if (disposable != null) {
|
|
||||||
disposable.dispose();
|
|
||||||
}
|
|
||||||
super.onTerminate();
|
super.onTerminate();
|
||||||
PicassoHelper.terminate();
|
PicassoHelper.terminate();
|
||||||
}
|
}
|
||||||
@@ -265,4 +254,5 @@ public class App extends MultiDexApplication {
|
|||||||
protected boolean isDisposedRxExceptionsReported() {
|
protected boolean isDisposedRxExceptionsReported() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,6 @@ public abstract class BaseFragment extends Fragment {
|
|||||||
//These values are used for controlling fragments when they are part of the frontpage
|
//These values are used for controlling fragments when they are part of the frontpage
|
||||||
@State
|
@State
|
||||||
protected boolean useAsFrontPage = false;
|
protected boolean useAsFrontPage = false;
|
||||||
private boolean mIsVisibleToUser = false;
|
|
||||||
|
|
||||||
public void useAsFrontPage(final boolean value) {
|
public void useAsFrontPage(final boolean value) {
|
||||||
useAsFrontPage = value;
|
useAsFrontPage = value;
|
||||||
@@ -85,12 +84,6 @@ public abstract class BaseFragment extends Fragment {
|
|||||||
AppWatcher.INSTANCE.getObjectWatcher().watch(this);
|
AppWatcher.INSTANCE.getObjectWatcher().watch(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
|
||||||
super.setUserVisibleHint(isVisibleToUser);
|
|
||||||
mIsVisibleToUser = isVisibleToUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Init
|
// Init
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@@ -109,8 +102,7 @@ public abstract class BaseFragment extends Fragment {
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "setTitle() called with: title = [" + title + "]");
|
Log.d(TAG, "setTitle() called with: title = [" + title + "]");
|
||||||
}
|
}
|
||||||
if ((!useAsFrontPage || mIsVisibleToUser)
|
if (!useAsFrontPage && activity != null && activity.getSupportActionBar() != null) {
|
||||||
&& (activity != null && activity.getSupportActionBar() != null)) {
|
|
||||||
activity.getSupportActionBar().setDisplayShowTitleEnabled(true);
|
activity.getSupportActionBar().setDisplayShowTitleEnabled(true);
|
||||||
activity.getSupportActionBar().setTitle(title);
|
activity.getSupportActionBar().setTitle(title);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,9 @@
|
|||||||
|
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.CheckForNewAppVersion.startNewVersionCheckService;
|
||||||
|
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -91,8 +94,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
private static final String TAG = "MainActivity";
|
private static final String TAG = "MainActivity";
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
@@ -165,7 +166,57 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
openMiniPlayerUponPlayerStarted();
|
openMiniPlayerUponPlayerStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupDrawer() throws Exception {
|
@Override
|
||||||
|
protected void onPostCreate(final Bundle savedInstanceState) {
|
||||||
|
super.onPostCreate(savedInstanceState);
|
||||||
|
|
||||||
|
final App app = App.getApp();
|
||||||
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
|
||||||
|
|
||||||
|
if (prefs.getBoolean(app.getString(R.string.update_app_key), true)) {
|
||||||
|
// Start the service which is checking all conditions
|
||||||
|
// and eventually searching for a new version.
|
||||||
|
// The service searching for a new NewPipe version must not be started in background.
|
||||||
|
startNewVersionCheckService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDrawer() throws ExtractionException {
|
||||||
|
addDrawerMenuForCurrentService();
|
||||||
|
|
||||||
|
toggle = new ActionBarDrawerToggle(this, mainBinding.getRoot(),
|
||||||
|
toolbarLayoutBinding.toolbar, R.string.drawer_open, R.string.drawer_close);
|
||||||
|
toggle.syncState();
|
||||||
|
mainBinding.getRoot().addDrawerListener(toggle);
|
||||||
|
mainBinding.getRoot().addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
|
||||||
|
private int lastService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDrawerOpened(final View drawerView) {
|
||||||
|
lastService = ServiceHelper.getSelectedServiceId(MainActivity.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDrawerClosed(final View drawerView) {
|
||||||
|
if (servicesShown) {
|
||||||
|
toggleServices();
|
||||||
|
}
|
||||||
|
if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) {
|
||||||
|
ActivityCompat.recreate(MainActivity.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
drawerLayoutBinding.navigation.setNavigationItemSelectedListener(this::drawerItemSelected);
|
||||||
|
setupDrawerHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the drawer menu for the current service.
|
||||||
|
*
|
||||||
|
* @throws ExtractionException
|
||||||
|
*/
|
||||||
|
private void addDrawerMenuForCurrentService() throws ExtractionException {
|
||||||
//Tabs
|
//Tabs
|
||||||
final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
|
final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
|
||||||
final StreamingService service = NewPipe.getService(currentServiceId);
|
final StreamingService service = NewPipe.getService(currentServiceId);
|
||||||
@@ -204,32 +255,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
drawerLayoutBinding.navigation.getMenu()
|
drawerLayoutBinding.navigation.getMenu()
|
||||||
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
|
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
|
||||||
.setIcon(R.drawable.ic_info_outline);
|
.setIcon(R.drawable.ic_info_outline);
|
||||||
|
|
||||||
toggle = new ActionBarDrawerToggle(this, mainBinding.getRoot(),
|
|
||||||
toolbarLayoutBinding.toolbar, R.string.drawer_open, R.string.drawer_close);
|
|
||||||
toggle.syncState();
|
|
||||||
mainBinding.getRoot().addDrawerListener(toggle);
|
|
||||||
mainBinding.getRoot().addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
|
|
||||||
private int lastService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDrawerOpened(final View drawerView) {
|
|
||||||
lastService = ServiceHelper.getSelectedServiceId(MainActivity.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDrawerClosed(final View drawerView) {
|
|
||||||
if (servicesShown) {
|
|
||||||
toggleServices();
|
|
||||||
}
|
|
||||||
if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) {
|
|
||||||
ActivityCompat.recreate(MainActivity.this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
drawerLayoutBinding.navigation.setNavigationItemSelectedListener(this::drawerItemSelected);
|
|
||||||
setupDrawerHeader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean drawerItemSelected(final MenuItem item) {
|
private boolean drawerItemSelected(final MenuItem item) {
|
||||||
@@ -337,11 +362,15 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_tabs_group);
|
drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_tabs_group);
|
||||||
drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_options_about_group);
|
drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_options_about_group);
|
||||||
|
|
||||||
|
// Show up or down arrow
|
||||||
|
drawerHeaderBinding.drawerArrow.setImageResource(
|
||||||
|
servicesShown ? R.drawable.ic_arrow_drop_up : R.drawable.ic_arrow_drop_down);
|
||||||
|
|
||||||
if (servicesShown) {
|
if (servicesShown) {
|
||||||
showServices();
|
showServices();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
showTabs();
|
addDrawerMenuForCurrentService();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
ErrorActivity.reportUiErrorInSnackbar(this, "Showing main page tabs", e);
|
ErrorActivity.reportUiErrorInSnackbar(this, "Showing main page tabs", e);
|
||||||
}
|
}
|
||||||
@@ -349,8 +378,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showServices() {
|
private void showServices() {
|
||||||
drawerHeaderBinding.drawerArrow.setImageResource(R.drawable.ic_arrow_drop_up);
|
|
||||||
|
|
||||||
for (final StreamingService s : NewPipe.getServices()) {
|
for (final StreamingService s : NewPipe.getServices()) {
|
||||||
final String title = s.getServiceInfo().getName()
|
final String title = s.getServiceInfo().getName()
|
||||||
+ (ServiceHelper.isBeta(s) ? " (beta)" : "");
|
+ (ServiceHelper.isBeta(s) ? " (beta)" : "");
|
||||||
@@ -414,48 +441,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
menuItem.setActionView(spinner);
|
menuItem.setActionView(spinner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showTabs() throws ExtractionException {
|
|
||||||
drawerHeaderBinding.drawerArrow.setImageResource(R.drawable.ic_arrow_drop_down);
|
|
||||||
|
|
||||||
//Tabs
|
|
||||||
final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
|
|
||||||
final StreamingService service = NewPipe.getService(currentServiceId);
|
|
||||||
|
|
||||||
int kioskId = 0;
|
|
||||||
|
|
||||||
for (final String ks : service.getKioskList().getAvailableKiosks()) {
|
|
||||||
drawerLayoutBinding.navigation.getMenu()
|
|
||||||
.add(R.id.menu_tabs_group, kioskId, ORDER,
|
|
||||||
KioskTranslator.getTranslatedKioskName(ks, this))
|
|
||||||
.setIcon(KioskTranslator.getKioskIcon(ks, this));
|
|
||||||
kioskId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawerLayoutBinding.navigation.getMenu()
|
|
||||||
.add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions)
|
|
||||||
.setIcon(R.drawable.ic_tv);
|
|
||||||
drawerLayoutBinding.navigation.getMenu()
|
|
||||||
.add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title)
|
|
||||||
.setIcon(R.drawable.ic_rss_feed);
|
|
||||||
drawerLayoutBinding.navigation.getMenu()
|
|
||||||
.add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks)
|
|
||||||
.setIcon(R.drawable.ic_bookmark);
|
|
||||||
drawerLayoutBinding.navigation.getMenu()
|
|
||||||
.add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads)
|
|
||||||
.setIcon(R.drawable.ic_file_download);
|
|
||||||
drawerLayoutBinding.navigation.getMenu()
|
|
||||||
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
|
|
||||||
.setIcon(R.drawable.ic_history);
|
|
||||||
|
|
||||||
//Settings and About
|
|
||||||
drawerLayoutBinding.navigation.getMenu()
|
|
||||||
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
|
|
||||||
.setIcon(R.drawable.ic_settings);
|
|
||||||
drawerLayoutBinding.navigation.getMenu()
|
|
||||||
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
|
|
||||||
.setIcon(R.drawable.ic_info_outline);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
@@ -9,8 +9,8 @@ import android.widget.PopupMenu;
|
|||||||
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistCreationDialog;
|
import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
@@ -18,6 +18,9 @@ import org.schabi.newpipe.util.NavigationHelper;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
public final class QueueItemMenuUtil {
|
public final class QueueItemMenuUtil {
|
||||||
|
private QueueItemMenuUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
public static void openPopupMenu(final PlayQueue playQueue,
|
public static void openPopupMenu(final PlayQueue playQueue,
|
||||||
final PlayQueueItem item,
|
final PlayQueueItem item,
|
||||||
final View view,
|
final View view,
|
||||||
@@ -47,13 +50,22 @@ public final class QueueItemMenuUtil {
|
|||||||
false);
|
false);
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_item_append_playlist:
|
case R.id.menu_item_append_playlist:
|
||||||
final PlaylistAppendDialog d = PlaylistAppendDialog.fromPlayQueueItems(
|
PlaylistDialog.createCorrespondingDialog(
|
||||||
Collections.singletonList(item)
|
context,
|
||||||
|
Collections.singletonList(new StreamEntity(item)),
|
||||||
|
dialog -> dialog.show(
|
||||||
|
fragmentManager,
|
||||||
|
"QueueItemMenuUtil@append_playlist"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
PlaylistAppendDialog.onPlaylistFound(context,
|
|
||||||
() -> d.show(fragmentManager, "QueueItemMenuUtil@append_playlist"),
|
return true;
|
||||||
() -> PlaylistCreationDialog.newInstance(d)
|
case R.id.menu_item_channel_details:
|
||||||
.show(fragmentManager, "QueueItemMenuUtil@append_playlist"));
|
// An intent must be used here.
|
||||||
|
// Opening with FragmentManager transactions is not working,
|
||||||
|
// as PlayQueueActivity doesn't use fragments.
|
||||||
|
NavigationHelper.openChannelFragmentUsingIntent(context, item.getServiceId(),
|
||||||
|
item.getUploaderUrl(), item.getUploader());
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_item_share:
|
case R.id.menu_item_share:
|
||||||
shareText(context, item.getTitle(), item.getUrl(),
|
shareText(context, item.getTitle(), item.getUrl(),
|
||||||
@@ -65,6 +77,4 @@ public final class QueueItemMenuUtil {
|
|||||||
|
|
||||||
popupMenu.show();
|
popupMenu.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private QueueItemMenuUtil() { }
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
|
||||||
|
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.IntentService;
|
import android.app.IntentService;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -30,6 +33,7 @@ import androidx.core.widget.TextViewCompat;
|
|||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
||||||
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
||||||
import org.schabi.newpipe.download.DownloadDialog;
|
import org.schabi.newpipe.download.DownloadDialog;
|
||||||
@@ -56,6 +60,7 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
|||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||||
|
import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
||||||
import org.schabi.newpipe.player.MainPlayer;
|
import org.schabi.newpipe.player.MainPlayer;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHolder;
|
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||||
@@ -69,14 +74,15 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
|||||||
import org.schabi.newpipe.util.ListHelper;
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||||
import org.schabi.newpipe.util.urlfinder.UrlFinder;
|
import org.schabi.newpipe.util.urlfinder.UrlFinder;
|
||||||
import org.schabi.newpipe.views.FocusOverlayView;
|
import org.schabi.newpipe.views.FocusOverlayView;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import icepick.Icepick;
|
import icepick.Icepick;
|
||||||
@@ -89,9 +95,6 @@ import io.reactivex.rxjava3.disposables.Disposable;
|
|||||||
import io.reactivex.rxjava3.functions.Consumer;
|
import io.reactivex.rxjava3.functions.Consumer;
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
|
||||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
|
|
||||||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the url from the intent and open it in the chosen preferred player.
|
* Get the url from the intent and open it in the chosen preferred player.
|
||||||
*/
|
*/
|
||||||
@@ -107,6 +110,7 @@ public class RouterActivity extends AppCompatActivity {
|
|||||||
protected String currentUrl;
|
protected String currentUrl;
|
||||||
private StreamingService currentService;
|
private StreamingService currentService;
|
||||||
private boolean selectionIsDownload = false;
|
private boolean selectionIsDownload = false;
|
||||||
|
private boolean selectionIsAddToPlaylist = false;
|
||||||
private AlertDialog alertDialogChoice = null;
|
private AlertDialog alertDialogChoice = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -350,7 +354,7 @@ public class RouterActivity extends AppCompatActivity {
|
|||||||
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
|
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
|
||||||
.setPositiveButton(R.string.always, dialogButtonsClickListener)
|
.setPositiveButton(R.string.always, dialogButtonsClickListener)
|
||||||
.setOnDismissListener((dialog) -> {
|
.setOnDismissListener((dialog) -> {
|
||||||
if (!selectionIsDownload) {
|
if (!selectionIsDownload && !selectionIsAddToPlaylist) {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -446,6 +450,10 @@ public class RouterActivity extends AppCompatActivity {
|
|||||||
final AdapterChoiceItem backgroundPlayer = new AdapterChoiceItem(
|
final AdapterChoiceItem backgroundPlayer = new AdapterChoiceItem(
|
||||||
getString(R.string.background_player_key), getString(R.string.background_player),
|
getString(R.string.background_player_key), getString(R.string.background_player),
|
||||||
R.drawable.ic_headset);
|
R.drawable.ic_headset);
|
||||||
|
final AdapterChoiceItem addToPlaylist = new AdapterChoiceItem(
|
||||||
|
getString(R.string.add_to_playlist_key), getString(R.string.add_to_playlist),
|
||||||
|
R.drawable.ic_add);
|
||||||
|
|
||||||
|
|
||||||
if (linkType == LinkType.STREAM) {
|
if (linkType == LinkType.STREAM) {
|
||||||
if (isExtVideoEnabled) {
|
if (isExtVideoEnabled) {
|
||||||
@@ -482,6 +490,10 @@ public class RouterActivity extends AppCompatActivity {
|
|||||||
getString(R.string.download),
|
getString(R.string.download),
|
||||||
R.drawable.ic_file_download));
|
R.drawable.ic_file_download));
|
||||||
|
|
||||||
|
// Add to playlist is not necessary for CHANNEL and PLAYLIST linkType since those can
|
||||||
|
// not be added to a playlist
|
||||||
|
returnList.add(addToPlaylist);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
returnList.add(showInfo);
|
returnList.add(showInfo);
|
||||||
if (capabilities.contains(VIDEO) && !isExtVideoEnabled) {
|
if (capabilities.contains(VIDEO) && !isExtVideoEnabled) {
|
||||||
@@ -547,6 +559,12 @@ public class RouterActivity extends AppCompatActivity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedChoiceKey.equals(getString(R.string.add_to_playlist_key))) {
|
||||||
|
selectionIsAddToPlaylist = true;
|
||||||
|
openAddToPlaylistDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// stop and bypass FetcherService if InfoScreen was selected since
|
// stop and bypass FetcherService if InfoScreen was selected since
|
||||||
// StreamDetailFragment can fetch data itself
|
// StreamDetailFragment can fetch data itself
|
||||||
if (selectedChoiceKey.equals(getString(R.string.show_info_key))) {
|
if (selectedChoiceKey.equals(getString(R.string.show_info_key))) {
|
||||||
@@ -572,6 +590,41 @@ public class RouterActivity extends AppCompatActivity {
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openAddToPlaylistDialog() {
|
||||||
|
// Getting the stream info usually takes a moment
|
||||||
|
// Notifying the user here to ensure that no confusion arises
|
||||||
|
Toast.makeText(
|
||||||
|
getApplicationContext(),
|
||||||
|
getString(R.string.processing_may_take_a_moment),
|
||||||
|
Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
|
||||||
|
disposables.add(ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, false)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
info -> PlaylistDialog.createCorrespondingDialog(
|
||||||
|
getThemeWrapperContext(),
|
||||||
|
Collections.singletonList(new StreamEntity(info)),
|
||||||
|
playlistDialog -> {
|
||||||
|
playlistDialog.setOnDismissListener(dialog -> finish());
|
||||||
|
|
||||||
|
playlistDialog.show(
|
||||||
|
this.getSupportFragmentManager(),
|
||||||
|
"addToPlaylistDialog"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
throwable -> handleError(this, new ErrorInfo(
|
||||||
|
throwable,
|
||||||
|
UserAction.REQUESTED_STREAM,
|
||||||
|
"Tried to add " + currentUrl + " to a playlist",
|
||||||
|
currentService.getServiceId())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
private void openDownloadDialog() {
|
private void openDownloadDialog() {
|
||||||
disposables.add(ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true)
|
disposables.add(ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true)
|
||||||
|
@@ -7,6 +7,7 @@ import androidx.room.Query
|
|||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import io.reactivex.rxjava3.core.Flowable
|
import io.reactivex.rxjava3.core.Flowable
|
||||||
|
import io.reactivex.rxjava3.core.Maybe
|
||||||
import org.schabi.newpipe.database.feed.model.FeedEntity
|
import org.schabi.newpipe.database.feed.model.FeedEntity
|
||||||
import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity
|
import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity
|
||||||
import org.schabi.newpipe.database.stream.StreamWithState
|
import org.schabi.newpipe.database.stream.StreamWithState
|
||||||
@@ -37,7 +38,7 @@ abstract class FeedDAO {
|
|||||||
LIMIT 500
|
LIMIT 500
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
abstract fun getAllStreams(): Flowable<List<StreamWithState>>
|
abstract fun getAllStreams(): Maybe<List<StreamWithState>>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""
|
"""
|
||||||
@@ -62,7 +63,7 @@ abstract class FeedDAO {
|
|||||||
LIMIT 500
|
LIMIT 500
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
abstract fun getAllStreamsForGroup(groupId: Long): Flowable<List<StreamWithState>>
|
abstract fun getAllStreamsForGroup(groupId: Long): Maybe<List<StreamWithState>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see StreamStateEntity.isFinished()
|
* @see StreamStateEntity.isFinished()
|
||||||
@@ -97,7 +98,7 @@ abstract class FeedDAO {
|
|||||||
LIMIT 500
|
LIMIT 500
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
abstract fun getLiveOrNotPlayedStreams(): Flowable<List<StreamWithState>>
|
abstract fun getLiveOrNotPlayedStreams(): Maybe<List<StreamWithState>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see StreamStateEntity.isFinished()
|
* @see StreamStateEntity.isFinished()
|
||||||
@@ -137,7 +138,7 @@ abstract class FeedDAO {
|
|||||||
LIMIT 500
|
LIMIT 500
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
abstract fun getLiveOrNotPlayedStreamsForGroup(groupId: Long): Flowable<List<StreamWithState>>
|
abstract fun getLiveOrNotPlayedStreamsForGroup(groupId: Long): Maybe<List<StreamWithState>>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""
|
"""
|
||||||
|
@@ -0,0 +1,103 @@
|
|||||||
|
package org.schabi.newpipe.error;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that a Exception is serializable.
|
||||||
|
* This is
|
||||||
|
*/
|
||||||
|
public final class EnsureExceptionSerializable {
|
||||||
|
private static final String TAG = "EnsureExSerializable";
|
||||||
|
|
||||||
|
private EnsureExceptionSerializable() {
|
||||||
|
// No instance
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that an exception is serializable.
|
||||||
|
* <br/>
|
||||||
|
* If that is not the case a {@link WorkaroundNotSerializableException} is created.
|
||||||
|
*
|
||||||
|
* @param exception
|
||||||
|
* @return if an exception is not serializable a new {@link WorkaroundNotSerializableException}
|
||||||
|
* otherwise the exception from the parameter
|
||||||
|
*/
|
||||||
|
public static Exception ensureSerializable(@NonNull final Exception exception) {
|
||||||
|
return checkIfSerializable(exception)
|
||||||
|
? exception
|
||||||
|
: WorkaroundNotSerializableException.create(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkIfSerializable(@NonNull final Exception exception) {
|
||||||
|
try {
|
||||||
|
// Check by creating a new ObjectOutputStream which does the serialization
|
||||||
|
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
ObjectOutputStream oos = new ObjectOutputStream(bos)
|
||||||
|
) {
|
||||||
|
oos.writeObject(exception);
|
||||||
|
oos.flush();
|
||||||
|
|
||||||
|
bos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
Log.d(TAG, "Exception is not serializable", ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WorkaroundNotSerializableException extends Exception {
|
||||||
|
protected WorkaroundNotSerializableException(
|
||||||
|
final Throwable notSerializableException,
|
||||||
|
final Throwable cause) {
|
||||||
|
super(notSerializableException.toString(), cause);
|
||||||
|
setStackTrace(notSerializableException.getStackTrace());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WorkaroundNotSerializableException(final Throwable notSerializableException) {
|
||||||
|
super(notSerializableException.toString());
|
||||||
|
setStackTrace(notSerializableException.getStackTrace());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorkaroundNotSerializableException create(
|
||||||
|
@NonNull final Exception notSerializableException
|
||||||
|
) {
|
||||||
|
// Build a list of the exception + all causes
|
||||||
|
final List<Throwable> throwableList = new ArrayList<>();
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
Throwable throwableToProcess = notSerializableException;
|
||||||
|
|
||||||
|
while (throwableToProcess != null) {
|
||||||
|
throwableList.add(throwableToProcess);
|
||||||
|
|
||||||
|
pos++;
|
||||||
|
throwableToProcess = throwableToProcess.getCause();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse list so that it starts with the last one
|
||||||
|
Collections.reverse(throwableList);
|
||||||
|
|
||||||
|
// Build exception stack
|
||||||
|
WorkaroundNotSerializableException cause = null;
|
||||||
|
for (final Throwable t : throwableList) {
|
||||||
|
cause = cause == null
|
||||||
|
? new WorkaroundNotSerializableException(t)
|
||||||
|
: new WorkaroundNotSerializableException(t, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -77,6 +77,16 @@ public class ErrorActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private ActivityErrorBinding activityErrorBinding;
|
private ActivityErrorBinding activityErrorBinding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports a new error by starting a new activity.
|
||||||
|
* <br/>
|
||||||
|
* Ensure that the data within errorInfo is serializable otherwise
|
||||||
|
* an exception will be thrown!<br/>
|
||||||
|
* {@link EnsureExceptionSerializable} might help.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param errorInfo
|
||||||
|
*/
|
||||||
public static void reportError(final Context context, final ErrorInfo errorInfo) {
|
public static void reportError(final Context context, final ErrorInfo errorInfo) {
|
||||||
final Intent intent = new Intent(context, ErrorActivity.class);
|
final Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
intent.putExtra(ERROR_INFO, errorInfo);
|
intent.putExtra(ERROR_INFO, errorInfo);
|
||||||
|
@@ -20,8 +20,8 @@ public class BlankFragment extends BaseFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
public void onResume() {
|
||||||
super.setUserVisibleHint(isVisibleToUser);
|
super.onResume();
|
||||||
setTitle("NewPipe");
|
setTitle("NewPipe");
|
||||||
// leave this inline. Will make it harder for copy cats.
|
// leave this inline. Will make it harder for copy cats.
|
||||||
// If you are a Copy cat FUCK YOU.
|
// If you are a Copy cat FUCK YOU.
|
||||||
|
@@ -52,6 +52,7 @@ import com.squareup.picasso.Callback;
|
|||||||
|
|
||||||
import org.schabi.newpipe.App;
|
import org.schabi.newpipe.App;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.databinding.FragmentVideoDetailBinding;
|
import org.schabi.newpipe.databinding.FragmentVideoDetailBinding;
|
||||||
import org.schabi.newpipe.download.DownloadDialog;
|
import org.schabi.newpipe.download.DownloadDialog;
|
||||||
import org.schabi.newpipe.error.ErrorActivity;
|
import org.schabi.newpipe.error.ErrorActivity;
|
||||||
@@ -73,10 +74,10 @@ import org.schabi.newpipe.fragments.EmptyFragment;
|
|||||||
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
|
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
|
||||||
import org.schabi.newpipe.fragments.list.videos.RelatedItemsFragment;
|
import org.schabi.newpipe.fragments.list.videos.RelatedItemsFragment;
|
||||||
import org.schabi.newpipe.ktx.AnimationType;
|
import org.schabi.newpipe.ktx.AnimationType;
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistCreationDialog;
|
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.player.MainPlayer;
|
import org.schabi.newpipe.player.MainPlayer;
|
||||||
|
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
||||||
import org.schabi.newpipe.player.Player;
|
import org.schabi.newpipe.player.Player;
|
||||||
import org.schabi.newpipe.player.event.OnKeyDownListener;
|
import org.schabi.newpipe.player.event.OnKeyDownListener;
|
||||||
import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener;
|
import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener;
|
||||||
@@ -98,6 +99,7 @@ import org.schabi.newpipe.util.external_communication.KoreUtils;
|
|||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -443,12 +445,11 @@ public final class VideoDetailFragment
|
|||||||
break;
|
break;
|
||||||
case R.id.detail_controls_playlist_append:
|
case R.id.detail_controls_playlist_append:
|
||||||
if (getFM() != null && currentInfo != null) {
|
if (getFM() != null && currentInfo != null) {
|
||||||
|
|
||||||
final PlaylistAppendDialog d = PlaylistAppendDialog.fromStreamInfo(currentInfo);
|
|
||||||
disposables.add(
|
disposables.add(
|
||||||
PlaylistAppendDialog.onPlaylistFound(getContext(),
|
PlaylistDialog.createCorrespondingDialog(
|
||||||
() -> d.show(getFM(), TAG),
|
getContext(),
|
||||||
() -> PlaylistCreationDialog.newInstance(d).show(getFM(), TAG)
|
Collections.singletonList(new StreamEntity(currentInfo)),
|
||||||
|
dialog -> dialog.show(getFM(), TAG)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -593,6 +594,11 @@ public final class VideoDetailFragment
|
|||||||
// Init
|
// Init
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(rootView, savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
@Override // called from onViewCreated in {@link BaseFragment#onViewCreated}
|
@Override // called from onViewCreated in {@link BaseFragment#onViewCreated}
|
||||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||||
super.initViews(rootView, savedInstanceState);
|
super.initViews(rootView, savedInstanceState);
|
||||||
@@ -603,6 +609,18 @@ public final class VideoDetailFragment
|
|||||||
|
|
||||||
binding.detailThumbnailRootLayout.requestFocus();
|
binding.detailThumbnailRootLayout.requestFocus();
|
||||||
|
|
||||||
|
binding.detailControlsPlayWithKodi.setVisibility(
|
||||||
|
KoreUtils.shouldShowPlayWithKodi(requireContext(), serviceId)
|
||||||
|
? View.VISIBLE
|
||||||
|
: View.GONE
|
||||||
|
);
|
||||||
|
binding.detailControlsCrashThePlayer.setVisibility(
|
||||||
|
DEBUG && PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||||
|
.getBoolean(getString(R.string.show_crash_the_player_key), false)
|
||||||
|
? View.VISIBLE
|
||||||
|
: View.GONE
|
||||||
|
);
|
||||||
|
|
||||||
if (DeviceUtils.isTv(getContext())) {
|
if (DeviceUtils.isTv(getContext())) {
|
||||||
// remove ripple effects from detail controls
|
// remove ripple effects from detail controls
|
||||||
final int transparent = ContextCompat.getColor(requireContext(),
|
final int transparent = ContextCompat.getColor(requireContext(),
|
||||||
@@ -637,8 +655,14 @@ public final class VideoDetailFragment
|
|||||||
binding.detailControlsShare.setOnClickListener(this);
|
binding.detailControlsShare.setOnClickListener(this);
|
||||||
binding.detailControlsOpenInBrowser.setOnClickListener(this);
|
binding.detailControlsOpenInBrowser.setOnClickListener(this);
|
||||||
binding.detailControlsPlayWithKodi.setOnClickListener(this);
|
binding.detailControlsPlayWithKodi.setOnClickListener(this);
|
||||||
binding.detailControlsPlayWithKodi.setVisibility(KoreUtils.shouldShowPlayWithKodi(
|
if (DEBUG) {
|
||||||
requireContext(), serviceId) ? View.VISIBLE : View.GONE);
|
binding.detailControlsCrashThePlayer.setOnClickListener(
|
||||||
|
v -> VideoDetailPlayerCrasher.onCrashThePlayer(
|
||||||
|
this.getContext(),
|
||||||
|
this.player,
|
||||||
|
getLayoutInflater())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
binding.overlayThumbnail.setOnClickListener(this);
|
binding.overlayThumbnail.setOnClickListener(this);
|
||||||
binding.overlayThumbnail.setOnLongClickListener(this);
|
binding.overlayThumbnail.setOnLongClickListener(this);
|
||||||
@@ -1095,8 +1119,8 @@ public final class VideoDetailFragment
|
|||||||
toggleFullscreenIfInFullscreenMode();
|
toggleFullscreenIfInFullscreenMode();
|
||||||
|
|
||||||
final PlayQueue queue = setupPlayQueueForIntent(append);
|
final PlayQueue queue = setupPlayQueueForIntent(append);
|
||||||
if (append) {
|
if (append) { //resumePlayback: false
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, queue, false);
|
NavigationHelper.enqueueOnPlayer(activity, queue, PlayerType.POPUP);
|
||||||
} else {
|
} else {
|
||||||
replaceQueueIfUserConfirms(() -> NavigationHelper
|
replaceQueueIfUserConfirms(() -> NavigationHelper
|
||||||
.playOnPopupPlayer(activity, queue, true));
|
.playOnPopupPlayer(activity, queue, true));
|
||||||
@@ -1154,7 +1178,7 @@ public final class VideoDetailFragment
|
|||||||
|
|
||||||
final PlayQueue queue = setupPlayQueueForIntent(append);
|
final PlayQueue queue = setupPlayQueueForIntent(append);
|
||||||
if (append) {
|
if (append) {
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, queue, false);
|
NavigationHelper.enqueueOnPlayer(activity, queue, PlayerType.AUDIO);
|
||||||
} else {
|
} else {
|
||||||
replaceQueueIfUserConfirms(() -> NavigationHelper
|
replaceQueueIfUserConfirms(() -> NavigationHelper
|
||||||
.playOnBackgroundPlayer(activity, queue, true));
|
.playOnBackgroundPlayer(activity, queue, true));
|
||||||
|
@@ -0,0 +1,159 @@
|
|||||||
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ContextThemeWrapper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.RadioGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
||||||
|
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
||||||
|
import org.schabi.newpipe.player.Player;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outsourced logic for crashing the player in the {@link VideoDetailFragment}.
|
||||||
|
*/
|
||||||
|
public final class VideoDetailPlayerCrasher {
|
||||||
|
|
||||||
|
// This has to be <= 23 chars on devices running Android 7 or lower (API <= 25)
|
||||||
|
// or it fails with an IllegalArgumentException
|
||||||
|
// https://stackoverflow.com/a/54744028
|
||||||
|
private static final String TAG = "VideoDetPlayerCrasher";
|
||||||
|
|
||||||
|
private static final Map<String, Supplier<ExoPlaybackException>> AVAILABLE_EXCEPTION_TYPES =
|
||||||
|
getExceptionTypes();
|
||||||
|
|
||||||
|
private VideoDetailPlayerCrasher() {
|
||||||
|
// No impls
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Supplier<ExoPlaybackException>> getExceptionTypes() {
|
||||||
|
final String defaultMsg = "Dummy";
|
||||||
|
final Map<String, Supplier<ExoPlaybackException>> exceptionTypes = new LinkedHashMap<>();
|
||||||
|
exceptionTypes.put(
|
||||||
|
"Source",
|
||||||
|
() -> ExoPlaybackException.createForSource(
|
||||||
|
new IOException(defaultMsg)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
exceptionTypes.put(
|
||||||
|
"Renderer",
|
||||||
|
() -> ExoPlaybackException.createForRenderer(
|
||||||
|
new Exception(defaultMsg),
|
||||||
|
"Dummy renderer",
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
C.FORMAT_HANDLED
|
||||||
|
)
|
||||||
|
);
|
||||||
|
exceptionTypes.put(
|
||||||
|
"Unexpected",
|
||||||
|
() -> ExoPlaybackException.createForUnexpected(
|
||||||
|
new RuntimeException(defaultMsg)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
exceptionTypes.put(
|
||||||
|
"Remote",
|
||||||
|
() -> ExoPlaybackException.createForRemote(defaultMsg)
|
||||||
|
);
|
||||||
|
|
||||||
|
return Collections.unmodifiableMap(exceptionTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Context getThemeWrapperContext(final Context context) {
|
||||||
|
return new ContextThemeWrapper(
|
||||||
|
context,
|
||||||
|
ThemeHelper.isLightThemeSelected(context)
|
||||||
|
? R.style.LightTheme
|
||||||
|
: R.style.DarkTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onCrashThePlayer(
|
||||||
|
@NonNull final Context context,
|
||||||
|
@Nullable final Player player,
|
||||||
|
@NonNull final LayoutInflater layoutInflater
|
||||||
|
) {
|
||||||
|
if (player == null) {
|
||||||
|
Log.d(TAG, "Player is not available");
|
||||||
|
Toast.makeText(context, "Player is not available", Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Build the dialog/UI --
|
||||||
|
|
||||||
|
final Context themeWrapperContext = getThemeWrapperContext(context);
|
||||||
|
|
||||||
|
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
|
||||||
|
final RadioGroup radioGroup = SingleChoiceDialogViewBinding.inflate(layoutInflater)
|
||||||
|
.list;
|
||||||
|
|
||||||
|
final AlertDialog alertDialog = new AlertDialog.Builder(getThemeWrapperContext(context))
|
||||||
|
.setTitle("Choose an exception")
|
||||||
|
.setView(radioGroup)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
for (final Map.Entry<String, Supplier<ExoPlaybackException>> entry
|
||||||
|
: AVAILABLE_EXCEPTION_TYPES.entrySet()) {
|
||||||
|
final RadioButton radioButton = ListRadioIconItemBinding.inflate(inflater).getRoot();
|
||||||
|
radioButton.setText(entry.getKey());
|
||||||
|
radioButton.setChecked(false);
|
||||||
|
radioButton.setLayoutParams(
|
||||||
|
new RadioGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
radioButton.setOnClickListener(v -> {
|
||||||
|
tryCrashPlayerWith(player, entry.getValue().get());
|
||||||
|
if (alertDialog != null) {
|
||||||
|
alertDialog.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
radioGroup.addView(radioButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
alertDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that this method does not crash the underlying exoplayer directly (it's not possible).
|
||||||
|
* It simply supplies a Exception to {@link Player#onPlayerError(ExoPlaybackException)}.
|
||||||
|
* @param player
|
||||||
|
* @param exception
|
||||||
|
*/
|
||||||
|
private static void tryCrashPlayerWith(
|
||||||
|
@NonNull final Player player,
|
||||||
|
@NonNull final ExoPlaybackException exception
|
||||||
|
) {
|
||||||
|
Log.d(TAG, "Crashing the player using player.onPlayerError(ex)");
|
||||||
|
try {
|
||||||
|
player.onPlayerError(exception);
|
||||||
|
} catch (final Exception exPlayer) {
|
||||||
|
Log.e(TAG,
|
||||||
|
"Run into an exception while crashing the player:",
|
||||||
|
exPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -143,7 +143,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
|||||||
final View focusedItem = itemsList.getFocusedChild();
|
final View focusedItem = itemsList.getFocusedChild();
|
||||||
final RecyclerView.ViewHolder itemHolder =
|
final RecyclerView.ViewHolder itemHolder =
|
||||||
itemsList.findContainingViewHolder(focusedItem);
|
itemsList.findContainingViewHolder(focusedItem);
|
||||||
return itemHolder.getAdapterPosition();
|
return itemHolder.getBindingAdapterPosition();
|
||||||
} catch (final NullPointerException e) {
|
} catch (final NullPointerException e) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -350,12 +350,16 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
|||||||
if (context == null || context.getResources() == null || activity == null) {
|
if (context == null || context.getResources() == null || activity == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<StreamDialogEntry> entries = new ArrayList<>();
|
final List<StreamDialogEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
if (PlayerHolder.getInstance().getType() != null) {
|
if (PlayerHolder.getInstance().isPlayerOpen()) {
|
||||||
entries.add(StreamDialogEntry.enqueue);
|
entries.add(StreamDialogEntry.enqueue);
|
||||||
|
|
||||||
|
if (PlayerHolder.getInstance().getQueueSize() > 1) {
|
||||||
|
entries.add(StreamDialogEntry.enqueue_next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
entries.addAll(Arrays.asList(
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
@@ -374,6 +378,13 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
|||||||
if (KoreUtils.shouldShowPlayWithKodi(context, item.getServiceId())) {
|
if (KoreUtils.shouldShowPlayWithKodi(context, item.getServiceId())) {
|
||||||
entries.add(StreamDialogEntry.play_with_kodi);
|
entries.add(StreamDialogEntry.play_with_kodi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// show "mark as watched" only when watch history is enabled
|
||||||
|
if (StreamDialogEntry.shouldAddMarkAsWatched(item.getStreamType(), context)) {
|
||||||
|
entries.add(
|
||||||
|
StreamDialogEntry.mark_as_watched
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!isNullOrEmpty(item.getUploaderUrl())) {
|
if (!isNullOrEmpty(item.getUploaderUrl())) {
|
||||||
entries.add(StreamDialogEntry.show_channel_details);
|
entries.add(StreamDialogEntry.show_channel_details);
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
|||||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||||
import org.schabi.newpipe.ktx.AnimationType;
|
import org.schabi.newpipe.ktx.AnimationType;
|
||||||
import org.schabi.newpipe.local.subscription.SubscriptionManager;
|
import org.schabi.newpipe.local.subscription.SubscriptionManager;
|
||||||
|
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
||||||
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
@@ -97,11 +98,9 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
public void onResume() {
|
||||||
super.setUserVisibleHint(isVisibleToUser);
|
super.onResume();
|
||||||
if (activity != null
|
if (activity != null && useAsFrontPage) {
|
||||||
&& useAsFrontPage
|
|
||||||
&& isVisibleToUser) {
|
|
||||||
setTitle(currentInfo != null ? currentInfo.getName() : name);
|
setTitle(currentInfo != null ? currentInfo.getName() : name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -495,12 +494,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||||||
.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||||
|
|
||||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
|
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.POPUP);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
|
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.AUDIO);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -99,9 +99,12 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
public void onResume() {
|
||||||
super.setUserVisibleHint(isVisibleToUser);
|
super.onResume();
|
||||||
if (useAsFrontPage && isVisibleToUser && activity != null) {
|
if (!Localization.getPreferredContentCountry(requireContext()).equals(contentCountry)) {
|
||||||
|
reloadContent();
|
||||||
|
}
|
||||||
|
if (useAsFrontPage && activity != null) {
|
||||||
try {
|
try {
|
||||||
setTitle(kioskTranslatedName);
|
setTitle(kioskTranslatedName);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
@@ -117,15 +120,6 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||||||
return inflater.inflate(R.layout.fragment_kiosk, container, false);
|
return inflater.inflate(R.layout.fragment_kiosk, container, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
if (!Localization.getPreferredContentCountry(requireContext()).equals(contentCountry)) {
|
|
||||||
reloadContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Menu
|
// Menu
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@@ -37,6 +37,7 @@ import org.schabi.newpipe.extractor.stream.StreamType;
|
|||||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
||||||
|
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHolder;
|
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||||
@@ -148,9 +149,14 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||||||
|
|
||||||
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
if (PlayerHolder.getInstance().getType() != null) {
|
if (PlayerHolder.getInstance().isPlayerOpen()) {
|
||||||
entries.add(StreamDialogEntry.enqueue);
|
entries.add(StreamDialogEntry.enqueue);
|
||||||
|
|
||||||
|
if (PlayerHolder.getInstance().getQueueSize() > 1) {
|
||||||
|
entries.add(StreamDialogEntry.enqueue_next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
entries.addAll(Arrays.asList(
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
@@ -170,6 +176,12 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||||||
entries.add(StreamDialogEntry.play_with_kodi);
|
entries.add(StreamDialogEntry.play_with_kodi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// show "mark as watched" only when watch history is enabled
|
||||||
|
if (StreamDialogEntry.shouldAddMarkAsWatched(item.getStreamType(), context)) {
|
||||||
|
entries.add(
|
||||||
|
StreamDialogEntry.mark_as_watched
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!isNullOrEmpty(item.getUploaderUrl())) {
|
if (!isNullOrEmpty(item.getUploaderUrl())) {
|
||||||
entries.add(StreamDialogEntry.show_channel_details);
|
entries.add(StreamDialogEntry.show_channel_details);
|
||||||
}
|
}
|
||||||
@@ -347,12 +359,12 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||||
|
|
||||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
||||||
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
|
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.POPUP);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
|
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.AUDIO);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1088,7 +1088,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public int getSuggestionMovementFlags(@NonNull final RecyclerView.ViewHolder viewHolder) {
|
public int getSuggestionMovementFlags(@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||||
final int position = viewHolder.getAdapterPosition();
|
final int position = viewHolder.getBindingAdapterPosition();
|
||||||
if (position == RecyclerView.NO_POSITION) {
|
if (position == RecyclerView.NO_POSITION) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1099,7 +1099,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) {
|
public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||||
final int position = viewHolder.getAdapterPosition();
|
final int position = viewHolder.getBindingAdapterPosition();
|
||||||
final String query = suggestionListAdapter.getItem(position).query;
|
final String query = suggestionListAdapter.getItem(position).query;
|
||||||
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
|
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user