mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-10-22 13:32:15 +02:00
Compare commits
73 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0ae04b8ead | ||
![]() |
5d59025b3c | ||
![]() |
768bb0bbcd | ||
![]() |
ac071b383f | ||
![]() |
b79ea7b51b | ||
![]() |
28c72e7f63 | ||
![]() |
5fcc3b4dab | ||
![]() |
7eb13a9b93 | ||
![]() |
7c9896beaf | ||
![]() |
54d3bff26d | ||
![]() |
e0ba9b3902 | ||
![]() |
f11b5ae7a1 | ||
![]() |
7baeb6eca7 | ||
![]() |
4e8519a1b9 | ||
![]() |
e6fffc0d5b | ||
![]() |
4c5fc7fa7c | ||
![]() |
b633108a4c | ||
![]() |
3d42da5ff5 | ||
![]() |
1b869199f4 | ||
![]() |
f3cd2f6c9d | ||
![]() |
2e3e7f9bf2 | ||
![]() |
92327dd9e3 | ||
![]() |
d40b432f46 | ||
![]() |
5b3137093f | ||
![]() |
4fc9f2e5fd | ||
![]() |
ce592f4baf | ||
![]() |
2b3edcf2d1 | ||
![]() |
f165f97bd9 | ||
![]() |
4ec572372e | ||
![]() |
672eb34049 | ||
![]() |
a0b042091b | ||
![]() |
b753705a84 | ||
![]() |
f48ff610a3 | ||
![]() |
93aed9f34c | ||
![]() |
3cf94382e6 | ||
![]() |
f52cb3bbe0 | ||
![]() |
d45182cb5c | ||
![]() |
22847c6c92 | ||
![]() |
a70c51b71c | ||
![]() |
02d417476e | ||
![]() |
bc3139e5f9 | ||
![]() |
c1f7b2653c | ||
![]() |
72dbb9441e | ||
![]() |
bbc13756f3 | ||
![]() |
ba0876b43b | ||
![]() |
c0d41661e8 | ||
![]() |
b2e2551e33 | ||
![]() |
ac371e6fb4 | ||
![]() |
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: ''
|
||||
|
||||
---
|
||||
<!-- 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). -->
|
||||
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the Preview tab). -->
|
||||
|
||||
### Checklist
|
||||
<!-- 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.
|
||||
- [ ] This issue contains only one feature request. I will open one issue for every feature I want to request.
|
||||
|
||||
|
||||
#### Describe the feature you want
|
||||
<!-- 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...* -->
|
||||
#### What feature do you want?
|
||||
<!-- Explain how you want the app's look or behavior to change to suit your needs. -->
|
||||
|
||||
|
||||
|
||||
#### Is your feature request related to a problem? Please describe it
|
||||
<!-- 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! -->
|
||||
|
||||
#### Why do you want this feature?
|
||||
<!-- Describe any problem or limitation you come across while using the app which would be solved by this feature. -->
|
||||
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -33,10 +33,10 @@ jobs:
|
||||
if: github.event_name == 'pull_request'
|
||||
run: git checkout -B ${{ github.head_ref }}
|
||||
|
||||
- name: set up JDK 8
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: 8
|
||||
java-version: 11
|
||||
distribution: "temurin"
|
||||
cache: 'gradle'
|
||||
|
||||
@@ -59,10 +59,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: set up JDK 8
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: 8
|
||||
java-version: 11
|
||||
distribution: "temurin"
|
||||
cache: 'gradle'
|
||||
|
||||
|
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>
|
||||
<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">
|
||||
<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://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://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://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://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://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="Licencia: GPLv3"><img src="https://img.shields.io/badge/Licencia-GPL%20v3-blue.svg"></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://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="Recompensas en Bountysource"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f"></a>
|
||||
</p>
|
||||
<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="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="#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>
|
||||
<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).*
|
||||
|
||||
<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_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)
|
||||
|
||||
## 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
|
||||
|
||||
* Buscar videos
|
||||
* No requiere inicio de sesión
|
||||
* Mostrar información general sobre videos
|
||||
* Mirar videos de YouTube
|
||||
* Escuchar audio de YouTube
|
||||
* Modo popup (reproductor flotante)
|
||||
* Elegir reproductor para mirar el video
|
||||
* Modo de solo audio en videos de YouTube
|
||||
* Modo pop-up (reproductor flotante)
|
||||
* Elegir un reproductor de video externo para mirar videos
|
||||
* Descargar videos
|
||||
* Descargar solamente audio
|
||||
* Abrir video en Kodi
|
||||
* Descargar solo audio
|
||||
* Abrir videos en Kodi
|
||||
* Mostrar videos próximos/relacionados
|
||||
* 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
|
||||
* Buscar canales
|
||||
* Buscar de canales
|
||||
* Mirar videos de un canal
|
||||
* Apoyo Orbot/Tor (todavía no directamente)
|
||||
* Apoyo 1080p/2K/4K
|
||||
* Ver historias
|
||||
* Subscribirse a canales
|
||||
* Buscar historias
|
||||
* Buscar/mirar listas de reproducción
|
||||
* Mirar listas de reproducción en fila
|
||||
* Poner videos en fila
|
||||
* Listas locales de reproducción
|
||||
* Soporte Orbot/Tor (todavía no directamente)
|
||||
* Soporte para videos en 1080p/2K/4K
|
||||
* Historial de videos vistos
|
||||
* Suscripción a canales
|
||||
* Historial de búsquedas
|
||||
* Buscar/Mirar listas de reproducción
|
||||
* Mirar listas de reproducción en cola
|
||||
* Poner videos en cola
|
||||
* Listas de reproducción locales
|
||||
* Subtítulos
|
||||
* Apoyo de medios en directo
|
||||
* Soporte para transmisiones en vivo
|
||||
* Mostrar comentarios
|
||||
|
||||
### Servicios apoyados
|
||||
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:
|
||||
### Servicios Soportados
|
||||
|
||||
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
|
||||
* SoundCloud \[beta\]
|
||||
@@ -80,61 +84,60 @@ NewPipe apoya varios servicios. Nuestras [documentaciones](https://teamnewpipe.g
|
||||
* PeerTube instances \[beta\]
|
||||
* Bandcamp \[beta\]
|
||||
|
||||
<!-- Brecha escondida para mantener compatibles los enlaces viejos. -->
|
||||
<span id="actualizaciones"></span>
|
||||
## Instalación y Actualizaciones
|
||||
|
||||
## Installación y actualizaciones
|
||||
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
|
||||
usuario no esté engañado para instalar una actualización maliciosa a una app.
|
||||
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/).
|
||||
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:
|
||||
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
|
||||
3. Descargar el APK del nuevo fuente e instalarlo.
|
||||
4. Importar los datos del paso 1 a través de Ajustes > Contenido > Importa base de datos.
|
||||
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.
|
||||
|
||||
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:
|
||||
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.
|
||||
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
|
||||
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).
|
||||
|
||||
<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>
|
||||
|
||||
## 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>
|
||||
<tr>
|
||||
<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>
|
||||
</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="assets/liberapay_qr_code.png" alt="Visit NewPipe at 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/"><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="Dona vía Liberapay" height="35px"></a></td>
|
||||
</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="assets/bountysource_qr_code.png" alt="Visit NewPipe at 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"><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="Revisa cuántas recompensas puedes obtener."></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 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/).
|
||||
## Política de Privacidad
|
||||
|
||||
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
|
||||
[](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에 추가하고 우리가 릴리즈를 게시하는 대로 저곳에서 릴리즈를 설치할 수 있습니다.
|
||||
이에 대한 설명서는 이곳에서 확인할 수 있습니다: 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를 생성하고, 이것에 서명하고, 사용자들에서 업데이트를 전달해야만 하기 때문에,
|
||||
이것은 업데이트를 받는 가장 느린 방법입니다.
|
||||
|
||||
|
@@ -89,7 +89,7 @@ NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/doc
|
||||
## Installation and updates
|
||||
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/
|
||||
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.
|
||||
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
|
||||
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/
|
||||
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.
|
||||
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>
|
||||
|
||||
## 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_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
|
||||
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/
|
||||
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.
|
||||
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.
|
||||
|
||||
|
@@ -17,8 +17,8 @@ android {
|
||||
resValue "string", "app_name", "NewPipe"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 29
|
||||
versionCode 976
|
||||
versionName "0.21.10"
|
||||
versionCode 979
|
||||
versionName "0.21.13"
|
||||
|
||||
multiDexEnabled true
|
||||
|
||||
@@ -54,6 +54,11 @@ android {
|
||||
// debug build. This seems to be a Gradle bug, therefore
|
||||
// TODO: update Gradle version
|
||||
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
|
||||
shrinkResources false // disabled to fix F-Droid's reproducible build
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
@@ -102,7 +107,7 @@ ext {
|
||||
icepickVersion = '3.2.0'
|
||||
exoPlayerVersion = '2.12.3'
|
||||
googleAutoServiceVersion = '1.0'
|
||||
groupieVersion = '2.8.1'
|
||||
groupieVersion = '2.9.0'
|
||||
markwonVersion = '4.6.2'
|
||||
|
||||
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
|
||||
// This works thanks to JitPack: https://jitpack.io/
|
||||
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
|
||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.21.10'
|
||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.21.11'
|
||||
|
||||
/** Checkstyle **/
|
||||
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||
|
@@ -43,10 +43,11 @@ class LocalPlaylistManagerTest {
|
||||
|
||||
@Test
|
||||
fun createPlaylist() {
|
||||
val NEWPIPE_URL = "https://newpipe.net/"
|
||||
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",
|
||||
uploaderUrl = "https://newpipe.net/"
|
||||
uploaderUrl = NEWPIPE_URL
|
||||
)
|
||||
|
||||
val result = manager.createPlaylist("name", listOf(stream))
|
||||
|
@@ -146,6 +146,7 @@
|
||||
<data android:pathPrefix="/embed/" />
|
||||
<data android:pathPrefix="/watch" />
|
||||
<data android:pathPrefix="/attribution_link" />
|
||||
<data android:pathPrefix="/shorts/" />
|
||||
<!-- channel prefix -->
|
||||
<data android:pathPrefix="/channel/" />
|
||||
<data android:pathPrefix="/user/" />
|
||||
@@ -224,6 +225,7 @@
|
||||
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:host="tubus.eduvid.org" />
|
||||
<data android:host="invidio.us" />
|
||||
<data android:host="dev.invidio.us" />
|
||||
<data android:host="www.invidio.us" />
|
||||
@@ -309,6 +311,7 @@
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data android:host="eduvid.org" />
|
||||
<data android:host="framatube.org" />
|
||||
<data android:host="media.assassinate-you.net" />
|
||||
<data android:host="peertube.co.uk" />
|
||||
@@ -358,6 +361,9 @@
|
||||
<service
|
||||
android:name=".RouterActivity$FetcherService"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".CheckForNewAppVersion"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- opting out of sending metrics to Google in Android System WebView -->
|
||||
<meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" />
|
||||
|
@@ -5,7 +5,6 @@ import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationChannelCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.multidex.MultiDexApplication;
|
||||
@@ -37,7 +36,6 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.exceptions.CompositeException;
|
||||
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
|
||||
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 App app;
|
||||
|
||||
@Nullable
|
||||
private Disposable disposable = null;
|
||||
|
||||
@NonNull
|
||||
public static App getApp() {
|
||||
return app;
|
||||
@@ -116,16 +111,10 @@ public class App extends MultiDexApplication {
|
||||
&& prefs.getBoolean(getString(R.string.show_image_indicators_key), false));
|
||||
|
||||
configureRxJavaErrorHandler();
|
||||
|
||||
// Check for new version
|
||||
disposable = CheckForNewAppVersion.checkNewVersion(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
super.onTerminate();
|
||||
PicassoHelper.terminate();
|
||||
}
|
||||
@@ -265,4 +254,5 @@ public class App extends MultiDexApplication {
|
||||
protected boolean isDisposedRxExceptionsReported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.IntentService;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -25,8 +26,11 @@ import com.grack.nanojson.JsonParserException;
|
||||
import org.schabi.newpipe.error.ErrorActivity;
|
||||
import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.extractor.downloader.Response;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -36,13 +40,10 @@ import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public final class CheckForNewAppVersion {
|
||||
private CheckForNewAppVersion() { }
|
||||
public final class CheckForNewAppVersion extends IntentService {
|
||||
public CheckForNewAppVersion() {
|
||||
super("CheckForNewAppVersion");
|
||||
}
|
||||
|
||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||
private static final String TAG = CheckForNewAppVersion.class.getSimpleName();
|
||||
@@ -168,78 +169,104 @@ public final class CheckForNewAppVersion {
|
||||
return getCertificateSHA1Fingerprint(app).equals(GITHUB_APK_SHA1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Disposable checkNewVersion(@NonNull final App app) {
|
||||
private void checkNewVersion() throws IOException, ReCaptchaException {
|
||||
final App app = App.getApp();
|
||||
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
|
||||
final NewVersionManager manager = new NewVersionManager();
|
||||
|
||||
// Check if user has enabled/disabled update checking
|
||||
// and if the current apk is a github one or not.
|
||||
if (!prefs.getBoolean(app.getString(R.string.update_app_key), true) || !isGithubApk(app)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the last request has happened a certain time ago
|
||||
// to reduce the number of API requests.
|
||||
final long expiry = prefs.getLong(app.getString(R.string.update_expiry_key), 0);
|
||||
if (!manager.isExpired(expiry)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
return Maybe
|
||||
.fromCallable(() -> {
|
||||
if (!isConnected(app)) {
|
||||
return null;
|
||||
}
|
||||
// Make a network request to get latest NewPipe data.
|
||||
final Response response = DownloaderImpl.getInstance().get(NEWPIPE_API_URL);
|
||||
handleResponse(response, manager, prefs, app);
|
||||
}
|
||||
|
||||
// Make a network request to get latest NewPipe data.
|
||||
return DownloaderImpl.getInstance().get(NEWPIPE_API_URL);
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
response -> {
|
||||
try {
|
||||
// Store a timestamp which needs to be exceeded,
|
||||
// before a new request to the API is made.
|
||||
final long newExpiry = manager
|
||||
.coerceExpiry(response.getHeader("expires"));
|
||||
prefs.edit()
|
||||
.putLong(app.getString(R.string.update_expiry_key), newExpiry)
|
||||
.apply();
|
||||
} catch (final Exception e) {
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not extract and save new expiry date", e);
|
||||
}
|
||||
}
|
||||
private void handleResponse(@NonNull final Response response,
|
||||
@NonNull final NewVersionManager manager,
|
||||
@NonNull final SharedPreferences prefs,
|
||||
@NonNull final App app) {
|
||||
try {
|
||||
// Store a timestamp which needs to be exceeded,
|
||||
// before a new request to the API is made.
|
||||
final long newExpiry = manager
|
||||
.coerceExpiry(response.getHeader("expires"));
|
||||
prefs.edit()
|
||||
.putLong(app.getString(R.string.update_expiry_key), newExpiry)
|
||||
.apply();
|
||||
} catch (final Exception e) {
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not extract and save new expiry date", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the json from the response.
|
||||
try {
|
||||
final JsonObject githubStableObject = JsonParser.object()
|
||||
.from(response.responseBody()).getObject("flavors")
|
||||
.getObject("github").getObject("stable");
|
||||
// Parse the json from the response.
|
||||
try {
|
||||
final JsonObject githubStableObject = JsonParser.object()
|
||||
.from(response.responseBody()).getObject("flavors")
|
||||
.getObject("github").getObject("stable");
|
||||
|
||||
final String versionName = githubStableObject
|
||||
.getString("version");
|
||||
final int versionCode = githubStableObject
|
||||
.getInt("version_code");
|
||||
final String apkLocationUrl = githubStableObject
|
||||
.getString("apk");
|
||||
final String versionName = githubStableObject
|
||||
.getString("version");
|
||||
final int versionCode = githubStableObject
|
||||
.getInt("version_code");
|
||||
final String apkLocationUrl = githubStableObject
|
||||
.getString("apk");
|
||||
|
||||
compareAppVersionAndShowNotification(app, versionName,
|
||||
apkLocationUrl, versionCode);
|
||||
} catch (final JsonParserException e) {
|
||||
// Most likely something is wrong in data received from NEWPIPE_API_URL.
|
||||
// Do not alarm user and fail silently.
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not get NewPipe API: invalid json", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new service which
|
||||
* checks if all conditions for performing a version check are met,
|
||||
* fetches the API endpoint {@link #NEWPIPE_API_URL} containing info
|
||||
* about the latest NewPipe version
|
||||
* and displays a notification about ana available update.
|
||||
* <br>
|
||||
* Following conditions need to be met, before data is request from the server:
|
||||
* <ul>
|
||||
* <li> The app is signed with the correct signing key (by TeamNewPipe / schabi).
|
||||
* If the signing key differs from the one used upstream, the update cannot be installed.</li>
|
||||
* <li>The user enabled searching for and notifying about updates in the settings.</li>
|
||||
* <li>The app did not recently check for updates.
|
||||
* We do not want to make unnecessary connections and DOS our servers.</li>
|
||||
* </ul>
|
||||
* <b>Must not be executed</b> when the app is in background.
|
||||
*/
|
||||
public static void startNewVersionCheckService() {
|
||||
final Intent intent = new Intent(App.getApp().getApplicationContext(),
|
||||
CheckForNewAppVersion.class);
|
||||
App.getApp().startService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(@Nullable final Intent intent) {
|
||||
try {
|
||||
checkNewVersion();
|
||||
} catch (final IOException e) {
|
||||
Log.w(TAG, "Could not fetch NewPipe API: probably network problem", e);
|
||||
} catch (final ReCaptchaException e) {
|
||||
Log.e(TAG, "ReCaptchaException should never happen here.", e);
|
||||
}
|
||||
|
||||
compareAppVersionAndShowNotification(app, versionName,
|
||||
apkLocationUrl, versionCode);
|
||||
} catch (final JsonParserException e) {
|
||||
// connectivity problems, do not alarm user and fail silently
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not get NewPipe API: invalid json", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
e -> {
|
||||
// connectivity problems, do not alarm user and fail silently
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "Could not get NewPipe API: network problem", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,9 @@
|
||||
|
||||
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.Context;
|
||||
import android.content.Intent;
|
||||
@@ -91,8 +94,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private static final String TAG = "MainActivity";
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@@ -165,6 +166,15 @@ public class MainActivity extends AppCompatActivity {
|
||||
openMiniPlayerUponPlayerStarted();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(final Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
// 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 Exception {
|
||||
//Tabs
|
||||
final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
|
||||
|
@@ -77,6 +77,7 @@ import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.local.dialog.PlaylistCreationDialog;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.player.MainPlayer;
|
||||
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
||||
import org.schabi.newpipe.player.Player;
|
||||
import org.schabi.newpipe.player.event.OnKeyDownListener;
|
||||
import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener;
|
||||
@@ -1095,8 +1096,8 @@ public final class VideoDetailFragment
|
||||
toggleFullscreenIfInFullscreenMode();
|
||||
|
||||
final PlayQueue queue = setupPlayQueueForIntent(append);
|
||||
if (append) {
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, queue, false);
|
||||
if (append) { //resumePlayback: false
|
||||
NavigationHelper.enqueueOnPlayer(activity, queue, PlayerType.POPUP);
|
||||
} else {
|
||||
replaceQueueIfUserConfirms(() -> NavigationHelper
|
||||
.playOnPopupPlayer(activity, queue, true));
|
||||
@@ -1154,7 +1155,7 @@ public final class VideoDetailFragment
|
||||
|
||||
final PlayQueue queue = setupPlayQueueForIntent(append);
|
||||
if (append) {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, queue, false);
|
||||
NavigationHelper.enqueueOnPlayer(activity, queue, PlayerType.AUDIO);
|
||||
} else {
|
||||
replaceQueueIfUserConfirms(() -> NavigationHelper
|
||||
.playOnBackgroundPlayer(activity, queue, true));
|
||||
|
@@ -350,12 +350,16 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
||||
if (context == null || context.getResources() == null || activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<StreamDialogEntry> entries = new ArrayList<>();
|
||||
|
||||
if (PlayerHolder.getInstance().getType() != null) {
|
||||
if (PlayerHolder.getInstance().isPlayerOpen()) {
|
||||
entries.add(StreamDialogEntry.enqueue);
|
||||
|
||||
if (PlayerHolder.getInstance().getQueueSize() > 1) {
|
||||
entries.add(StreamDialogEntry.enqueue_next);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||
entries.addAll(Arrays.asList(
|
||||
StreamDialogEntry.start_here_on_background,
|
||||
|
@@ -37,6 +37,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.ktx.AnimationType;
|
||||
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.PlayQueue;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
@@ -495,12 +496,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
||||
.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
|
||||
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.POPUP);
|
||||
return true;
|
||||
});
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
|
||||
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.AUDIO);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
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.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||
@@ -148,9 +149,14 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
|
||||
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
||||
|
||||
if (PlayerHolder.getInstance().getType() != null) {
|
||||
if (PlayerHolder.getInstance().isPlayerOpen()) {
|
||||
entries.add(StreamDialogEntry.enqueue);
|
||||
|
||||
if (PlayerHolder.getInstance().getQueueSize() > 1) {
|
||||
entries.add(StreamDialogEntry.enqueue_next);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||
entries.addAll(Arrays.asList(
|
||||
StreamDialogEntry.start_here_on_background,
|
||||
@@ -347,12 +353,12 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
|
||||
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.POPUP);
|
||||
return true;
|
||||
});
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
|
||||
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.AUDIO);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
@@ -1,8 +1,7 @@
|
||||
package org.schabi.newpipe.info_list
|
||||
|
||||
import android.util.Log
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo
|
||||
import kotlin.math.max
|
||||
|
||||
@@ -11,7 +10,7 @@ import kotlin.math.max
|
||||
*/
|
||||
class StreamSegmentAdapter(
|
||||
private val listener: StreamSegmentListener
|
||||
) : GroupAdapter<GroupieViewHolder>() {
|
||||
) : GroupieAdapter() {
|
||||
|
||||
var currentIndex: Int = 0
|
||||
private set
|
||||
|
@@ -40,8 +40,7 @@ import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import com.xwray.groupie.Item
|
||||
import com.xwray.groupie.OnItemClickListener
|
||||
import com.xwray.groupie.OnItemLongClickListener
|
||||
@@ -91,7 +90,7 @@ class FeedFragment : BaseStateFragment<FeedState>() {
|
||||
private var groupName = ""
|
||||
private var oldestSubscriptionUpdate: OffsetDateTime? = null
|
||||
|
||||
private lateinit var groupAdapter: GroupAdapter<GroupieViewHolder>
|
||||
private lateinit var groupAdapter: GroupieAdapter
|
||||
@State @JvmField var showPlayedItems: Boolean = true
|
||||
|
||||
private var onSettingsChangeListener: SharedPreferences.OnSharedPreferenceChangeListener? = null
|
||||
@@ -131,7 +130,7 @@ class FeedFragment : BaseStateFragment<FeedState>() {
|
||||
viewModel = ViewModelProvider(this, factory).get(FeedViewModel::class.java)
|
||||
viewModel.stateLiveData.observe(viewLifecycleOwner, { it?.let(::handleResult) })
|
||||
|
||||
groupAdapter = GroupAdapter<GroupieViewHolder>().apply {
|
||||
groupAdapter = GroupieAdapter().apply {
|
||||
setOnItemClickListener(listenerStreamItem)
|
||||
setOnItemLongClickListener(listenerStreamItem)
|
||||
}
|
||||
@@ -326,9 +325,14 @@ class FeedFragment : BaseStateFragment<FeedState>() {
|
||||
if (context == null || context.resources == null || activity == null) return
|
||||
|
||||
val entries = ArrayList<StreamDialogEntry>()
|
||||
if (PlayerHolder.getInstance().getType() != null) {
|
||||
if (PlayerHolder.getInstance().isPlayerOpen) {
|
||||
entries.add(StreamDialogEntry.enqueue)
|
||||
|
||||
if (PlayerHolder.getInstance().queueSize > 1) {
|
||||
entries.add(StreamDialogEntry.enqueue_next)
|
||||
}
|
||||
}
|
||||
|
||||
if (item.streamType == StreamType.AUDIO_STREAM) {
|
||||
entries.addAll(
|
||||
listOf(
|
||||
|
@@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.stream.StreamType.LIVE_STREAM
|
||||
import org.schabi.newpipe.extractor.stream.StreamType.VIDEO_STREAM
|
||||
import org.schabi.newpipe.util.Localization
|
||||
import org.schabi.newpipe.util.PicassoHelper
|
||||
import org.schabi.newpipe.util.StreamTypeUtil
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
data class StreamItem(
|
||||
@@ -58,8 +59,6 @@ data class StreamItem(
|
||||
viewBinding.itemVideoTitleView.text = stream.title
|
||||
viewBinding.itemUploaderView.text = stream.uploader
|
||||
|
||||
val isLiveStream = stream.streamType == LIVE_STREAM || stream.streamType == AUDIO_LIVE_STREAM
|
||||
|
||||
if (stream.duration > 0) {
|
||||
viewBinding.itemDurationView.text = Localization.getDurationString(stream.duration)
|
||||
viewBinding.itemDurationView.setBackgroundColor(
|
||||
@@ -77,7 +76,7 @@ data class StreamItem(
|
||||
} else {
|
||||
viewBinding.itemProgressView.visibility = View.GONE
|
||||
}
|
||||
} else if (isLiveStream) {
|
||||
} else if (StreamTypeUtil.isLiveStream(stream.streamType)) {
|
||||
viewBinding.itemDurationView.setText(R.string.duration_live)
|
||||
viewBinding.itemDurationView.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
|
@@ -338,9 +338,14 @@ public class StatisticsPlaylistFragment
|
||||
|
||||
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
||||
|
||||
if (PlayerHolder.getInstance().getType() != null) {
|
||||
if (PlayerHolder.getInstance().isPlayerOpen()) {
|
||||
entries.add(StreamDialogEntry.enqueue);
|
||||
|
||||
if (PlayerHolder.getInstance().getQueueSize() > 1) {
|
||||
entries.add(StreamDialogEntry.enqueue_next);
|
||||
}
|
||||
}
|
||||
|
||||
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||
entries.addAll(Arrays.asList(
|
||||
StreamDialogEntry.start_here_on_background,
|
||||
|
@@ -42,6 +42,7 @@ import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
||||
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
@@ -493,12 +494,12 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayPopupButton.setOnLongClickListener(view -> {
|
||||
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
|
||||
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.POPUP);
|
||||
return true;
|
||||
});
|
||||
|
||||
playlistControlBinding.playlistCtrlPlayBgButton.setOnLongClickListener(view -> {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
|
||||
NavigationHelper.enqueueOnPlayer(activity, getPlayQueue(), PlayerType.AUDIO);
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -752,8 +753,12 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||
|
||||
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
||||
|
||||
if (PlayerHolder.getInstance().getType() != null) {
|
||||
if (PlayerHolder.getInstance().isPlayerOpen()) {
|
||||
entries.add(StreamDialogEntry.enqueue);
|
||||
|
||||
if (PlayerHolder.getInstance().getQueueSize() > 1) {
|
||||
entries.add(StreamDialogEntry.enqueue_next);
|
||||
}
|
||||
}
|
||||
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||
entries.addAll(Arrays.asList(
|
||||
|
@@ -22,7 +22,7 @@ enum class FeedGroupIcon(
|
||||
COMPUTER(5, R.drawable.ic_computer),
|
||||
GAMING(6, R.drawable.ic_videogame_asset),
|
||||
SPORTS(7, R.drawable.ic_directions_bike),
|
||||
NEWS(8, R.drawable.ic_megaphone),
|
||||
NEWS(8, R.drawable.ic_campaign),
|
||||
FAVORITES(9, R.drawable.ic_favorite),
|
||||
CAR(10, R.drawable.ic_directions_car),
|
||||
MOTORCYCLE(11, R.drawable.ic_motorcycle),
|
||||
|
@@ -21,8 +21,7 @@ import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import com.xwray.groupie.OnItemClickListener
|
||||
import com.xwray.groupie.Section
|
||||
import icepick.Icepick
|
||||
@@ -78,7 +77,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||
|
||||
private val subscriptionMainSection = Section()
|
||||
private val subscriptionEmptyFooter = Section()
|
||||
private lateinit var subscriptionGroupAdapter: GroupAdapter<GroupieViewHolder>
|
||||
private lateinit var subscriptionGroupAdapter: GroupieAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -153,7 +152,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||
}
|
||||
}
|
||||
|
||||
subscriptionGroupAdapter = GroupAdapter<GroupieViewHolder>().apply {
|
||||
subscriptionGroupAdapter = GroupieAdapter().apply {
|
||||
add(subscriptionMainSection)
|
||||
add(subscriptionEmptyFooter)
|
||||
spanCount = 4
|
||||
@@ -379,7 +378,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||
}
|
||||
|
||||
private fun setupIconPicker() {
|
||||
val groupAdapter = GroupAdapter<GroupieViewHolder>()
|
||||
val groupAdapter = GroupieAdapter()
|
||||
groupAdapter.addAll(FeedGroupIcon.values().map { PickerIconItem(it) })
|
||||
|
||||
feedGroupCreateBinding.iconSelector.apply {
|
||||
|
@@ -11,8 +11,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.GroupieAdapter
|
||||
import com.xwray.groupie.TouchCallback
|
||||
import icepick.Icepick
|
||||
import icepick.State
|
||||
@@ -38,7 +37,7 @@ class FeedGroupReorderDialog : DialogFragment() {
|
||||
@State
|
||||
@JvmField
|
||||
var groupOrderedIdList = ArrayList<Long>()
|
||||
private val groupAdapter = GroupAdapter<GroupieViewHolder>()
|
||||
private val groupAdapter = GroupieAdapter()
|
||||
private val itemTouchHelper = ItemTouchHelper(getItemTouchCallback())
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,8 @@ import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
||||
import org.schabi.newpipe.player.mediasession.PlayQueueNavigator;
|
||||
import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class MediaSessionManager {
|
||||
private static final String TAG = MediaSessionManager.class.getSimpleName();
|
||||
public static final boolean DEBUG = MainActivity.DEBUG;
|
||||
@@ -30,6 +32,9 @@ public class MediaSessionManager {
|
||||
@NonNull
|
||||
private final MediaSessionConnector sessionConnector;
|
||||
|
||||
private int lastTitleHashCode;
|
||||
private int lastArtistHashCode;
|
||||
private long lastDuration;
|
||||
private int lastAlbumArtHashCode;
|
||||
|
||||
public MediaSessionManager(@NonNull final Context context,
|
||||
@@ -65,47 +70,127 @@ public class MediaSessionManager {
|
||||
return mediaSession.getSessionToken();
|
||||
}
|
||||
|
||||
public void setMetadata(final String title,
|
||||
final String artist,
|
||||
final Bitmap albumArt,
|
||||
final long duration) {
|
||||
if (albumArt == null || !mediaSession.isActive()) {
|
||||
/**
|
||||
* sets the Metadata - if required.
|
||||
*
|
||||
* @param title {@link MediaMetadataCompat#METADATA_KEY_TITLE}
|
||||
* @param artist {@link MediaMetadataCompat#METADATA_KEY_ARTIST}
|
||||
* @param optAlbumArt {@link MediaMetadataCompat#METADATA_KEY_ALBUM_ART}
|
||||
* @param duration {@link MediaMetadataCompat#METADATA_KEY_DURATION}
|
||||
* - should be a negative value for unknown durations, e.g. for livestreams
|
||||
*/
|
||||
public void setMetadata(@NonNull final String title,
|
||||
@NonNull final String artist,
|
||||
@NonNull final Optional<Bitmap> optAlbumArt,
|
||||
final long duration
|
||||
) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setMetadata called:"
|
||||
+ " t: " + title
|
||||
+ " a: " + artist
|
||||
+ " thumb: " + (
|
||||
optAlbumArt.isPresent()
|
||||
? optAlbumArt.get().hashCode()
|
||||
: "<none>")
|
||||
+ " d: " + duration);
|
||||
}
|
||||
|
||||
if (!mediaSession.isActive()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setMetadata: mediaSession not active - exiting");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkIfMetadataShouldBeSet(title, artist, optAlbumArt, duration)) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setMetadata: No update required - exiting");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
if (getMetadataAlbumArt() == null) {
|
||||
Log.d(TAG, "N_getMetadataAlbumArt: thumb == null");
|
||||
}
|
||||
if (getMetadataTitle() == null) {
|
||||
Log.d(TAG, "N_getMetadataTitle: title == null");
|
||||
}
|
||||
if (getMetadataArtist() == null) {
|
||||
Log.d(TAG, "N_getMetadataArtist: artist == null");
|
||||
}
|
||||
if (getMetadataDuration() <= 1) {
|
||||
Log.d(TAG, "N_getMetadataDuration: duration <= 1; " + getMetadataDuration());
|
||||
}
|
||||
Log.d(TAG, "setMetadata: N_Metadata update:"
|
||||
+ " t: " + title
|
||||
+ " a: " + artist
|
||||
+ " thumb: " + (
|
||||
optAlbumArt.isPresent()
|
||||
? optAlbumArt.get().hashCode()
|
||||
: "<none>")
|
||||
+ " d: " + duration);
|
||||
}
|
||||
|
||||
if (getMetadataAlbumArt() == null || getMetadataTitle() == null
|
||||
|| getMetadataArtist() == null || getMetadataDuration() <= 1
|
||||
|| albumArt.hashCode() != lastAlbumArtHashCode) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setMetadata: N_Metadata update: t: " + title + " a: " + artist
|
||||
+ " thumb: " + albumArt.hashCode() + " d: " + duration);
|
||||
}
|
||||
final MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder()
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration);
|
||||
|
||||
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
|
||||
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt)
|
||||
.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, albumArt)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration).build());
|
||||
lastAlbumArtHashCode = albumArt.hashCode();
|
||||
if (optAlbumArt.isPresent()) {
|
||||
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, optAlbumArt.get());
|
||||
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, optAlbumArt.get());
|
||||
}
|
||||
|
||||
mediaSession.setMetadata(builder.build());
|
||||
|
||||
lastTitleHashCode = title.hashCode();
|
||||
lastArtistHashCode = artist.hashCode();
|
||||
lastDuration = duration;
|
||||
if (optAlbumArt.isPresent()) {
|
||||
lastAlbumArtHashCode = optAlbumArt.get().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkIfMetadataShouldBeSet(
|
||||
@NonNull final String title,
|
||||
@NonNull final String artist,
|
||||
@NonNull final Optional<Bitmap> optAlbumArt,
|
||||
final long duration
|
||||
) {
|
||||
// Check if the values have changed since the last time
|
||||
if (title.hashCode() != lastTitleHashCode
|
||||
|| artist.hashCode() != lastArtistHashCode
|
||||
|| duration != lastDuration
|
||||
|| (optAlbumArt.isPresent() && optAlbumArt.get().hashCode() != lastAlbumArtHashCode)
|
||||
) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG,
|
||||
"checkIfMetadataShouldBeSet: true - reason: changed values since last");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the currently set metadata is valid
|
||||
if (getMetadataTitle() == null
|
||||
|| getMetadataArtist() == null
|
||||
// Note that the duration can be <= 0 for live streams
|
||||
) {
|
||||
if (DEBUG) {
|
||||
if (getMetadataTitle() == null) {
|
||||
Log.d(TAG,
|
||||
"N_getMetadataTitle: title == null");
|
||||
} else if (getMetadataArtist() == null) {
|
||||
Log.d(TAG,
|
||||
"N_getMetadataArtist: artist == null");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we got an album art check if the current set AlbumArt is null
|
||||
if (optAlbumArt.isPresent() && getMetadataAlbumArt() == null) {
|
||||
if (DEBUG) {
|
||||
if (getMetadataAlbumArt() == null) {
|
||||
Log.d(TAG, "N_getMetadataAlbumArt: thumb == null");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Default - no update required
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private Bitmap getMetadataAlbumArt() {
|
||||
return mediaSession.getController().getMetadata()
|
||||
.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
|
||||
@@ -121,11 +206,6 @@ public class MediaSessionManager {
|
||||
.getString(MediaMetadataCompat.METADATA_KEY_ARTIST);
|
||||
}
|
||||
|
||||
private long getMetadataDuration() {
|
||||
return mediaSession.getController().getMetadata()
|
||||
.getLong(MediaMetadataCompat.METADATA_KEY_DURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called on player destruction to prevent leakage.
|
||||
*/
|
||||
|
@@ -70,6 +70,10 @@ public final class PlayerHolder {
|
||||
return player != null;
|
||||
}
|
||||
|
||||
public int getQueueSize() {
|
||||
return isPlayerOpen() ? player.getPlayQueue().size() : 0;
|
||||
}
|
||||
|
||||
public void setListener(@Nullable final PlayerServiceExtendedEventListener newListener) {
|
||||
listener = newListener;
|
||||
|
||||
|
@@ -14,6 +14,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
||||
import org.schabi.newpipe.util.StreamTypeUtil;
|
||||
|
||||
public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
|
||||
|
||||
@@ -21,7 +22,7 @@ public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
|
||||
default MediaSource maybeBuildLiveMediaSource(@NonNull final PlayerDataSource dataSource,
|
||||
@NonNull final StreamInfo info) {
|
||||
final StreamType streamType = info.getStreamType();
|
||||
if (!(streamType == StreamType.AUDIO_LIVE_STREAM || streamType == StreamType.LIVE_STREAM)) {
|
||||
if (!StreamTypeUtil.isLiveStream(streamType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@@ -91,6 +91,11 @@ class ContentSettingsManager(private val fileLocator: NewPipeFileLocator) {
|
||||
is String -> {
|
||||
preferenceEditor.putString(key, value)
|
||||
}
|
||||
is Set<*> -> {
|
||||
// There are currently only Sets with type String possible
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
preferenceEditor.putStringSet(key, value as Set<String>?)
|
||||
}
|
||||
}
|
||||
}
|
||||
preferenceEditor.commit()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user