diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..82025c3c5
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,112 @@
+name: CI
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - legacy
+ paths-ignore:
+ - 'README.md'
+ - 'fastlane/**'
+ - 'assets/**'
+ - '.github/**/*.md'
+ - '.github/FUNDING.yml'
+ - '.github/ISSUE_TEMPLATE/**'
+ push:
+ branches:
+ - legacy
+ paths-ignore:
+ - 'README.md'
+ - 'fastlane/**'
+ - 'assets/**'
+ - '.github/**/*.md'
+ - '.github/FUNDING.yml'
+ - '.github/ISSUE_TEMPLATE/**'
+
+jobs:
+ build-and-test-jvm:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - uses: gradle/wrapper-validation-action@v1
+
+ - name: create and checkout branch
+ # push events already checked out the branch
+ if: github.event_name == 'pull_request'
+ run: git checkout -B ${{ github.head_ref }}
+
+ - name: set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: 11
+ distribution: "temurin"
+ cache: 'gradle'
+
+ - name: Build debug APK
+ run: ./gradlew assembleDebug lintDebug --stacktrace -DskipFormatKtlint
+
+ - name: Upload APK
+ uses: actions/upload-artifact@v3
+ with:
+ name: app
+ path: app/build/outputs/apk/debug/*.apk
+
+# test-android:
+# # macos has hardware acceleration. See android-emulator-runner action
+# runs-on: macos-latest
+# timeout-minutes: 20
+# strategy:
+# matrix:
+# # api-level 16 is min sdk, but throws errors related to desugaring
+# api-level: [ 21, 29 ]
+# steps:
+# - uses: actions/checkout@v3
+#
+# - name: set up JDK 11
+# uses: actions/setup-java@v3
+# with:
+# java-version: 11
+# distribution: "temurin"
+# cache: 'gradle'
+#
+# - name: Run android tests
+# uses: reactivecircus/android-emulator-runner@v2
+# with:
+# api-level: ${{ matrix.api-level }}
+# # workaround to emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160
+# emulator-build: 7425822
+# script: ./gradlew connectedCheck
+#
+# sonar:
+# runs-on: ubuntu-latest
+# steps:
+# - uses: actions/checkout@v3
+# with:
+# fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
+
+# - name: Set up JDK 11
+# uses: actions/setup-java@v3
+# with:
+# java-version: 11 # Sonar requires JDK 11
+# distribution: "temurin"
+
+# - name: Cache SonarCloud packages
+# uses: actions/cache@v3
+# with:
+# path: ~/.sonar/cache
+# key: ${{ runner.os }}-sonar
+# restore-keys: ${{ runner.os }}-sonar
+
+# - name: Cache Gradle packages
+# uses: actions/cache@v3
+# with:
+# path: ~/.gradle/caches
+# key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
+# restore-keys: ${{ runner.os }}-gradle
+
+# - name: Build and analyze
+# env:
+# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
+# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+# run: ./gradlew build sonarqube --info
diff --git a/.github/workflows/release-legacy.yml b/.github/workflows/release-legacy.yml
new file mode 100644
index 000000000..b6f5f2acf
--- /dev/null
+++ b/.github/workflows/release-legacy.yml
@@ -0,0 +1,40 @@
+name: release legacy
+
+on:
+ push:
+ tags:
+ - "v*-legacy"
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: '0'
+
+ - uses: actions/setup-java@v1
+ with:
+ java-version: '8'
+
+ - name: "Build release apk"
+ run: ./gradlew assembleRelease --stacktrace
+
+ - name: "Sign release"
+ uses: r0adkll/sign-android-release@v1
+ id: sign_app
+ with:
+ releaseDirectory: app/build/outputs/apk/release
+ signingKeyBase64: ${{ secrets.SIGNING_KEY }}
+ alias: ${{ secrets.ALIAS }}
+ keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
+ keyPassword: ${{ secrets.KEY_PASSWORD }}
+
+ - name: "Rename archive"
+ run: mv ${{ steps.sign_app.outputs.signedReleaseFile }} app-release.apk
+
+ - name: "Create GitHub release"
+ uses: softprops/action-gh-release@v1
+ with:
+ files: app-release.apk
+ fail_on_unmatched_files: true
\ No newline at end of file
diff --git a/README.md b/README.md
index 219cda826..db2866755 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,18 @@
-# FoxPipe
-
-A NewPipe fork with the old UI having separate Players handling 3 seperate Queues
-
-FoxPipe
-A libre lightweight streaming frontend for Android.
+
+NewPipe_Legacy_Revo
+NewPipeLegacy preunified & unified version with working NewPipeExtractor dependency.
-
+
-
+
+
+
+
-Screenshots • Description • Features • Installation and updates • Contribution • License
-Website • FAQ • Press
+Screenshots • Description • Features • Installation and updates • Contribution • Donate • License
+Website • Blog • FAQ • Press
WARNING: THIS IS A BETA VERSION, THEREFORE YOU MAY ENCOUNTER BUGS. IF YOU DO, OPEN AN ISSUE VIA OUR GITHUB REPOSITORY.
@@ -26,21 +26,24 @@ A NewPipe fork with the old UI having separate Players handling 3 seperate Queue
[ ](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png)
[ ](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png)
[ ](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png)
+[ ](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png)
[ ](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png)
[ ](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png)
[ ](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png)
[ ](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png)
-
## Description
-This repository contains old preunified version 0.19.8 of [NewPipe](https://github.com/TeamNewPipe/NewPipe/releases/tag/v0.19.8) with up-to-date [NewPipeExtractor](https://github.com/ShareASmile/NewPipeExtractor/tree/ify) dependency.
+ This repository contains updated preunified [NewPipeLegacy-v0.19.8](https://github.com/TeamNewPipe/NewPipe-legacy/releases/tag/v0.19.8) & revived unified v0.21.2 of [tossj fork of NewPipeLegacy](https://github.com/tossj/NewPipe-legacy/tree/update-newpipe-extractor-0.21.1) with updated [NewPipeExtractor for Legacy Devices](https://github.com/ShareASmile/NewPipeExtractor) dependency.
+
+The application itself heavily relies on the extractor component which is responsible for proper parsing of various video/audio streams, including Youtube site. The old NewPipe Legacy version 0.19.8 depends on old extractor version which is practically deprecated and can't handle current Youtube (and similar?) streams, thus rendering the application useless for daily use.
+
+NewPipe Legacy version 0.19.8+ in this repository uses the updated version of NewPipeExtractor for legacy devices and resolves the forementioned issue, thus making it possible to use old NewPipe Legacy updated version 0.19.8 with bug fixes, features & support for SoundCloud, Bandcamp, media.ccc.de sites added for legacy devices along with updated extractor version. However v0.21.2+ is also available with Unified Player in toss branch. APK's for legacy devices on Ice Cream Sandwich (Android 4.0.1 – 4.0.4) has been available from Releases, but it has a very basic support, can be buggy.
-The application itself heavily relies on the extractor component which is responsible for proper parsing of various video/audio streams, including Youtube site. The old NewPipe version 0.19.8 depends on old extractor version which is practically deprecated and can't handle current Youtube (and similar?) streams,thus rendering the application useless for daily use.
-FoxPipe in this repository uses the updated version of NewPipeExtractor for legacy devices and resolves the forementioned issue, thus making it possible to use old NewPipe version 0.19.8 based FoxPipe with some bug fixes & features added along with updated extractor version. You don't need a YouTube account to use NewPipe, it is a copylefted libre software.
+You don't need a YouTube account to use NewPipe, it is a copylefted libre software.
## Motivation
-Not so long ago, NewPipe project implemented a new UI elements for video streams. Personally, I didn't like that change. I wanted to keep using the old UI having separate Players(Video, Popup & Background) handling three seperate Queues simultaneously instead.
+Not so long ago, NewPipe project implemented a new UI elements for video streams. Personally, I didn't like that change. I wanted to keep using the old UI instead.
### Features
@@ -50,7 +53,7 @@ Not so long ago, NewPipe project implemented a new UI elements for video streams
* Listen to YouTube videos
* Popup mode (floating player)
* Select streaming player to watch video with
-* Make Personalised Watch Later Playlists
+* Make Personalised Playlists Locally on device
* Open a video in Kodi
* Show next/related videos
* Search YouTube in a specific language
@@ -85,11 +88,10 @@ 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. Download the APK from [Github Releases](https://github.com/ShareASmile/FoxPipe/releases) and install it.
- 2. Build a debug APK yourself or install from [actions](https://github.com/ShareASmile/FoxPipe/actions) This is the fastest way to get new features on your device.
+You can install NewPipe Legacy Revo using one of the following methods:
-We recommend method 1 for most users. 2. Building a debug APK using method 2 excludes a key entirely. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app.
+ 1. Download the APK from [Github Releases](https://github.com/ShareASmile/NewPipe-Legacy-Revo/releases) and install it.
+ 2. 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.
## Contribution
Whether you have ideas, translations, design changes, code cleaning, or real heavy code changes, help is always welcome.
@@ -97,11 +99,35 @@ The more is done the better it gets!
If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md).
+
+
+
+
+## Donate
+If you like NewPipe we'd be happy about a donation. You can either send bitcoin or donate via Bountysource or Liberapay. For further info on donating to NewPipe, please visit our [upstream](https://newpipe.net/donate).
+
+
+
+
+
+ 16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh
+
+
+
+
+
+
+
+
+
+
+
+
## Privacy Policy
-The FoxPipe project aims to provide a private, anonymous experience for using media web services.
-Therefore, the app does not collect any data without your consent.
+The NewPipe project aims to provide a private, anonymous experience for using media web services.
+Therefore, the app does not collect any data without your consent. NewPipe's privacy policy explains in detail what data is sent and stored when you send a crash report, or comment in our blog. You can find the document [here](https://newpipe.net/legal/privacy/).
## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](https://www.gnu.org/licenses/gpl-3.0.en.html)
@@ -110,4 +136,4 @@ NewPipe is Free Software: You can use, study share and improve it at your
will. Specifically you can redistribute and/or modify it under the terms of the
[GNU General Public License](https://www.gnu.org/licenses/gpl.html) as
published by the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
\ No newline at end of file
+(at your option) any later version.
diff --git a/app/build.gradle b/app/build.gradle
index 7827edd23..2bb1b2402 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,19 +2,21 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
-apply plugin: 'checkstyle'
android {
compileSdkVersion 29
- buildToolsVersion '29.0.3'
+ buildToolsVersion '30.0.3'
defaultConfig {
applicationId 'org.schabi.newpipelegacy'
resValue "string", "app_name", "NewPipe Legacy"
minSdkVersion 16
+ //noinspection ExpiredTargetSdkVersion
targetSdkVersion 29
- versionCode 90
- versionName "0.19.8"
+ versionCode 110
+ versionName "0.19.9.4"
+
+ multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -28,12 +30,11 @@ android {
buildTypes {
debug {
- multiDexEnabled true
debuggable true
// suffix the app id and the app name with git branch name
def workingBranch = getGitWorkingBranch()
- def normalizedWorkingBranch = workingBranch.replaceAll("[^A-Za-z]+", "").toLowerCase()
+ def normalizedWorkingBranch = workingBranch.replaceFirst("^[^A-Za-z]+", "").replaceAll("[^0-9A-Za-z]+", "")
if (normalizedWorkingBranch.isEmpty() || workingBranch == "master" || workingBranch == "dev") {
// default values when branch name could not be determined or is master or dev
applicationIdSuffix ".debug"
@@ -50,7 +51,7 @@ android {
// TODO: update Gradle version
release {
minifyEnabled true
- shrinkResources false // disabled to fix F-Droid's reproducible build
+ shrinkResources true // could be disabled to fix F-Droid's reproducible build
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
archivesBaseName = 'app'
}
@@ -61,14 +62,24 @@ android {
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
+ // suppress false warning ("Resource IDs will be non-final in Android Gradle Plugin version
+ // 5.0, avoid using them in switch case statements"), which affects only library projects
+ disable 'NonConstantResourceId'
}
compileOptions {
+ // Flag to enable support for the new language APIs
+ coreLibraryDesugaringEnabled true
+
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
encoding 'utf-8'
}
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_1_8
+ }
+
// Required and used only by groupie
androidExtensions {
experimental = true
@@ -81,132 +92,146 @@ android {
ext {
icepickVersion = '3.2.0'
- checkstyleVersion = '8.32'
stethoVersion = '1.5.1'
- leakCanaryVersion = '2.2'
- exoPlayerVersion = '2.11.6'
+ leakCanaryVersion = '2.5'
+ exoPlayerVersion = '2.11.8'
androidxLifecycleVersion = '2.2.0'
androidxRoomVersion = '2.2.5'
- groupieVersion = '2.8.0'
- markwonVersion = '4.3.1'
+ groupieVersion = '2.8.1'
+ markwonVersion = '4.6.0'
+ googleAutoServiceVersion = '1.0-rc7'
}
configurations {
- checkstyle
ktlint
}
-checkstyle {
- configFile rootProject.file('checkstyle.xml')
- ignoreFailures false
- showViolations true
- toolVersion = checkstyleVersion
-}
-
-task runCheckstyle(type: Checkstyle) {
- source 'src'
- include '**/*.java'
- exclude '**/gen/**'
- exclude '**/R.java'
- exclude '**/BuildConfig.java'
- exclude 'main/java/us/shandian/giga/**'
-
- classpath = configurations.checkstyle
-
- showViolations true
-
- reports {
- xml.enabled true
- html.enabled true
- }
-}
+def outputDir = "${project.buildDir}/reports/ktlint/"
+def inputFiles = project.fileTree(dir: "src", include: "**/*.kt")
task runKtlint(type: JavaExec) {
+ inputs.files(inputFiles)
+ outputs.dir(outputDir)
main = "com.pinterest.ktlint.Main"
classpath = configurations.ktlint
args "src/**/*.kt"
}
task formatKtlint(type: JavaExec) {
+ inputs.files(inputFiles)
+ outputs.dir(outputDir)
main = "com.pinterest.ktlint.Main"
classpath = configurations.ktlint
args "-F", "src/**/*.kt"
}
afterEvaluate {
- preDebugBuild.dependsOn runCheckstyle, runKtlint
+ if (!System.properties.containsKey('skipFormatKtlint')) {
+ preDebugBuild.dependsOn formatKtlint
+ }
+ preDebugBuild.dependsOn runKtlint
}
dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-
- implementation "frankiesardo:icepick:${icepickVersion}"
- kapt "frankiesardo:icepick-processor:${icepickVersion}"
+/** Desugaring **/
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
- checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
- ktlint "com.pinterest:ktlint:0.35.0"
-
- debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
- debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}"
-
- debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}"
- implementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
-
- debugImplementation "androidx.multidex:multidex:2.0.1"
-
- testImplementation 'junit:junit:4.13'
- testImplementation 'org.mockito:mockito-core:3.3.3'
-
- androidTestImplementation "androidx.test.ext:junit:1.1.1"
- androidTestImplementation "androidx.room:room-testing:${androidxRoomVersion}"
- androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0", {
- exclude module: 'support-annotations'
- }
-
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:5ac80624a40f4c600ae493e66881b5bf008f0ddb'
-
- implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
- implementation "org.jsoup:jsoup:1.13.1"
+/** Ktlint **/
+ ktlint "com.pinterest:ktlint:0.40.0"
- implementation "com.squareup.okhttp3:okhttp:3.12.11"
+/** Kotlin **/
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation "com.google.android.exoplayer:exoplayer:${exoPlayerVersion}"
- implementation "com.google.android.exoplayer:extension-mediasession:${exoPlayerVersion}"
-
- implementation "com.google.android.material:material:1.1.0"
-
- implementation "androidx.appcompat:appcompat:1.1.0"
- implementation "androidx.preference:preference:1.1.1"
- implementation "androidx.recyclerview:recyclerview:1.1.0"
+/** AndroidX **/
+ implementation "androidx.appcompat:appcompat:1.1.0" //no change
implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
-
implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}"
implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}"
implementation "androidx.lifecycle:lifecycle-extensions:${androidxLifecycleVersion}"
-
+ implementation 'androidx.media:media:1.2.1'
+ implementation "androidx.multidex:multidex:2.0.1"
+ implementation "androidx.preference:preference:1.1.1"
+ implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.room:room-runtime:${androidxRoomVersion}"
implementation "androidx.room:room-rxjava2:${androidxRoomVersion}"
kapt "androidx.room:room-compiler:${androidxRoomVersion}"
+ implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
+ implementation "com.google.android.material:material:1.2.1"
+
+/** NewPipe libraries **/
+ // You can use a local version by uncommenting a few lines in settings.gradle
+ // Or you can use a commit you pushed to GitHub by just replacing TeamNewPipe with your GitHub
+ // 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.ShareASmile:NewPipeExtractor:4aea925d97'
+
+/** Third-party libraries **/
+ // Instance state boilerplate elimination
+ implementation "frankiesardo:icepick:${icepickVersion}"
+ kapt "frankiesardo:icepick-processor:${icepickVersion}"
+
+ // HTML parser
+ implementation "org.jsoup:jsoup:1.13.1"
+
+ // HTTP client
+ //noinspection GradleDependency --> do not update okhttp beyond 3.12.x to keep supporting Android 4.4 & Lower users
+ implementation "com.squareup.okhttp3:okhttp:3.12.13"
+ // Media player
+ implementation "com.google.android.exoplayer:exoplayer:${exoPlayerVersion}"
+ implementation "com.google.android.exoplayer:extension-mediasession:${exoPlayerVersion}"
+
+ // Metadata generator for service descriptors
+ compileOnly "com.google.auto.service:auto-service-annotations:${googleAutoServiceVersion}"
+ kapt "com.google.auto.service:auto-service:${googleAutoServiceVersion}"
+
+ // Manager for complex RecyclerView layouts
implementation "com.xwray:groupie:${groupieVersion}"
implementation "com.xwray:groupie-kotlin-android-extensions:${groupieVersion}"
+ // Circular ImageView
implementation "de.hdodenhof:circleimageview:3.1.0"
+ // Image loading
implementation "com.nostra13.universalimageloader:universal-image-loader:1.9.5"
+ // Markdown library for Android
implementation "io.noties.markwon:core:${markwonVersion}"
implementation "io.noties.markwon:linkify:${markwonVersion}"
+ // File picker
implementation "com.nononsenseapps:filepicker:4.2.1"
+ // Crash reporting
implementation "ch.acra:acra-core:5.5.0"
+ // Reactive extensions for Java VM
implementation "io.reactivex.rxjava2:rxjava:2.2.19"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
+ // RxJava binding APIs for Android UI widgets
implementation "com.jakewharton.rxbinding2:rxbinding:2.2.0"
- implementation "org.ocpsoft.prettytime:prettytime:4.0.5.Final"
+ // Date and time formatting
+ implementation "org.ocpsoft.prettytime:prettytime:5.0.7.Final"
+
+/** Debugging **/
+ // Memory leak detection
+ debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}"
+ implementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
+ // Debug bridge for Android
+ debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
+ debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}"
+
+/** Testing **/
+ testImplementation 'junit:junit:4.13.2'
+ testImplementation 'org.mockito:mockito-core:3.6.0'
+
+ androidTestImplementation "androidx.test.ext:junit:1.1.2"
+ androidTestImplementation "androidx.room:room-testing:${androidxRoomVersion}"
+ androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0", {
+ exclude module: 'support-annotations'
+ }
}
static String getGitWorkingBranch() {
diff --git a/app/src/androidTest/java/org/schabi/newpipelegacy/database/AppDatabaseTest.kt b/app/src/androidTest/java/org/schabi/newpipelegacy/database/AppDatabaseTest.kt
index 442257fa7..ed3a0f2e0 100644
--- a/app/src/androidTest/java/org/schabi/newpipelegacy/database/AppDatabaseTest.kt
+++ b/app/src/androidTest/java/org/schabi/newpipelegacy/database/AppDatabaseTest.kt
@@ -31,49 +31,62 @@ class AppDatabaseTest {
}
@get:Rule
- val testHelper = MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
- AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory())
+ val testHelper = MigrationTestHelper(
+ InstrumentationRegistry.getInstrumentation(),
+ AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory()
+ )
@Test
fun migrateDatabaseFrom2to3() {
val databaseInV2 = testHelper.createDatabase(AppDatabase.DATABASE_NAME, Migrations.DB_VER_2)
databaseInV2.run {
- insert("streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
- // put("uid", null)
- put("service_id", DEFAULT_SERVICE_ID)
- put("url", DEFAULT_URL)
- put("title", DEFAULT_TITLE)
- put("stream_type", DEFAULT_TYPE.name)
- put("duration", DEFAULT_DURATION)
- put("uploader", DEFAULT_UPLOADER_NAME)
- put("thumbnail_url", DEFAULT_THUMBNAIL)
- })
- insert("streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
- // put("uid", null)
- put("service_id", DEFAULT_SECOND_SERVICE_ID)
- put("url", DEFAULT_SECOND_URL)
- // put("title", null)
- // put("stream_type", null)
- // put("duration", null)
- // put("uploader", null)
- // put("thumbnail_url", null)
- })
- insert("streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
- // put("uid", null)
- put("service_id", DEFAULT_SERVICE_ID)
- // put("url", null)
- // put("title", null)
- // put("stream_type", null)
- // put("duration", null)
- // put("uploader", null)
- // put("thumbnail_url", null)
- })
+ insert(
+ "streams", SQLiteDatabase.CONFLICT_FAIL,
+ ContentValues().apply {
+ // put("uid", null)
+ put("service_id", DEFAULT_SERVICE_ID)
+ put("url", DEFAULT_URL)
+ put("title", DEFAULT_TITLE)
+ put("stream_type", DEFAULT_TYPE.name)
+ put("duration", DEFAULT_DURATION)
+ put("uploader", DEFAULT_UPLOADER_NAME)
+ put("thumbnail_url", DEFAULT_THUMBNAIL)
+ }
+ )
+ insert(
+ "streams", SQLiteDatabase.CONFLICT_FAIL,
+ ContentValues().apply {
+ // put("uid", null)
+ put("service_id", DEFAULT_SECOND_SERVICE_ID)
+ put("url", DEFAULT_SECOND_URL)
+ // put("title", null)
+ // put("stream_type", null)
+ // put("duration", null)
+ // put("uploader", null)
+ // put("thumbnail_url", null)
+ }
+ )
+ insert(
+ "streams", SQLiteDatabase.CONFLICT_FAIL,
+ ContentValues().apply {
+ // put("uid", null)
+ put("service_id", DEFAULT_SERVICE_ID)
+ // put("url", null)
+ // put("title", null)
+ // put("stream_type", null)
+ // put("duration", null)
+ // put("uploader", null)
+ // put("thumbnail_url", null)
+ }
+ )
close()
}
- testHelper.runMigrationsAndValidate(AppDatabase.DATABASE_NAME, Migrations.DB_VER_3,
- true, Migrations.MIGRATION_2_3)
+ testHelper.runMigrationsAndValidate(
+ AppDatabase.DATABASE_NAME, Migrations.DB_VER_3,
+ true, Migrations.MIGRATION_2_3
+ )
val migratedDatabaseV3 = getMigratedDatabase()
val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst()
@@ -110,9 +123,11 @@ class AppDatabaseTest {
}
private fun getMigratedDatabase(): AppDatabase {
- val database: AppDatabase = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
- AppDatabase::class.java, AppDatabase.DATABASE_NAME)
- .build()
+ val database: AppDatabase = Room.databaseBuilder(
+ ApplicationProvider.getApplicationContext(),
+ AppDatabase::class.java, AppDatabase.DATABASE_NAME
+ )
+ .build()
testHelper.closeWhenFinished(database)
return database
}
diff --git a/app/src/debug/java/org/schabi/newpipelegacy/DebugApp.kt b/app/src/debug/java/org/schabi/newpipelegacy/DebugApp.kt
index 9ae06a1f4..eaf3cf0dc 100644
--- a/app/src/debug/java/org/schabi/newpipelegacy/DebugApp.kt
+++ b/app/src/debug/java/org/schabi/newpipelegacy/DebugApp.kt
@@ -1,6 +1,5 @@
package org.schabi.newpipelegacy
-import android.content.Context
import androidx.multidex.MultiDex
import androidx.preference.PreferenceManager
import com.facebook.stetho.Stetho
@@ -11,29 +10,38 @@ import okhttp3.OkHttpClient
import org.schabi.newpipe.extractor.downloader.Downloader
class DebugApp : App() {
- override fun attachBaseContext(base: Context) {
- super.attachBaseContext(base)
- MultiDex.install(this)
- }
-
override fun onCreate() {
super.onCreate()
initStetho()
// Give each object 10 seconds to be GC'ed, before LeakCanary gets nosy on it
AppWatcher.config = AppWatcher.config.copy(watchDurationMillis = 10000)
- LeakCanary.config = LeakCanary.config.copy(dumpHeap = PreferenceManager
- .getDefaultSharedPreferences(this).getBoolean(getString(
- R.string.allow_heap_dumping_key), false))
+ LeakCanary.config = LeakCanary.config.copy(
+ dumpHeap = PreferenceManager
+ .getDefaultSharedPreferences(this).getBoolean(
+ getString(
+ R.string.allow_heap_dumping_key
+ ),
+ false
+ )
+ )
}
override fun getDownloader(): Downloader {
- val downloader = DownloaderImpl.init(OkHttpClient.Builder()
- .addNetworkInterceptor(StethoInterceptor()))
+ val downloader = DownloaderImpl.init(
+ OkHttpClient.Builder()
+ .addNetworkInterceptor(StethoInterceptor())
+ )
setCookiesToDownloader(downloader)
return downloader
}
+ override fun initACRA() {
+ // install MultiDex before initializing ACRA
+ MultiDex.install(this)
+ super.initACRA()
+ }
+
private fun initStetho() {
// Create an InitializerBuilder
val initializerBuilder = Stetho.newInitializerBuilder(this)
@@ -43,7 +51,8 @@ class DebugApp : App() {
// Enable command line interface
initializerBuilder.enableDumpapp(
- Stetho.defaultDumperPluginsProvider(applicationContext))
+ Stetho.defaultDumperPluginsProvider(applicationContext)
+ )
// Use the InitializerBuilder to generate an Initializer
val initializer = initializerBuilder.build()
@@ -54,6 +63,6 @@ class DebugApp : App() {
override fun isDisposedRxExceptionsReported(): Boolean {
return PreferenceManager.getDefaultSharedPreferences(this)
- .getBoolean(getString(R.string.allow_disposed_exceptions_key), false)
+ .getBoolean(getString(R.string.allow_disposed_exceptions_key), false)
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7306e2348..f27ea136d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,7 +1,8 @@
+ package="org.schabi.newpipelegacy"
+ android:installLocation="auto">
@@ -20,17 +21,20 @@
android:banner="@mipmap/newpipe_tv_banner"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
+ android:largeHeap="true"
android:logo="@mipmap/ic_launcher"
+ android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:theme="@style/OpeningTheme"
+ android:resizeableActivity="true"
tools:ignore="AllowBackup">
-
@@ -160,6 +164,8 @@
+
+
@@ -238,23 +244,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
@@ -282,7 +278,7 @@
-
+
@@ -313,26 +309,62 @@
+
-
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/assets/gpl_2.html b/app/src/main/assets/gpl_2.html
deleted file mode 100644
index 0e1b8827e..000000000
--- a/app/src/main/assets/gpl_2.html
+++ /dev/null
@@ -1,400 +0,0 @@
-
-
-
-
-
- GNU General Public License v2.0 - GNU Project - Free Software Foundation (FSF)
-
-
-
-
-
-Version 2, June 1991
-
-
-
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-
-Everyone is permitted to copy and distribute verbatim copies
-of this license document, but changing it is not allowed.
-
-
-
-
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
-
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
-
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
-
-
-
-
-
-
-0.
- This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-
-
-1.
- You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-
-
-2.
- You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-
-
-
-
- a)
- You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
-
-
- b)
- You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
-
-
- c)
- If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-
-
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-
-
-3.
- You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-
-
-
-
-
-
-
- a)
- Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
-
-
- b)
- Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
-
-
- c)
- Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-
-
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major softwareComponents (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-
-
-4.
- You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-
-
-5.
- You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-
-
-6.
- Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-
-
-7.
- If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-
-
-8.
- If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-
-
-9.
- The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-
-
-10.
- If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-
-NO WARRANTY
-
-
-11.
- BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-
-
-12.
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-
diff --git a/app/src/main/java/org/schabi/newpipelegacy/App.java b/app/src/main/java/org/schabi/newpipelegacy/App.java
index ef5fc504c..7ee2005e0 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/App.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/App.java
@@ -1,7 +1,6 @@
package org.schabi.newpipelegacy;
import android.annotation.TargetApi;
-import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
@@ -10,6 +9,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.multidex.MultiDexApplication;
import androidx.preference.PreferenceManager;
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
@@ -63,12 +63,10 @@
* along with NewPipe. If not, see .
*/
-public class App extends Application {
+public class App extends MultiDexApplication {
protected static final String TAG = App.class.toString();
- @SuppressWarnings("unchecked")
- private static final Class extends ReportSenderFactory>[]
- REPORT_SENDER_FACTORY_CLASSES = new Class[]{AcraReportSenderFactory.class};
private static App app;
+ public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
public static App getApp() {
return app;
@@ -77,7 +75,6 @@ public static App getApp() {
@Override
protected void attachBaseContext(final Context base) {
super.attachBaseContext(base);
-
initACRA();
}
@@ -106,7 +103,7 @@ public void onCreate() {
configureRxJavaErrorHandler();
// Check for new version
- new CheckForNewAppVersionTask().execute();
+ //new CheckForNewAppVersionTask().execute();
}
protected Downloader getDownloader() {
@@ -119,7 +116,7 @@ protected void setCookiesToDownloader(final DownloaderImpl downloader) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
getApplicationContext());
final String key = getApplicationContext().getString(R.string.recaptcha_cookies_key);
- downloader.setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, prefs.getString(key, ""));
+ downloader.setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, prefs.getString(key, null));
downloader.updateYoutubeRestrictedModeCookies(getApplicationContext());
}
@@ -200,10 +197,13 @@ private ImageLoaderConfiguration getImageLoaderConfigurations(final int memoryCa
.build();
}
- private void initACRA() {
+ /**
+ * Called in {@link #attachBaseContext(Context)} after calling the {@code super} method.
+ * Should be overridden if MultiDex is enabled, since it has to be initialized before ACRA.
+ */
+ protected void initACRA() {
try {
final CoreConfiguration acraConfig = new CoreConfigurationBuilder(this)
- .setReportSenderFactoryClasses(REPORT_SENDER_FACTORY_CLASSES)
.setBuildConfigClass(BuildConfig.class)
.build();
ACRA.init(this, acraConfig);
diff --git a/app/src/main/java/org/schabi/newpipelegacy/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipelegacy/DownloaderImpl.java
index 4459f88e0..41774fa0c 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/DownloaderImpl.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/DownloaderImpl.java
@@ -43,7 +43,7 @@
public final class DownloaderImpl extends Downloader {
public static final String USER_AGENT
- = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0";
+ = "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0";
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE_KEY
= "youtube_restricted_mode_key";
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE = "PREF=f2=8000000";
diff --git a/app/src/main/java/org/schabi/newpipelegacy/MainActivity.java b/app/src/main/java/org/schabi/newpipelegacy/MainActivity.java
index ca8480f08..7c7aee197 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/MainActivity.java
@@ -99,6 +99,7 @@ public class MainActivity extends AppCompatActivity {
private static final int ITEM_ID_BOOKMARKS = -3;
private static final int ITEM_ID_DOWNLOADS = -4;
private static final int ITEM_ID_HISTORY = -5;
+ private static final int ITEM_ID_BG_PLAYER = -6;
private static final int ITEM_ID_SETTINGS = 0;
private static final int ITEM_ID_ABOUT = 1;
@@ -116,7 +117,7 @@ protected void onCreate(final Bundle savedInstanceState) {
}
// enable TLS1.1/1.2 for jelly bean and kitkat devices, to fix download and play for
- // mediaCCC sources
+ // media.ccc.de sources
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
TLSSocketFactoryCompat.setAsDefault();
}
@@ -179,6 +180,9 @@ private void setupDrawer() throws Exception {
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_history));
+ drawerItems.getMenu()
+ .add(R.id.menu_tabs_group, ITEM_ID_BG_PLAYER, ORDER, R.string.background_player)
+ .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_play_arrow));
//Settings and About
drawerItems.getMenu()
@@ -263,6 +267,9 @@ private void tabSelected(final MenuItem item) throws ExtractionException {
case ITEM_ID_HISTORY:
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
break;
+ case ITEM_ID_BG_PLAYER:
+ NavigationHelper.openBackgroundPlayer(this);
+ break;
default:
int currentServiceId = ServiceHelper.getSelectedServiceId(this);
StreamingService service = NewPipe.getService(currentServiceId);
diff --git a/app/src/main/java/org/schabi/newpipelegacy/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipelegacy/ReCaptchaActivity.java
index 8b3289f00..156367c47 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/ReCaptchaActivity.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/ReCaptchaActivity.java
@@ -53,6 +53,16 @@ public class ReCaptchaActivity extends AppCompatActivity {
public static final String YT_URL = "https://www.youtube.com";
public static final String RECAPTCHA_COOKIES_KEY = "recaptcha_cookies";
+ public static String sanitizeRecaptchaUrl(@Nullable final String url) {
+ if (url == null || url.trim().isEmpty()) {
+ return YT_URL; // YouTube is the most likely service to have thrown a recaptcha
+ } else {
+ // remove "pbj=1" parameter from YouYube urls, as it makes the page JSON and not HTML
+ return url.replace("&pbj=1", "").replace("pbj=1&", "").replace("?pbj=1", "");
+ }
+ }
+
+
private WebView webView;
private String foundCookies = "";
@@ -64,20 +74,16 @@ protected void onCreate(final Bundle savedInstanceState) {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- String url = getIntent().getStringExtra(RECAPTCHA_URL_EXTRA);
- if (url == null || url.isEmpty()) {
- url = YT_URL;
- }
-
+ final String url = sanitizeRecaptchaUrl(getIntent().getStringExtra(RECAPTCHA_URL_EXTRA));
// set return to Cancel by default
setResult(RESULT_CANCELED);
-
webView = findViewById(R.id.reCaptchaWebView);
// enable Javascript
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
+ webSettings.setUserAgentString(DownloaderImpl.USER_AGENT);
webView.setWebViewClient(new WebViewClient() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@@ -115,8 +121,7 @@ public void onPageFinished(final WebView view, final String url) {
webView.clearHistory();
android.webkit.CookieManager cookieManager = CookieManager.getInstance();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- cookieManager.removeAllCookies(aBoolean -> {
- });
+ cookieManager.removeAllCookies(value -> { });
} else {
cookieManager.removeAllCookie();
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/RouterActivity.java b/app/src/main/java/org/schabi/newpipelegacy/RouterActivity.java
index b5f5e6137..c33f27a5b 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/RouterActivity.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/RouterActivity.java
@@ -50,6 +50,7 @@
import org.schabi.newpipelegacy.util.ListHelper;
import org.schabi.newpipelegacy.util.NavigationHelper;
import org.schabi.newpipelegacy.util.PermissionHelper;
+import org.schabi.newpipelegacy.util.ShareUtils;
import org.schabi.newpipelegacy.util.ThemeHelper;
import org.schabi.newpipelegacy.util.urlfinder.UrlFinder;
import org.schabi.newpipelegacy.views.FocusOverlayView;
@@ -159,27 +160,36 @@ private void handleUrl(final String url) {
if (result) {
onSuccess();
} else {
- onError();
+ showUnsupportedUrlDialog(url);
}
- }, this::handleError));
+ }, throwable -> handleError(throwable, url)));
}
- private void handleError(final Throwable error) {
- error.printStackTrace();
+ private void handleError(final Throwable throwable, final String url) {
+ throwable.printStackTrace();
- if (error instanceof ExtractionException) {
- Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
+ if (throwable instanceof ExtractionException) {
+ showUnsupportedUrlDialog(url);
} else {
- ExtractorHelper.handleGeneralException(this, -1, null, error,
+ ExtractorHelper.handleGeneralException(this, -1, url, throwable,
UserAction.SOMETHING_ELSE, null);
+ finish();
}
-
- finish();
}
- private void onError() {
- Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
- finish();
+ private void showUnsupportedUrlDialog(final String url) {
+ final Context context = getThemeWrapperContext();
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.unsupported_url)
+ .setMessage(R.string.unsupported_url_dialog_message)
+ .setIcon(ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_share))
+ .setPositiveButton(R.string.open_in_browser,
+ (dialog, which) -> ShareUtils.openUrlInBrowser(this, url))
+ .setNegativeButton(R.string.share,
+ (dialog, which) -> ShareUtils.shareUrl(this, "", url)) // no subject
+ .setNeutralButton(R.string.cancel, null)
+ .setOnDismissListener(dialog -> finish())
+ .show();
}
protected void onSuccess() {
@@ -459,7 +469,7 @@ private void handleChoice(final String selectedChoiceKey) {
startActivity(intent);
finish();
- }, this::handleError)
+ }, throwable -> handleError(throwable, currentUrl))
);
return;
}
@@ -492,11 +502,9 @@ private void openDownloadDialog() {
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
downloadDialog.show(fm, "downloadDialog");
fm.executePendingTransactions();
- downloadDialog.getDialog().setOnDismissListener(dialog -> {
- finish();
- });
+ downloadDialog.getDialog().setOnDismissListener(dialog -> finish());
}, (@NonNull Throwable throwable) -> {
- onError();
+ showUnsupportedUrlDialog(currentUrl);
});
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/about/AboutActivity.java b/app/src/main/java/org/schabi/newpipelegacy/about/AboutActivity.java
index 0fe60c53c..ba7ebbf27 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/about/AboutActivity.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/about/AboutActivity.java
@@ -32,7 +32,7 @@ public class AboutActivity extends AppCompatActivity {
*/
private static final SoftwareComponent[] SOFTWARE_COMPONENTS = new SoftwareComponent[]{
new SoftwareComponent("Giga Get", "2014 - 2015", "Peter Cai",
- "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2),
+ "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL3),
new SoftwareComponent("NewPipe Extractor", "2017 - 2020", "Christian Schabesberger",
"https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3),
new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley",
diff --git a/app/src/main/java/org/schabi/newpipelegacy/about/License.java b/app/src/main/java/org/schabi/newpipelegacy/about/License.java
index 38138a061..fe79cddbf 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/about/License.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/about/License.java
@@ -46,10 +46,10 @@ protected License(final Parcel in) {
public Uri getContentUri() {
return new Uri.Builder()
- .scheme("file")
- .path("/android_asset")
- .appendPath(filename)
- .build();
+ .scheme("file")
+ .path("/android_asset")
+ .appendPath(filename)
+ .build();
}
public String getAbbreviation() {
diff --git a/app/src/main/java/org/schabi/newpipelegacy/about/LicenseFragmentHelper.java b/app/src/main/java/org/schabi/newpipelegacy/about/LicenseFragmentHelper.java
index b1c9bf558..fb4dd6217 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/about/LicenseFragmentHelper.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/about/LicenseFragmentHelper.java
@@ -16,8 +16,8 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
-import java.nio.charset.StandardCharsets;
import static org.schabi.newpipelegacy.util.Localization.assureCorrectAppLanguage;
@@ -41,7 +41,7 @@ private static String getFormattedLicense(@NonNull final Context context,
final String webViewData;
try {
final BufferedReader in = new BufferedReader(new InputStreamReader(
- context.getAssets().open(license.getFilename()), StandardCharsets.UTF_8));
+ context.getAssets().open(license.getFilename()), "utf-8"));
String str;
while ((str = in.readLine()) != null) {
licenseContent.append(str);
@@ -50,10 +50,10 @@ private static String getFormattedLicense(@NonNull final Context context,
// split the HTML file and insert the stylesheet into the HEAD of the file
webViewData = licenseContent.toString().replace("",
- "");
+ "");
} catch (IOException e) {
throw new IllegalArgumentException(
- "Could not get license file: " + license.getFilename(), e);
+ "Could not get license file: " + license.getFilename(), e);
}
return webViewData;
}
@@ -65,16 +65,16 @@ private static String getFormattedLicense(@NonNull final Context context,
private static String getLicenseStylesheet(final Context context) {
final boolean isLightTheme = ThemeHelper.isLightThemeSelected(context);
return "body{padding:12px 15px;margin:0;"
- + "background:#" + getHexRGBColor(context, isLightTheme
- ? R.color.light_license_background_color
- : R.color.dark_license_background_color) + ";"
- + "color:#" + getHexRGBColor(context, isLightTheme
- ? R.color.light_license_text_color
- : R.color.dark_license_text_color) + "}"
- + "a[href]{color:#" + getHexRGBColor(context, isLightTheme
- ? R.color.light_youtube_primary_color
- : R.color.dark_youtube_primary_color) + "}"
- + "pre{white-space:pre-wrap}";
+ + "background:#" + getHexRGBColor(context, isLightTheme
+ ? R.color.light_license_background_color
+ : R.color.dark_license_background_color) + ";"
+ + "color:#" + getHexRGBColor(context, isLightTheme
+ ? R.color.light_license_text_color
+ : R.color.dark_license_text_color) + "}"
+ + "a[href]{color:#" + getHexRGBColor(context, isLightTheme
+ ? R.color.light_youtube_primary_color
+ : R.color.dark_youtube_primary_color) + "}"
+ + "pre{white-space:pre-wrap}";
}
/**
@@ -112,17 +112,23 @@ protected void onPostExecute(final Integer result) {
return;
}
- final String webViewData = Base64.encodeToString(getFormattedLicense(activity, license)
- .getBytes(StandardCharsets.UTF_8), Base64.NO_PADDING);
- final WebView webView = new WebView(activity);
- webView.loadData(webViewData, "text/html; charset=UTF-8", "base64");
-
- final AlertDialog.Builder alert = new AlertDialog.Builder(activity);
- alert.setTitle(license.getName());
- alert.setView(webView);
- assureCorrectAppLanguage(activity);
- alert.setNegativeButton(activity.getString(R.string.finish),
+ final String webViewData;
+ try {
+ webViewData = Base64.encodeToString(getFormattedLicense(activity, license)
+ .getBytes("utf-8"), Base64.NO_PADDING);
+ final WebView webView = new WebView(activity);
+ webView.loadData(webViewData, "text/html; charset=UTF-8", "base64");
+
+ final AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+ alert.setTitle(license.getName());
+ alert.setView(webView);
+ assureCorrectAppLanguage(activity);
+ alert.setNegativeButton(activity.getString(R.string.finish),
(dialog, which) -> dialog.dismiss());
- alert.show();
+ alert.show();
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/about/StandardLicenses.java b/app/src/main/java/org/schabi/newpipelegacy/about/StandardLicenses.java
index a548089dd..58cdcc5fa 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/about/StandardLicenses.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/about/StandardLicenses.java
@@ -4,8 +4,6 @@
* Class containing information about standard software licenses.
*/
public final class StandardLicenses {
- public static final License GPL2
- = new License("GNU General Public License, Version 2.0", "GPLv2", "gpl_2.html");
public static final License GPL3
= new License("GNU General Public License, Version 3.0", "GPLv3", "gpl_3.html");
public static final License APACHE2
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/feed/dao/FeedDAO.kt b/app/src/main/java/org/schabi/newpipelegacy/database/feed/dao/FeedDAO.kt
index 3a6efc671..ecc8288bf 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/feed/dao/FeedDAO.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/feed/dao/FeedDAO.kt
@@ -7,18 +7,19 @@ import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import io.reactivex.Flowable
-import java.util.Date
import org.schabi.newpipelegacy.database.feed.model.FeedEntity
import org.schabi.newpipelegacy.database.feed.model.FeedLastUpdatedEntity
import org.schabi.newpipelegacy.database.stream.model.StreamEntity
import org.schabi.newpipelegacy.database.subscription.SubscriptionEntity
+import java.util.Date
@Dao
abstract class FeedDAO {
@Query("DELETE FROM feed")
abstract fun deleteAll(): Int
- @Query("""
+ @Query(
+ """
SELECT s.* FROM streams s
INNER JOIN feed f
@@ -27,10 +28,12 @@ abstract class FeedDAO {
ORDER BY s.upload_date IS NULL DESC, s.upload_date DESC, s.uploader ASC
LIMIT 500
- """)
+ """
+ )
abstract fun getAllStreams(): Flowable>
- @Query("""
+ @Query(
+ """
SELECT s.* FROM streams s
INNER JOIN feed f
@@ -46,10 +49,12 @@ abstract class FeedDAO {
ORDER BY s.upload_date IS NULL DESC, s.upload_date DESC, s.uploader ASC
LIMIT 500
- """)
+ """
+ )
abstract fun getAllStreamsFromGroup(groupId: Long): Flowable>
- @Query("""
+ @Query(
+ """
DELETE FROM feed WHERE
feed.stream_id IN (
@@ -60,10 +65,12 @@ abstract class FeedDAO {
WHERE s.upload_date < :date
)
- """)
+ """
+ )
abstract fun unlinkStreamsOlderThan(date: Date)
- @Query("""
+ @Query(
+ """
DELETE FROM feed
WHERE feed.subscription_id = :subscriptionId
@@ -76,7 +83,8 @@ abstract class FeedDAO {
WHERE s.stream_type = "LIVE_STREAM" OR s.stream_type = "AUDIO_LIVE_STREAM"
)
- """)
+ """
+ )
abstract fun unlinkOldLivestreams(subscriptionId: Long)
@Insert(onConflict = OnConflictStrategy.IGNORE)
@@ -100,12 +108,14 @@ abstract class FeedDAO {
}
}
- @Query("""
+ @Query(
+ """
SELECT MIN(lu.last_updated) FROM feed_last_updated lu
INNER JOIN feed_group_subscription_join fgs
ON fgs.subscription_id = lu.subscription_id AND fgs.group_id = :groupId
- """)
+ """
+ )
abstract fun oldestSubscriptionUpdate(groupId: Long): Flowable>
@Query("SELECT MIN(last_updated) FROM feed_last_updated")
@@ -114,7 +124,8 @@ abstract class FeedDAO {
@Query("SELECT COUNT(*) FROM feed_last_updated WHERE last_updated IS NULL")
abstract fun notLoadedCount(): Flowable
- @Query("""
+ @Query(
+ """
SELECT COUNT(*) FROM subscriptions s
INNER JOIN feed_group_subscription_join fgs
@@ -124,20 +135,24 @@ abstract class FeedDAO {
ON s.uid = lu.subscription_id
WHERE lu.last_updated IS NULL
- """)
+ """
+ )
abstract fun notLoadedCountForGroup(groupId: Long): Flowable
- @Query("""
+ @Query(
+ """
SELECT s.* FROM subscriptions s
LEFT JOIN feed_last_updated lu
ON s.uid = lu.subscription_id
WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold
- """)
+ """
+ )
abstract fun getAllOutdated(outdatedThreshold: Date): Flowable>
- @Query("""
+ @Query(
+ """
SELECT s.* FROM subscriptions s
INNER JOIN feed_group_subscription_join fgs
@@ -147,6 +162,7 @@ abstract class FeedDAO {
ON s.uid = lu.subscription_id
WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold
- """)
+ """
+ )
abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: Date): Flowable>
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedEntity.kt b/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedEntity.kt
index 7271115e3..03fa728e7 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedEntity.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedEntity.kt
@@ -10,21 +10,24 @@ import org.schabi.newpipelegacy.database.feed.model.FeedEntity.Companion.SUBSCRI
import org.schabi.newpipelegacy.database.stream.model.StreamEntity
import org.schabi.newpipelegacy.database.subscription.SubscriptionEntity
-@Entity(tableName = FEED_TABLE,
- primaryKeys = [STREAM_ID, SUBSCRIPTION_ID],
- indices = [Index(SUBSCRIPTION_ID)],
- foreignKeys = [
- ForeignKey(
- entity = StreamEntity::class,
- parentColumns = [StreamEntity.STREAM_ID],
- childColumns = [STREAM_ID],
- onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true),
- ForeignKey(
- entity = SubscriptionEntity::class,
- parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
- childColumns = [SUBSCRIPTION_ID],
- onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true)
- ]
+@Entity(
+ tableName = FEED_TABLE,
+ primaryKeys = [STREAM_ID, SUBSCRIPTION_ID],
+ indices = [Index(SUBSCRIPTION_ID)],
+ foreignKeys = [
+ ForeignKey(
+ entity = StreamEntity::class,
+ parentColumns = [StreamEntity.STREAM_ID],
+ childColumns = [STREAM_ID],
+ onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true
+ ),
+ ForeignKey(
+ entity = SubscriptionEntity::class,
+ parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
+ childColumns = [SUBSCRIPTION_ID],
+ onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true
+ )
+ ]
)
data class FeedEntity(
@ColumnInfo(name = STREAM_ID)
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedGroupEntity.kt b/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedGroupEntity.kt
index 59559ff89..153564f96 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedGroupEntity.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedGroupEntity.kt
@@ -9,8 +9,8 @@ import org.schabi.newpipelegacy.database.feed.model.FeedGroupEntity.Companion.SO
import org.schabi.newpipelegacy.local.subscription.FeedGroupIcon
@Entity(
- tableName = FEED_GROUP_TABLE,
- indices = [Index(SORT_ORDER)]
+ tableName = FEED_GROUP_TABLE,
+ indices = [Index(SORT_ORDER)]
)
data class FeedGroupEntity(
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedGroupSubscriptionEntity.kt b/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedGroupSubscriptionEntity.kt
index 1b3d5b8f9..b9f38bdb1 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedGroupSubscriptionEntity.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedGroupSubscriptionEntity.kt
@@ -11,22 +11,24 @@ import org.schabi.newpipelegacy.database.feed.model.FeedGroupSubscriptionEntity.
import org.schabi.newpipelegacy.database.subscription.SubscriptionEntity
@Entity(
- tableName = FEED_GROUP_SUBSCRIPTION_TABLE,
- primaryKeys = [GROUP_ID, SUBSCRIPTION_ID],
- indices = [Index(SUBSCRIPTION_ID)],
- foreignKeys = [
- ForeignKey(
- entity = FeedGroupEntity::class,
- parentColumns = [FeedGroupEntity.ID],
- childColumns = [GROUP_ID],
- onDelete = CASCADE, onUpdate = CASCADE, deferred = true),
+ tableName = FEED_GROUP_SUBSCRIPTION_TABLE,
+ primaryKeys = [GROUP_ID, SUBSCRIPTION_ID],
+ indices = [Index(SUBSCRIPTION_ID)],
+ foreignKeys = [
+ ForeignKey(
+ entity = FeedGroupEntity::class,
+ parentColumns = [FeedGroupEntity.ID],
+ childColumns = [GROUP_ID],
+ onDelete = CASCADE, onUpdate = CASCADE, deferred = true
+ ),
- ForeignKey(
- entity = SubscriptionEntity::class,
- parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
- childColumns = [SUBSCRIPTION_ID],
- onDelete = CASCADE, onUpdate = CASCADE, deferred = true)
- ]
+ ForeignKey(
+ entity = SubscriptionEntity::class,
+ parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
+ childColumns = [SUBSCRIPTION_ID],
+ onDelete = CASCADE, onUpdate = CASCADE, deferred = true
+ )
+ ]
)
data class FeedGroupSubscriptionEntity(
@ColumnInfo(name = GROUP_ID)
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedLastUpdatedEntity.kt b/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedLastUpdatedEntity.kt
index 842fa8633..c847f1e19 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedLastUpdatedEntity.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/feed/model/FeedLastUpdatedEntity.kt
@@ -4,20 +4,21 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
-import java.util.Date
import org.schabi.newpipelegacy.database.feed.model.FeedLastUpdatedEntity.Companion.FEED_LAST_UPDATED_TABLE
import org.schabi.newpipelegacy.database.feed.model.FeedLastUpdatedEntity.Companion.SUBSCRIPTION_ID
import org.schabi.newpipelegacy.database.subscription.SubscriptionEntity
+import java.util.Date
@Entity(
- tableName = FEED_LAST_UPDATED_TABLE,
- foreignKeys = [
- ForeignKey(
- entity = SubscriptionEntity::class,
- parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
- childColumns = [SUBSCRIPTION_ID],
- onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true)
- ]
+ tableName = FEED_LAST_UPDATED_TABLE,
+ foreignKeys = [
+ ForeignKey(
+ entity = SubscriptionEntity::class,
+ parentColumns = [SubscriptionEntity.SUBSCRIPTION_UID],
+ childColumns = [SUBSCRIPTION_ID],
+ onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE, deferred = true
+ )
+ ]
)
data class FeedLastUpdatedEntity(
@PrimaryKey
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/history/dao/StreamHistoryDAO.java b/app/src/main/java/org/schabi/newpipelegacy/database/history/dao/StreamHistoryDAO.java
index 2b48d0ddf..034001a0c 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/history/dao/StreamHistoryDAO.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/history/dao/StreamHistoryDAO.java
@@ -20,6 +20,9 @@
import static org.schabi.newpipelegacy.database.stream.StreamStatisticsEntry.STREAM_WATCH_COUNT;
import static org.schabi.newpipelegacy.database.stream.model.StreamEntity.STREAM_ID;
import static org.schabi.newpipelegacy.database.stream.model.StreamEntity.STREAM_TABLE;
+import static org.schabi.newpipelegacy.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS;
+import static org.schabi.newpipelegacy.database.stream.model.StreamStateEntity.STREAM_PROGRESS_TIME;
+import static org.schabi.newpipelegacy.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
@Dao
public abstract class StreamHistoryDAO implements HistoryDAO {
@@ -73,6 +76,12 @@ public Flowable> listByService(final int serviceId) {
+ " SUM(" + STREAM_REPEAT_COUNT + ") AS " + STREAM_WATCH_COUNT
+ " FROM " + STREAM_HISTORY_TABLE + " GROUP BY " + JOIN_STREAM_ID + ")"
- + " ON " + STREAM_ID + " = " + JOIN_STREAM_ID)
+ + " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
+
+ + " LEFT JOIN "
+ + "(SELECT " + JOIN_STREAM_ID + " AS " + JOIN_STREAM_ID_ALIAS + ", "
+ + STREAM_PROGRESS_TIME
+ + " FROM " + STREAM_STATE_TABLE + " )"
+ + " ON " + STREAM_ID + " = " + JOIN_STREAM_ID_ALIAS)
public abstract Flowable> getStatistics();
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/history/model/StreamHistoryEntry.kt b/app/src/main/java/org/schabi/newpipelegacy/database/history/model/StreamHistoryEntry.kt
index 71fad2766..3b39abdee 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/history/model/StreamHistoryEntry.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/history/model/StreamHistoryEntry.kt
@@ -2,8 +2,8 @@ package org.schabi.newpipelegacy.database.history.model
import androidx.room.ColumnInfo
import androidx.room.Embedded
-import java.util.Date
import org.schabi.newpipelegacy.database.stream.model.StreamEntity
+import java.util.Date
data class StreamHistoryEntry(
@Embedded
@@ -25,6 +25,6 @@ data class StreamHistoryEntry(
fun hasEqualValues(other: StreamHistoryEntry): Boolean {
return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId &&
- accessDate.compareTo(other.accessDate) == 0
+ accessDate.compareTo(other.accessDate) == 0
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/playlist/PlaylistStreamEntry.kt b/app/src/main/java/org/schabi/newpipelegacy/database/playlist/PlaylistStreamEntry.kt
index 506012cfe..f190a1e88 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/playlist/PlaylistStreamEntry.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/playlist/PlaylistStreamEntry.kt
@@ -6,11 +6,16 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipelegacy.database.LocalItem
import org.schabi.newpipelegacy.database.playlist.model.PlaylistStreamEntity
import org.schabi.newpipelegacy.database.stream.model.StreamEntity
+import org.schabi.newpipelegacy.database.stream.model.StreamStateEntity
+import kotlin.jvm.Throws
-class PlaylistStreamEntry(
+data class PlaylistStreamEntry(
@Embedded
val streamEntity: StreamEntity,
+ @ColumnInfo(name = StreamStateEntity.STREAM_PROGRESS_TIME, defaultValue = "0")
+ val progressTime: Long,
+
@ColumnInfo(name = PlaylistStreamEntity.JOIN_STREAM_ID)
val streamId: Long,
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/playlist/dao/PlaylistStreamDAO.java b/app/src/main/java/org/schabi/newpipelegacy/database/playlist/dao/PlaylistStreamDAO.java
index 3d560cc7a..b099482e3 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/playlist/dao/PlaylistStreamDAO.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/playlist/dao/PlaylistStreamDAO.java
@@ -24,6 +24,9 @@
import static org.schabi.newpipelegacy.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE;
import static org.schabi.newpipelegacy.database.stream.model.StreamEntity.STREAM_ID;
import static org.schabi.newpipelegacy.database.stream.model.StreamEntity.STREAM_TABLE;
+import static org.schabi.newpipelegacy.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS;
+import static org.schabi.newpipelegacy.database.stream.model.StreamStateEntity.STREAM_PROGRESS_TIME;
+import static org.schabi.newpipelegacy.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
@Dao
public abstract class PlaylistStreamDAO implements BasicDAO {
@@ -58,6 +61,13 @@ public Flowable> listByService(final int serviceId) {
// then merge with the stream metadata
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
+
+ + " LEFT JOIN "
+ + "(SELECT " + JOIN_STREAM_ID + " AS " + JOIN_STREAM_ID_ALIAS + ", "
+ + STREAM_PROGRESS_TIME
+ + " FROM " + STREAM_STATE_TABLE + " )"
+ + " ON " + STREAM_ID + " = " + JOIN_STREAM_ID_ALIAS
+
+ " ORDER BY " + JOIN_INDEX + " ASC")
public abstract Flowable> getOrderedStreamsOf(long playlistId);
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/stream/StreamStatisticsEntry.kt b/app/src/main/java/org/schabi/newpipelegacy/database/stream/StreamStatisticsEntry.kt
index 4c5427b68..feb79c543 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/stream/StreamStatisticsEntry.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/stream/StreamStatisticsEntry.kt
@@ -2,16 +2,20 @@ package org.schabi.newpipelegacy.database.stream
import androidx.room.ColumnInfo
import androidx.room.Embedded
-import java.util.Date
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipelegacy.database.LocalItem
import org.schabi.newpipelegacy.database.history.model.StreamHistoryEntity
import org.schabi.newpipelegacy.database.stream.model.StreamEntity
+import org.schabi.newpipelegacy.database.stream.model.StreamStateEntity.STREAM_PROGRESS_TIME
+import java.util.Date
class StreamStatisticsEntry(
@Embedded
val streamEntity: StreamEntity,
+ @ColumnInfo(name = STREAM_PROGRESS_TIME, defaultValue = "0")
+ val progressTime: Long,
+
@ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID)
val streamId: Long,
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/stream/dao/StreamDAO.kt b/app/src/main/java/org/schabi/newpipelegacy/database/stream/dao/StreamDAO.kt
index 9224cd548..b47f77454 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/stream/dao/StreamDAO.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/stream/dao/StreamDAO.kt
@@ -7,13 +7,13 @@ import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import io.reactivex.Flowable
-import java.util.Date
import org.schabi.newpipe.extractor.stream.StreamType
import org.schabi.newpipe.extractor.stream.StreamType.AUDIO_LIVE_STREAM
import org.schabi.newpipe.extractor.stream.StreamType.LIVE_STREAM
import org.schabi.newpipelegacy.database.BasicDAO
import org.schabi.newpipelegacy.database.stream.model.StreamEntity
import org.schabi.newpipelegacy.database.stream.model.StreamEntity.Companion.STREAM_ID
+import java.util.Date
@Dao
abstract class StreamDAO : BasicDAO {
@@ -35,10 +35,12 @@ abstract class StreamDAO : BasicDAO {
@Insert(onConflict = OnConflictStrategy.IGNORE)
internal abstract fun silentInsertAllInternal(streams: List): List
- @Query("""
+ @Query(
+ """
SELECT uid, stream_type, textual_upload_date, upload_date, is_upload_date_approximation, duration
FROM streams WHERE url = :url AND service_id = :serviceId
- """)
+ """
+ )
internal abstract fun getMinimalStreamForCompare(serviceId: Int, url: String): StreamCompareFeed?
@Transaction
@@ -79,7 +81,7 @@ abstract class StreamDAO : BasicDAO {
private fun compareAndUpdateStream(newerStream: StreamEntity) {
val existentMinimalStream = getMinimalStreamForCompare(newerStream.serviceId, newerStream.url)
- ?: throw IllegalStateException("Stream cannot be null just after insertion.")
+ ?: throw IllegalStateException("Stream cannot be null just after insertion.")
newerStream.uid = existentMinimalStream.uid
val isNewerStreamLive = newerStream.streamType == AUDIO_LIVE_STREAM || newerStream.streamType == LIVE_STREAM
@@ -88,7 +90,7 @@ abstract class StreamDAO : BasicDAO {
// Use the existent upload date if the newer stream does not have a better precision
// (i.e. is an approximation). This is done to prevent unnecessary changes.
val hasBetterPrecision =
- newerStream.uploadDate != null && newerStream.isUploadDateApproximation != true
+ newerStream.uploadDate != null && newerStream.isUploadDateApproximation != true
if (existentMinimalStream.uploadDate != null && !hasBetterPrecision) {
newerStream.uploadDate = existentMinimalStream.uploadDate
newerStream.textualUploadDate = existentMinimalStream.textualUploadDate
@@ -101,7 +103,8 @@ abstract class StreamDAO : BasicDAO {
}
}
- @Query("""
+ @Query(
+ """
DELETE FROM streams WHERE
NOT EXISTS (SELECT 1 FROM stream_history sh
@@ -112,7 +115,8 @@ abstract class StreamDAO : BasicDAO {
AND NOT EXISTS (SELECT 1 FROM feed f
WHERE f.stream_id = streams.uid)
- """)
+ """
+ )
abstract fun deleteOrphans(): Int
/**
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/stream/model/StreamEntity.kt b/app/src/main/java/org/schabi/newpipelegacy/database/stream/model/StreamEntity.kt
index 62c4f5659..8f4fad87e 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/stream/model/StreamEntity.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/stream/model/StreamEntity.kt
@@ -5,9 +5,6 @@ import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.Index
import androidx.room.PrimaryKey
-import java.io.Serializable
-import java.util.Calendar
-import java.util.Date
import org.schabi.newpipe.extractor.localization.DateWrapper
import org.schabi.newpipe.extractor.stream.StreamInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem
@@ -16,11 +13,15 @@ import org.schabi.newpipelegacy.database.stream.model.StreamEntity.Companion.STR
import org.schabi.newpipelegacy.database.stream.model.StreamEntity.Companion.STREAM_TABLE
import org.schabi.newpipelegacy.database.stream.model.StreamEntity.Companion.STREAM_URL
import org.schabi.newpipelegacy.player.playqueue.PlayQueueItem
+import java.io.Serializable
+import java.util.Calendar
+import java.util.Date
-@Entity(tableName = STREAM_TABLE,
- indices = [
- Index(value = [STREAM_SERVICE_ID, STREAM_URL], unique = true)
- ]
+@Entity(
+ tableName = STREAM_TABLE,
+ indices = [
+ Index(value = [STREAM_SERVICE_ID, STREAM_URL], unique = true)
+ ]
)
data class StreamEntity(
@PrimaryKey(autoGenerate = true)
@@ -60,30 +61,29 @@ data class StreamEntity(
@ColumnInfo(name = STREAM_IS_UPLOAD_DATE_APPROXIMATION)
var isUploadDateApproximation: Boolean? = null
) : Serializable {
-
@Ignore
constructor(item: StreamInfoItem) : this(
- serviceId = item.serviceId, url = item.url, title = item.name,
- streamType = item.streamType, duration = item.duration, uploader = item.uploaderName,
- thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount,
- textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.date()?.time,
- isUploadDateApproximation = item.uploadDate?.isApproximation
+ serviceId = item.serviceId, url = item.url, title = item.name,
+ streamType = item.streamType, duration = item.duration, uploader = item.uploaderName,
+ thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount,
+ textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.date()?.time,
+ isUploadDateApproximation = item.uploadDate?.isApproximation
)
@Ignore
constructor(info: StreamInfo) : this(
- serviceId = info.serviceId, url = info.url, title = info.name,
- streamType = info.streamType, duration = info.duration, uploader = info.uploaderName,
- thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount,
- textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.date()?.time,
- isUploadDateApproximation = info.uploadDate?.isApproximation
+ serviceId = info.serviceId, url = info.url, title = info.name,
+ streamType = info.streamType, duration = info.duration, uploader = info.uploaderName,
+ thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount,
+ textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.date()?.time,
+ isUploadDateApproximation = info.uploadDate?.isApproximation
)
@Ignore
constructor(item: PlayQueueItem) : this(
- serviceId = item.serviceId, url = item.url, title = item.title,
- streamType = item.streamType, duration = item.duration, uploader = item.uploader,
- thumbnailUrl = item.thumbnailUrl
+ serviceId = item.serviceId, url = item.url, title = item.title,
+ streamType = item.streamType, duration = item.duration, uploader = item.uploader,
+ thumbnailUrl = item.thumbnailUrl
)
fun toStreamInfoItem(): StreamInfoItem {
@@ -95,8 +95,11 @@ data class StreamEntity(
if (viewCount != null) item.viewCount = viewCount as Long
item.textualUploadDate = textualUploadDate
item.uploadDate = uploadDate?.let {
- DateWrapper(Calendar.getInstance().apply { time = it }, isUploadDateApproximation
- ?: false)
+ DateWrapper(
+ Calendar.getInstance().apply { time = it },
+ isUploadDateApproximation
+ ?: false
+ )
}
return item
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/stream/model/StreamStateEntity.java b/app/src/main/java/org/schabi/newpipelegacy/database/stream/model/StreamStateEntity.java
index caa187e5a..51a9c9352 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/stream/model/StreamStateEntity.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/stream/model/StreamStateEntity.java
@@ -22,6 +22,9 @@
public class StreamStateEntity {
public static final String STREAM_STATE_TABLE = "stream_state";
public static final String JOIN_STREAM_ID = "stream_id";
+ // This additional field is required for the SQL query because 'stream_id' is used
+ // for some other joins already
+ public static final String JOIN_STREAM_ID_ALIAS = "stream_id_alias";
public static final String STREAM_PROGRESS_TIME = "progress_time";
/**
diff --git a/app/src/main/java/org/schabi/newpipelegacy/database/subscription/SubscriptionDAO.kt b/app/src/main/java/org/schabi/newpipelegacy/database/subscription/SubscriptionDAO.kt
index 24ed3bd70..9a58b36a1 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/database/subscription/SubscriptionDAO.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/database/subscription/SubscriptionDAO.kt
@@ -20,16 +20,19 @@ abstract class SubscriptionDAO : BasicDAO {
@Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC")
abstract override fun getAll(): Flowable>
- @Query("""
+ @Query(
+ """
SELECT * FROM subscriptions
WHERE name LIKE '%' || :filter || '%'
ORDER BY name COLLATE NOCASE ASC
- """)
+ """
+ )
abstract fun getSubscriptionsFiltered(filter: String): Flowable>
- @Query("""
+ @Query(
+ """
SELECT * FROM subscriptions s
LEFT JOIN feed_group_subscription_join fgs
@@ -38,12 +41,14 @@ abstract class SubscriptionDAO : BasicDAO {
WHERE (fgs.subscription_id IS NULL OR fgs.group_id = :currentGroupId)
ORDER BY name COLLATE NOCASE ASC
- """)
+ """
+ )
abstract fun getSubscriptionsOnlyUngrouped(
currentGroupId: Long
): Flowable>
- @Query("""
+ @Query(
+ """
SELECT * FROM subscriptions s
LEFT JOIN feed_group_subscription_join fgs
@@ -53,7 +58,8 @@ abstract class SubscriptionDAO : BasicDAO {
AND s.name LIKE '%' || :filter || '%'
ORDER BY name COLLATE NOCASE ASC
- """)
+ """
+ )
abstract fun getSubscriptionsOnlyUngroupedFiltered(
currentGroupId: Long,
filter: String
diff --git a/app/src/main/java/org/schabi/newpipelegacy/download/DownloadActivity.java b/app/src/main/java/org/schabi/newpipelegacy/download/DownloadActivity.java
index 7e4ef1193..84f56075b 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/download/DownloadActivity.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/download/DownloadActivity.java
@@ -14,6 +14,7 @@
import org.schabi.newpipelegacy.R;
import org.schabi.newpipelegacy.util.AndroidTvUtils;
+import org.schabi.newpipelegacy.util.NavigationHelper;
import org.schabi.newpipelegacy.util.ThemeHelper;
import org.schabi.newpipelegacy.views.FocusOverlayView;
@@ -87,6 +88,9 @@ public boolean onOptionsItemSelected(final MenuItem item) {
case android.R.id.home:
onBackPressed();
return true;
+ case R.id.action_settings:
+ NavigationHelper.openSettings(this);
+ return true;
default:
return super.onOptionsItemSelected(item);
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipelegacy/download/DownloadDialog.java
index e91141dfc..b77f79e7c 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/download/DownloadDialog.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/download/DownloadDialog.java
@@ -748,7 +748,7 @@ private void checkSelectedDownload(final StoredDirectoryHelper mainStorage,
AlertDialog.Builder askDialog = new AlertDialog.Builder(context)
.setTitle(R.string.download_dialog_title)
.setMessage(msgBody)
- .setNegativeButton(android.R.string.cancel, null);
+ .setNegativeButton(R.string.cancel, null);
final StoredFileHelper finalStorage = storage;
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/BaseStateFragment.java
index 76a4a479e..6352f0a71 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/BaseStateFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/BaseStateFragment.java
@@ -19,9 +19,15 @@
import org.schabi.newpipelegacy.MainActivity;
import org.schabi.newpipelegacy.R;
import org.schabi.newpipelegacy.ReCaptchaActivity;
+import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
+import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
+import org.schabi.newpipe.extractor.exceptions.PaidContentException;
+import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
+import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
+import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
import org.schabi.newpipelegacy.report.ErrorActivity;
import org.schabi.newpipelegacy.report.UserAction;
import org.schabi.newpipelegacy.util.ExceptionUtils;
@@ -210,12 +216,30 @@ protected boolean onError(final Throwable exception) {
if (exception instanceof ReCaptchaException) {
onReCaptchaException((ReCaptchaException) exception);
return true;
- } else if (exception instanceof ContentNotAvailableException) {
- showError(getString(R.string.content_not_available), false);
- return true;
} else if (ExceptionUtils.isNetworkRelated(exception)) {
showError(getString(R.string.network_error), true);
return true;
+ } else if (exception instanceof AgeRestrictedContentException) {
+ showError(getString(R.string.restricted_video_no_stream), false);
+ return true;
+ } else if (exception instanceof GeographicRestrictionException) {
+ showError(getString(R.string.georestricted_content), false);
+ return true;
+ } else if (exception instanceof PaidContentException) {
+ showError(getString(R.string.paid_content), false);
+ return true;
+ } else if (exception instanceof PrivateContentException) {
+ showError(getString(R.string.private_content), false);
+ return true;
+ } else if (exception instanceof SoundCloudGoPlusContentException) {
+ showError(getString(R.string.soundcloud_go_plus_content), false);
+ return true;
+ } else if (exception instanceof YoutubeMusicPremiumContentException) {
+ showError(getString(R.string.youtube_music_premium_content), false);
+ return true;
+ } else if (exception instanceof ContentNotAvailableException) {
+ showError(getString(R.string.content_not_available), false);
+ return true;
} else if (exception instanceof ContentNotSupportedException) {
showError(getString(R.string.content_not_supported), false);
return true;
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/MainFragment.java
index 805ec6602..ddc86e018 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/MainFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/MainFragment.java
@@ -148,10 +148,8 @@ public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
try {
- NavigationHelper.openSearchFragment(
- getFragmentManager(),
- ServiceHelper.getSelectedServiceId(activity),
- "");
+ NavigationHelper.openSearchFragment(getFM(),
+ ServiceHelper.getSelectedServiceId(activity), "");
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/detail/VideoDetailFragment.java
index d488b68d2..90f1c7f8d 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/detail/VideoDetailFragment.java
@@ -53,6 +53,7 @@
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
+import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.AudioStream;
@@ -84,6 +85,7 @@
import org.schabi.newpipelegacy.util.Localization;
import org.schabi.newpipelegacy.util.NavigationHelper;
import org.schabi.newpipelegacy.util.PermissionHelper;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import org.schabi.newpipelegacy.util.ShareUtils;
import org.schabi.newpipelegacy.util.StreamItemAdapter;
import org.schabi.newpipelegacy.util.StreamItemAdapter.StreamSizeWrapper;
@@ -327,7 +329,7 @@ public void onActivityResult(final int requestCode, final int resultCode, final
case ReCaptchaActivity.RECAPTCHA_REQUEST:
if (resultCode == Activity.RESULT_OK) {
NavigationHelper
- .openVideoDetailFragment(getFragmentManager(), serviceId, url, name);
+ .openVideoDetailFragment(getFM(), serviceId, url, name);
} else {
Log.e(TAG, "ReCaptcha failed");
}
@@ -408,9 +410,9 @@ public void onClick(final View v) {
openPopupPlayer(false);
break;
case R.id.detail_controls_playlist_append:
- if (getFragmentManager() != null && currentInfo != null) {
+ if (getFM() != null && currentInfo != null) {
PlaylistAppendDialog.fromStreamInfo(currentInfo)
- .show(getFragmentManager(), TAG);
+ .show(getFM(), TAG);
}
break;
case R.id.detail_controls_download:
@@ -449,11 +451,8 @@ public void onClick(final View v) {
private void openChannel(final String subChannelUrl, final String subChannelName) {
try {
- NavigationHelper.openChannelFragment(
- getFragmentManager(),
- currentInfo.getServiceId(),
- subChannelUrl,
- subChannelName);
+ NavigationHelper.openChannelFragment(getFM(), currentInfo.getServiceId(),
+ subChannelUrl, subChannelName);
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
@@ -466,6 +465,9 @@ public boolean onLongClick(final View v) {
}
switch (v.getId()) {
+ case R.id.detail_controls_playlist_append:
+ NavigationHelper.openBookmarksFragment(getFM());
+ break;
case R.id.detail_controls_background:
openBackgroundPlayer(true);
break;
@@ -589,6 +591,7 @@ protected void initListeners() {
detailControlsBackground.setOnClickListener(this);
detailControlsPopup.setOnClickListener(this);
detailControlsAddToPlaylist.setOnClickListener(this);
+ detailControlsAddToPlaylist.setOnLongClickListener(this);
detailControlsDownload.setOnClickListener(this);
detailControlsDownload.setOnLongClickListener(this);
@@ -618,7 +621,7 @@ private View.OnTouchListener getOnControlsTouchListener() {
private void initThumbnailViews(@NonNull final StreamInfo info) {
thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
if (!TextUtils.isEmpty(info.getThumbnailUrl())) {
- final String infoServiceName = NewPipe.getNameOfService(info.getServiceId());
+ final String infoServiceName = ServiceHelper.getNameOfServiceById(info.getServiceId());
final ImageLoadingListener onFailListener = new SimpleImageLoadingListener() {
@Override
public void onLoadingFailed(final String imageUri, final View view,
@@ -693,6 +696,18 @@ public boolean onOptionsItemSelected(final MenuItem item) {
currentInfo.getOriginalUrl());
}
return true;
+ case R.id.menu_item_share_stream:
+ if (currentInfo != null) {
+ final Stream stream;
+ if (currentInfo.getVideoStreams().isEmpty()
+ && currentInfo.getVideoOnlyStreams().isEmpty()) {
+ stream = getDefaultAudioStream();
+ } else {
+ stream = getSelectedVideoStream();
+ }
+ ShareUtils.shareUrl(requireContext(), currentInfo.getName(), stream.getUrl());
+ }
+ return true;
case R.id.menu_item_openInBrowser:
if (currentInfo != null) {
ShareUtils.openUrlInBrowser(requireContext(), currentInfo.getOriginalUrl());
@@ -926,10 +941,10 @@ private boolean shouldShowComments() {
//////////////////////////////////////////////////////////////////////////*/
private void openBackgroundPlayer(final boolean append) {
- AudioStream audioStream = currentInfo.getAudioStreams()
- .get(ListHelper.getDefaultAudioFormat(activity, currentInfo.getAudioStreams()));
+ final AudioStream audioStream = getDefaultAudioStream();
- boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
+ final boolean useExternalAudioPlayer = PreferenceManager
+ .getDefaultSharedPreferences(activity)
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
@@ -1013,9 +1028,23 @@ private VideoStream getSelectedVideoStream() {
return sortedVideoStreams != null ? sortedVideoStreams.get(selectedVideoStreamIndex) : null;
}
+ /**
+ * Get the stream to play when the current stream is an audio-only stream.
+ *
+ * This is the audio-only equivalent of getSelectedVideoStream,
+ * without the ability for the user to select a custom stream quality.
+ *
+ * @return AudioStream instance according to user settings
+ */
+ private AudioStream getDefaultAudioStream() {
+ final List audioStreams = currentInfo.getAudioStreams();
+ final int streamIndex = ListHelper.getDefaultAudioFormat(activity, audioStreams);
+ return audioStreams.get(streamIndex);
+ }
+
private void prepareDescription(final Description description) {
if (description == null || TextUtils.isEmpty(description.getContent())
- || description == Description.emptyDescription) {
+ || description == Description.EMPTY_DESCRIPTION) {
return;
}
@@ -1259,33 +1288,35 @@ public void handleResult(@NonNull final StreamInfo info) {
setTitleToUrl(info.getServiceId(), info.getOriginalUrl(), info.getName());
if (!info.getErrors().isEmpty()) {
- showSnackBarError(info.getErrors(),
- UserAction.REQUESTED_STREAM,
- NewPipe.getNameOfService(info.getServiceId()),
- info.getUrl(),
- 0);
- }
-
- switch (info.getStreamType()) {
- case LIVE_STREAM:
- case AUDIO_LIVE_STREAM:
- detailControlsDownload.setVisibility(View.GONE);
- spinnerToolbar.setVisibility(View.GONE);
- break;
- default:
- if (info.getAudioStreams().isEmpty()) {
- detailControlsBackground.setVisibility(View.GONE);
- }
- if (!info.getVideoStreams().isEmpty() || !info.getVideoOnlyStreams().isEmpty()) {
- break;
+ // Bandcamp fan pages are not yet supported and thus a ContentNotAvailableException is
+ // thrown. This is not an error and thus should not be shown to the user.
+ for (final Throwable throwable : info.getErrors()) {
+ if (throwable instanceof ContentNotSupportedException
+ && "Fan pages are not supported".equals(throwable.getMessage())) {
+ info.getErrors().remove(throwable);
}
+ }
- detailControlsPopup.setVisibility(View.GONE);
- spinnerToolbar.setVisibility(View.GONE);
- thumbnailPlayButton.setImageResource(R.drawable.ic_headset_shadow);
- break;
+ if (!info.getErrors().isEmpty()) {
+ showSnackBarError(info.getErrors(),
+ UserAction.REQUESTED_STREAM,
+ ServiceHelper.getNameOfServiceById(info.getServiceId()),
+ info.getUrl(),
+ 0);
+ }
}
+ detailControlsDownload.setVisibility(info.getStreamType() == StreamType.LIVE_STREAM
+ || info.getStreamType() == StreamType.AUDIO_LIVE_STREAM ? View.GONE : View.VISIBLE);
+ detailControlsBackground.setVisibility(info.getAudioStreams().isEmpty()
+ ? View.GONE : View.VISIBLE);
+
+ final boolean noVideoStreams =
+ info.getVideoStreams().isEmpty() && info.getVideoOnlyStreams().isEmpty();
+ detailControlsPopup.setVisibility(noVideoStreams ? View.GONE : View.VISIBLE);
+ thumbnailPlayButton.setImageResource(
+ noVideoStreams ? R.drawable.ic_headset_shadow : R.drawable.ic_play_arrow_shadow);
+
if (autoPlayEnabled) {
openVideoPlayer();
// Only auto play in the first open
@@ -1364,14 +1395,14 @@ protected boolean onError(final Throwable exception) {
return true;
}
- int errorId = exception instanceof YoutubeStreamExtractor.DecryptException
+ final int errorId = exception instanceof YoutubeStreamExtractor.DeobfuscateException
? R.string.youtube_signature_decryption_error
: exception instanceof ExtractionException
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_STREAM,
- NewPipe.getNameOfService(serviceId), url, errorId);
+ ServiceHelper.getNameOfServiceById(serviceId), url, errorId);
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/channel/ChannelFragment.java
index 2096600a3..27f506418 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/channel/ChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/channel/ChannelFragment.java
@@ -30,7 +30,6 @@
import org.schabi.newpipelegacy.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
@@ -46,6 +45,7 @@
import org.schabi.newpipelegacy.util.ImageDisplayConstants;
import org.schabi.newpipelegacy.util.Localization;
import org.schabi.newpipelegacy.util.NavigationHelper;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import org.schabi.newpipelegacy.util.ShareUtils;
import org.schabi.newpipelegacy.util.ThemeHelper;
@@ -244,7 +244,7 @@ private void monitorSubscription(final ChannelInfo info) {
final Consumer onError = (Throwable throwable) -> {
animateView(headerSubscribeButton, false, 100);
showSnackBarError(throwable, UserAction.SUBSCRIPTION,
- NewPipe.getNameOfService(currentInfo.getServiceId()),
+ ServiceHelper.getNameOfServiceById(currentInfo.getServiceId()),
"Get subscription status", 0);
};
@@ -297,7 +297,7 @@ private void updateSubscription(final ChannelInfo info) {
final Consumer onError = (@NonNull Throwable throwable) ->
onUnrecoverableError(throwable,
UserAction.SUBSCRIPTION,
- NewPipe.getNameOfService(info.getServiceId()),
+ ServiceHelper.getNameOfServiceById(info.getServiceId()),
"Updating Subscription for " + info.getUrl(),
R.string.subscription_update_failed);
@@ -318,7 +318,7 @@ private Disposable monitorSubscribeButton(final Button subscribeButton,
final Consumer onError = (@NonNull Throwable throwable) ->
onUnrecoverableError(throwable,
UserAction.SUBSCRIPTION,
- NewPipe.getNameOfService(currentInfo.getServiceId()),
+ ServiceHelper.getNameOfServiceById(currentInfo.getServiceId()),
"Subscription Change",
R.string.subscription_change_failed);
@@ -426,8 +426,8 @@ public void onClick(final View v) {
case R.id.sub_channel_title_view:
if (!TextUtils.isEmpty(currentInfo.getParentChannelUrl())) {
try {
- NavigationHelper.openChannelFragment(getFragmentManager(),
- currentInfo.getServiceId(), currentInfo.getParentChannelUrl(),
+ NavigationHelper.openChannelFragment(getFM(), currentInfo.getServiceId(),
+ currentInfo.getParentChannelUrl(),
currentInfo.getParentChannelName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
@@ -505,7 +505,9 @@ public void handleResult(@NonNull final ChannelInfo result) {
if (!errors.isEmpty()) {
showSnackBarError(errors, UserAction.REQUESTED_CHANNEL,
- NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
+ ServiceHelper.getNameOfServiceById(result.getServiceId()),
+ result.getUrl(), 0
+ );
}
}
@@ -565,7 +567,7 @@ public void handleNextItems(final ListExtractor.InfoItemsPage result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(),
UserAction.REQUESTED_CHANNEL,
- NewPipe.getNameOfService(serviceId),
+ ServiceHelper.getNameOfServiceById(serviceId),
"Get next page of: " + url,
R.string.general_error);
}
@@ -585,7 +587,7 @@ protected boolean onError(final Throwable exception) {
? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_CHANNEL,
- NewPipe.getNameOfService(serviceId), url, errorId);
+ ServiceHelper.getNameOfServiceById(serviceId), url, errorId);
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/comments/CommentsFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/comments/CommentsFragment.java
index 46785d181..92ba112bd 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/comments/CommentsFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/comments/CommentsFragment.java
@@ -7,18 +7,19 @@
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.schabi.newpipelegacy.R;
import org.schabi.newpipe.extractor.ListExtractor;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.comments.CommentsInfo;
import org.schabi.newpipelegacy.fragments.list.BaseListInfoFragment;
import org.schabi.newpipelegacy.report.UserAction;
import org.schabi.newpipelegacy.util.AnimationUtils;
import org.schabi.newpipelegacy.util.ExtractorHelper;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import io.reactivex.Single;
import io.reactivex.disposables.CompositeDisposable;
@@ -28,6 +29,8 @@ public class CommentsFragment extends BaseListInfoFragment {
private boolean mIsVisibleToUser = false;
+ private TextView emptyStateDesc;
+
public static CommentsFragment getInstance(final int serviceId, final String url,
final String name) {
CommentsFragment instance = new CommentsFragment();
@@ -35,6 +38,13 @@ public static CommentsFragment getInstance(final int serviceId, final String ur
return instance;
}
+ @Override
+ protected void initViews(final View rootView, final Bundle savedInstanceState) {
+ super.initViews(rootView, savedInstanceState);
+
+ emptyStateDesc = rootView.findViewById(R.id.empty_state_desc);
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@@ -92,11 +102,16 @@ public void showLoading() {
public void handleResult(@NonNull final CommentsInfo result) {
super.handleResult(result);
+ emptyStateDesc.setText(
+ result.isCommentsDisabled()
+ ? R.string.comments_are_disabled
+ : R.string.no_comments);
+
AnimationUtils.slideUp(getView(), 120, 150, 0.06f);
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS,
- NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
+ ServiceHelper.getNameOfServiceById(result.getServiceId()), result.getUrl(), 0);
}
if (disposables != null) {
@@ -110,7 +125,7 @@ public void handleNextItems(final ListExtractor.InfoItemsPage result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS,
- NewPipe.getNameOfService(serviceId), "Get next page of: " + url,
+ ServiceHelper.getNameOfServiceById(serviceId), "Get next page of: " + url,
R.string.general_error);
}
}
@@ -127,7 +142,8 @@ protected boolean onError(final Throwable exception) {
hideLoading();
showSnackBarError(exception, UserAction.REQUESTED_COMMENTS,
- NewPipe.getNameOfService(serviceId), url, R.string.error_unable_to_load_comments);
+ ServiceHelper.getNameOfServiceById(serviceId),
+ url, R.string.error_unable_to_load_comments);
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/kiosk/KioskFragment.java
index 0845f5b41..4cb91bfc4 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/kiosk/KioskFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/kiosk/KioskFragment.java
@@ -24,6 +24,7 @@
import org.schabi.newpipelegacy.util.ExtractorHelper;
import org.schabi.newpipelegacy.util.KioskTranslator;
import org.schabi.newpipelegacy.util.Localization;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import icepick.State;
import io.reactivex.Single;
@@ -173,7 +174,7 @@ public void handleResult(@NonNull final KioskInfo result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(),
UserAction.REQUESTED_KIOSK,
- NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
+ ServiceHelper.getNameOfServiceById(result.getServiceId()), result.getUrl(), 0);
}
}
@@ -183,7 +184,7 @@ public void handleNextItems(final ListExtractor.InfoItemsPage result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(),
- UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId),
+ UserAction.REQUESTED_PLAYLIST, ServiceHelper.getNameOfServiceById(serviceId),
"Get next page of: " + url, 0);
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/playlist/PlaylistFragment.java
index 78e48d03a..33e7f40a2 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/playlist/PlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/playlist/PlaylistFragment.java
@@ -25,7 +25,6 @@
import org.schabi.newpipelegacy.database.playlist.model.PlaylistRemoteEntity;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
@@ -41,6 +40,7 @@
import org.schabi.newpipelegacy.util.ImageDisplayConstants;
import org.schabi.newpipelegacy.util.Localization;
import org.schabi.newpipelegacy.util.NavigationHelper;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import org.schabi.newpipelegacy.util.ShareUtils;
import org.schabi.newpipelegacy.util.StreamDialogEntry;
import org.schabi.newpipelegacy.util.ThemeHelper;
@@ -286,10 +286,8 @@ public void handleResult(@NonNull final PlaylistInfo result) {
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
headerUploaderLayout.setOnClickListener(v -> {
try {
- NavigationHelper.openChannelFragment(getFragmentManager(),
- result.getServiceId(),
- result.getUploaderUrl(),
- result.getUploaderName());
+ NavigationHelper.openChannelFragment(getFM(), result.getServiceId(),
+ result.getUploaderUrl(), result.getUploaderName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
@@ -308,7 +306,7 @@ public void handleResult(@NonNull final PlaylistInfo result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST,
- NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
+ ServiceHelper.getNameOfServiceById(result.getServiceId()), result.getUrl(), 0);
}
remotePlaylistManager.getPlaylist(result)
@@ -361,7 +359,7 @@ public void handleNextItems(final ListExtractor.InfoItemsPage result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST,
- NewPipe.getNameOfService(serviceId), "Get next page of: " + url, 0);
+ ServiceHelper.getNameOfServiceById(serviceId), "Get next page of: " + url, 0);
}
}
@@ -378,7 +376,7 @@ protected boolean onError(final Throwable exception) {
int errorId = exception instanceof ExtractionException
? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_PLAYLIST,
- NewPipe.getNameOfService(serviceId), url, errorId);
+ ServiceHelper.getNameOfServiceById(serviceId), url, errorId);
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/search/SearchFragment.java
index c2b1b4588..411026bcc 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/search/SearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/search/SearchFragment.java
@@ -43,6 +43,8 @@
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
+import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
+import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory;
import org.schabi.newpipelegacy.fragments.BackPressable;
import org.schabi.newpipelegacy.fragments.list.BaseListFragment;
import org.schabi.newpipelegacy.local.history.HistoryRecordManager;
@@ -51,6 +53,7 @@
import org.schabi.newpipelegacy.util.AndroidTvUtils;
import org.schabi.newpipelegacy.util.AnimationUtils;
import org.schabi.newpipelegacy.util.Constants;
+import org.schabi.newpipelegacy.util.ExceptionUtils;
import org.schabi.newpipelegacy.util.ExtractorHelper;
import org.schabi.newpipelegacy.util.NavigationHelper;
import org.schabi.newpipelegacy.util.ServiceHelper;
@@ -78,7 +81,7 @@
import static java.util.Arrays.asList;
import static org.schabi.newpipelegacy.util.AnimationUtils.animateView;
-public class SearchFragment extends BaseListFragment
+public class SearchFragment extends BaseListFragment>
implements BackPressable {
/*//////////////////////////////////////////////////////////////////////////
// Search
@@ -133,7 +136,6 @@ public class SearchFragment extends BaseListFragment menuItemToFilterName;
private StreamingService service;
private Page nextPage;
- private String contentCountry;
private boolean isSuggestionsEnabled = true;
private Disposable searchDisposable;
@@ -154,6 +156,7 @@ public class SearchFragment extends BaseListFragment 0
&& !isLoading.get()) {
hideSuggestionsPanel();
@@ -722,7 +722,7 @@ private void initSuggestionObserver() {
suggestionDisposable = observable
.switchMap(query -> {
final Flowable> flowable = historyRecordManager
- .getRelatedSearches(query, 3, 25);
+ .getRelatedSearches(query, 60, 80);
final Observable> local = flowable.toObservable()
.map(searchHistoryEntries -> {
List result = new ArrayList<>();
@@ -740,6 +740,13 @@ private void initSuggestionObserver() {
final Observable> network = ExtractorHelper
.suggestionsFor(serviceId, query)
+ .onErrorReturn(throwable -> {
+ if (!ExceptionUtils.isNetworkRelated(throwable)) {
+ showSnackBarError(throwable, UserAction.GET_SUGGESTIONS,
+ ServiceHelper.getNameOfServiceById(serviceId), searchString, 0);
+ }
+ return new ArrayList<>();
+ })
.toObservable()
.map(strings -> {
List result = new ArrayList<>();
@@ -789,28 +796,30 @@ protected void doInitialLoadLogic() {
// no-op
}
- private void search(final String ss, final String[] cf, final String sf) {
+ private void search(final String theSearchString,
+ final String[] theContentFilter,
+ final String theSortFilter) {
if (DEBUG) {
- Log.d(TAG, "search() called with: query = [" + ss + "]");
+ Log.d(TAG, "search() called with: query = [" + theSearchString + "]");
}
- if (ss.isEmpty()) {
+ if (theSearchString.isEmpty()) {
return;
}
try {
- final StreamingService streamingService = NewPipe.getServiceByUrl(ss);
+ final StreamingService streamingService = NewPipe.getServiceByUrl(theSearchString);
if (streamingService != null) {
showLoading();
disposables.add(Observable
- .fromCallable(() ->
- NavigationHelper.getIntentByLink(activity, streamingService, ss))
+ .fromCallable(() -> NavigationHelper.getIntentByLink(activity,
+ streamingService, theSearchString))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(intent -> {
- getFragmentManager().popBackStackImmediate();
+ getFM().popBackStackImmediate();
activity.startActivity(intent);
}, throwable ->
- showError(getString(R.string.url_not_supported_toast), false)));
+ showError(getString(R.string.unsupported_url), false)));
return;
}
} catch (Exception ignored) {
@@ -818,29 +827,27 @@ private void search(final String ss, final String[] cf, final String sf) {
}
lastSearchedString = this.searchString;
- this.searchString = ss;
+ this.searchString = theSearchString;
infoListAdapter.clearStreamItemList();
hideSuggestionsPanel();
hideKeyboardSearch();
- historyRecordManager.onSearched(serviceId, ss)
+ disposables.add(historyRecordManager.onSearched(serviceId, theSearchString)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> {
},
error -> showSnackBarError(error, UserAction.SEARCHED,
- NewPipe.getNameOfService(serviceId), ss, 0)
- );
- suggestionPublisher.onNext(ss);
+ ServiceHelper.getNameOfServiceById(serviceId), theSearchString, 0)
+ ));
+ suggestionPublisher.onNext(theSearchString);
startLoading(false);
}
@Override
public void startLoading(final boolean forceLoad) {
super.startLoading(forceLoad);
- if (disposables != null) {
- disposables.clear();
- }
+ disposables.clear();
if (searchDisposable != null) {
searchDisposable.dispose();
}
@@ -879,8 +886,7 @@ protected void loadMoreItems() {
@Override
protected boolean hasMoreItems() {
- // TODO: No way to tell if search has more items in the moment
- return true;
+ return Page.isValid(nextPage);
}
@Override
@@ -893,22 +899,25 @@ protected void onItemSelected(final InfoItem selectedItem) {
// Utils
//////////////////////////////////////////////////////////////////////////*/
- private void changeContentFilter(final MenuItem item, final List cf) {
- this.filterItemCheckedId = item.getItemId();
+ private void changeContentFilter(final MenuItem item, final List theContentFilter) {
+ filterItemCheckedId = item.getItemId();
item.setChecked(true);
- this.contentFilter = new String[]{cf.get(0)};
+ contentFilter = new String[]{theContentFilter.get(0)};
if (!TextUtils.isEmpty(searchString)) {
- search(searchString, this.contentFilter, sortFilter);
+ search(searchString, contentFilter, sortFilter);
}
}
- private void setQuery(final int sid, final String ss, final String[] cf, final String sf) {
- this.serviceId = sid;
- this.searchString = searchString;
- this.contentFilter = cf;
- this.sortFilter = sf;
+ private void setQuery(final int theServiceId,
+ final String theSearchString,
+ final String[] theContentFilter,
+ final String theSortFilter) {
+ serviceId = theServiceId;
+ searchString = theSearchString;
+ contentFilter = theContentFilter;
+ sortFilter = theSortFilter;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -922,7 +931,7 @@ public void handleSuggestions(@NonNull final List suggestions) {
suggestionsRecyclerView.smoothScrollToPosition(0);
suggestionsRecyclerView.post(() -> suggestionListAdapter.setItems(suggestions));
- if (errorPanelRoot.getVisibility() == View.VISIBLE) {
+ if (suggestionsPanelVisible && errorPanelRoot.getVisibility() == View.VISIBLE) {
hideLoading();
}
}
@@ -939,7 +948,7 @@ public void onSuggestionError(final Throwable exception) {
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS,
- NewPipe.getNameOfService(serviceId), searchString, errorId);
+ ServiceHelper.getNameOfServiceById(serviceId), searchString, errorId);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -970,7 +979,7 @@ public void handleResult(@NonNull final SearchInfo result) {
&& !(exceptions.size() == 1
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
- NewPipe.getNameOfService(serviceId), searchString, 0);
+ ServiceHelper.getNameOfServiceById(serviceId), searchString, 0);
}
searchSuggestion = result.getSearchSuggestion();
@@ -1026,14 +1035,14 @@ private void handleSearchSuggestion() {
}
@Override
- public void handleNextItems(final ListExtractor.InfoItemsPage result) {
+ public void handleNextItems(final ListExtractor.InfoItemsPage> result) {
showListFooter(false);
infoListAdapter.addInfoItemList(result.getItems());
nextPage = result.getNextPage();
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
- NewPipe.getNameOfService(serviceId),
+ ServiceHelper.getNameOfServiceById(serviceId),
"\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", "
+ "pageIds: " + nextPage.getIds() + ", "
+ "pageCookies: " + nextPage.getCookies(), 0);
@@ -1055,7 +1064,7 @@ protected boolean onError(final Throwable exception) {
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.SEARCHED,
- NewPipe.getNameOfService(serviceId), searchString, errorId);
+ ServiceHelper.getNameOfServiceById(serviceId), searchString, errorId);
}
return true;
@@ -1065,8 +1074,7 @@ protected boolean onError(final Throwable exception) {
// Suggestion item touch helper
//////////////////////////////////////////////////////////////////////////*/
- public int getSuggestionMovementFlags(@NonNull final RecyclerView recyclerView,
- @NonNull final RecyclerView.ViewHolder viewHolder) {
+ public int getSuggestionMovementFlags(@NonNull final RecyclerView.ViewHolder viewHolder) {
final int position = viewHolder.getAdapterPosition();
if (position == RecyclerView.NO_POSITION) {
return 0;
@@ -1077,8 +1085,7 @@ public int getSuggestionMovementFlags(@NonNull final RecyclerView recyclerView,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
}
- public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder,
- final int i) {
+ public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) {
final int position = viewHolder.getAdapterPosition();
final String query = suggestionListAdapter.getItem(position).query;
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
diff --git a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/videos/RelatedVideosFragment.java b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/videos/RelatedVideosFragment.java
index d37a5a953..4ef769f90 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/fragments/list/videos/RelatedVideosFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/fragments/list/videos/RelatedVideosFragment.java
@@ -16,12 +16,12 @@
import org.schabi.newpipelegacy.R;
import org.schabi.newpipe.extractor.ListExtractor;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipelegacy.fragments.list.BaseListInfoFragment;
import org.schabi.newpipelegacy.report.UserAction;
import org.schabi.newpipelegacy.util.AnimationUtils;
import org.schabi.newpipelegacy.util.RelatedStreamInfo;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import java.io.Serializable;
@@ -123,7 +123,7 @@ public void handleResult(@NonNull final RelatedStreamInfo result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_STREAM,
- NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
+ ServiceHelper.getNameOfServiceById(result.getServiceId()), result.getUrl(), 0);
}
if (disposables != null) {
@@ -138,7 +138,7 @@ public void handleNextItems(final ListExtractor.InfoItemsPage result) {
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(),
UserAction.REQUESTED_STREAM,
- NewPipe.getNameOfService(serviceId),
+ ServiceHelper.getNameOfServiceById(serviceId),
"Get next page of: " + url,
R.string.general_error);
}
@@ -156,7 +156,7 @@ protected boolean onError(final Throwable exception) {
hideLoading();
showSnackBarError(exception, UserAction.REQUESTED_STREAM,
- NewPipe.getNameOfService(serviceId), url, R.string.general_error);
+ ServiceHelper.getNameOfServiceById(serviceId), url, R.string.general_error);
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/CommentsInfoItemHolder.java b/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/CommentsInfoItemHolder.java
index f54af283f..ccb2436a9 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/CommentsInfoItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/CommentsInfoItemHolder.java
@@ -1,6 +1,8 @@
package org.schabi.newpipelegacy.info_list.holder;
+import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipelegacy.R;
@@ -31,11 +33,13 @@
public class CommentsInfoItemHolder extends CommentsMiniInfoItemHolder {
public final TextView itemTitleView;
+ private final ImageView itemHeartView;
public CommentsInfoItemHolder(final InfoItemBuilder infoItemBuilder, final ViewGroup parent) {
super(infoItemBuilder, R.layout.list_comments_item, parent);
itemTitleView = itemView.findViewById(R.id.itemTitleView);
+ itemHeartView = itemView.findViewById(R.id.detail_heart_image_view);
}
@Override
@@ -49,5 +53,7 @@ public void updateFromItem(final InfoItem infoItem,
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
itemTitleView.setText(item.getUploaderName());
+
+ itemHeartView.setVisibility(item.isHeartedByUploader() ? View.VISIBLE : View.GONE);
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/CommentsMiniInfoItemHolder.java
index c8e1d7e9c..705ee5c77 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/CommentsMiniInfoItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/CommentsMiniInfoItemHolder.java
@@ -8,6 +8,7 @@
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.text.util.LinkifyCompat;
import org.schabi.newpipelegacy.R;
import org.schabi.newpipe.extractor.InfoItem;
@@ -22,6 +23,7 @@
import org.schabi.newpipelegacy.util.NavigationHelper;
import org.schabi.newpipelegacy.util.ShareUtils;
+import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -95,7 +97,14 @@ public void updateFromItem(final InfoItem infoItem,
streamUrl = item.getUrl();
itemContentView.setLines(COMMENT_DEFAULT_LINES);
- commentText = item.getCommentText();
+ try {
+ commentText = item.getCommentText().getContent().replace(" ", "")
+ .replace("", " ")
+ .replace(" ", " ").replace("", " ")
+ .replace("", " ").replace(" ", " ");
+ } catch (Exception e) {
+ commentText = " ";
+ }
itemContentView.setText(commentText);
itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE);
diff --git a/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/StreamMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/StreamMiniInfoItemHolder.java
index 6159c1710..ed1d2d2c0 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/StreamMiniInfoItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/info_list/holder/StreamMiniInfoItemHolder.java
@@ -70,7 +70,8 @@ public void updateFromItem(final InfoItem infoItem,
} else {
itemProgressView.setVisibility(View.GONE);
}
- } else if (item.getStreamType() == StreamType.LIVE_STREAM) {
+ } else if (item.getStreamType() == StreamType.LIVE_STREAM
+ || item.getStreamType() == StreamType.AUDIO_LIVE_STREAM) {
itemDurationView.setText(R.string.duration_live);
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
R.color.live_duration_background_color));
@@ -100,7 +101,7 @@ public void updateFromItem(final InfoItem infoItem,
case AUDIO_LIVE_STREAM:
enableLongClick(item);
break;
- case FILE:
+ case POST_LIVE_AUDIO_STREAM:
case NONE:
default:
disableLongClick();
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedDatabaseManager.kt
index 7d41a4134..20c0a77d3 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedDatabaseManager.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedDatabaseManager.kt
@@ -7,8 +7,6 @@ import io.reactivex.Flowable
import io.reactivex.Maybe
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
-import java.util.Calendar
-import java.util.Date
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.extractor.stream.StreamType
import org.schabi.newpipelegacy.MainActivity.DEBUG
@@ -18,6 +16,8 @@ import org.schabi.newpipelegacy.database.feed.model.FeedGroupEntity
import org.schabi.newpipelegacy.database.feed.model.FeedLastUpdatedEntity
import org.schabi.newpipelegacy.database.stream.model.StreamEntity
import org.schabi.newpipelegacy.local.subscription.FeedGroupIcon
+import java.util.Calendar
+import java.util.Date
class FeedDatabaseManager(context: Context) {
private val database = NewPipeDatabase.getInstance(context)
@@ -65,10 +65,10 @@ class FeedDatabaseManager(context: Context) {
}
fun outdatedSubscriptionsForGroup(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, outdatedThreshold: Date) =
- feedTable.getAllOutdatedForGroup(groupId, outdatedThreshold)
+ feedTable.getAllOutdatedForGroup(groupId, outdatedThreshold)
fun markAsOutdated(subscriptionId: Long) = feedTable
- .setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, null))
+ .setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, null))
fun upsertAll(
subscriptionId: Long,
@@ -116,38 +116,38 @@ class FeedDatabaseManager(context: Context) {
fun subscriptionIdsForGroup(groupId: Long): Flowable> {
return feedGroupTable.getSubscriptionIdsFor(groupId)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
}
fun updateSubscriptionsForGroup(groupId: Long, subscriptionIds: List): Completable {
return Completable.fromCallable { feedGroupTable.updateSubscriptionsForGroup(groupId, subscriptionIds) }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
}
fun createGroup(name: String, icon: FeedGroupIcon): Maybe {
return Maybe.fromCallable { feedGroupTable.insert(FeedGroupEntity(0, name, icon)) }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
}
fun getGroup(groupId: Long): Maybe {
return feedGroupTable.getGroup(groupId)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
}
fun updateGroup(feedGroupEntity: FeedGroupEntity): Completable {
return Completable.fromCallable { feedGroupTable.update(feedGroupEntity) }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
}
fun deleteGroup(groupId: Long): Completable {
return Completable.fromCallable { feedGroupTable.delete(groupId) }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
}
fun updateGroupsOrder(groupIdList: List): Completable {
@@ -155,8 +155,8 @@ class FeedDatabaseManager(context: Context) {
val orderMap = groupIdList.associateBy({ it }, { index++ })
return Completable.fromCallable { feedGroupTable.updateOrder(orderMap) }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
}
fun oldestSubscriptionUpdate(groupId: Long): Flowable> {
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedFragment.kt
index 2f4018b29..406ee80b0 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedFragment.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedFragment.kt
@@ -32,8 +32,8 @@ import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.preference.PreferenceManager
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import icepick.State
-import java.util.Calendar
import kotlinx.android.synthetic.main.error_retry.error_button_retry
import kotlinx.android.synthetic.main.error_retry.error_message_view
import kotlinx.android.synthetic.main.fragment_feed.empty_state_view
@@ -51,9 +51,11 @@ import org.schabi.newpipelegacy.local.feed.service.FeedLoadService
import org.schabi.newpipelegacy.report.UserAction
import org.schabi.newpipelegacy.util.AnimationUtils.animateView
import org.schabi.newpipelegacy.util.Localization
+import java.util.Calendar
class FeedFragment : BaseListFragment() {
private lateinit var viewModel: FeedViewModel
+ private lateinit var swipeRefreshLayout: SwipeRefreshLayout
@State
@JvmField
var listState: Parcelable? = null
@@ -71,7 +73,7 @@ class FeedFragment : BaseListFragment() {
super.onCreate(savedInstanceState)
groupId = arguments?.getLong(KEY_GROUP_ID, FeedGroupEntity.GROUP_ALL_ID)
- ?: FeedGroupEntity.GROUP_ALL_ID
+ ?: FeedGroupEntity.GROUP_ALL_ID
groupName = arguments?.getString(KEY_GROUP_NAME) ?: ""
}
@@ -81,7 +83,8 @@ class FeedFragment : BaseListFragment() {
override fun onViewCreated(rootView: View, savedInstanceState: Bundle?) {
super.onViewCreated(rootView, savedInstanceState)
-
+ swipeRefreshLayout = requireView().findViewById(R.id.swiperefresh)
+ swipeRefreshLayout.setOnRefreshListener { reloadContent() }
viewModel = ViewModelProviders.of(this, FeedViewModel.Factory(requireContext(), groupId)).get(FeedViewModel::class.java)
viewModel.stateLiveData.observe(viewLifecycleOwner, Observer { it?.let(::handleResult) })
}
@@ -138,15 +141,15 @@ class FeedFragment : BaseListFragment() {
}
AlertDialog.Builder(requireContext())
- .setMessage(R.string.feed_use_dedicated_fetch_method_help_text)
- .setNeutralButton(enableDisableButtonText) { _, _ ->
- sharedPreferences.edit()
- .putBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), !usingDedicatedMethod)
- .apply()
- }
- .setPositiveButton(resources.getString(R.string.finish), null)
- .create()
- .show()
+ .setMessage(R.string.feed_use_dedicated_fetch_method_help_text)
+ .setNeutralButton(enableDisableButtonText) { _, _ ->
+ sharedPreferences.edit()
+ .putBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), !usingDedicatedMethod)
+ .apply()
+ }
+ .setPositiveButton(resources.getString(R.string.finish), null)
+ .create()
+ .show()
return true
}
@@ -176,6 +179,7 @@ class FeedFragment : BaseListFragment() {
empty_state_view?.let { animateView(it, false, 0) }
animateView(error_panel, false, 0)
+ swipeRefreshLayout.isRefreshing = false
}
override fun hideLoading() {
@@ -227,7 +231,7 @@ class FeedFragment : BaseListFragment() {
showLoading()
val isIndeterminate = progressState.currentProgress == -1 &&
- progressState.maxProgress == -1
+ progressState.maxProgress == -1
if (!isIndeterminate) {
loading_progress_text.text = "${progressState.currentProgress}/${progressState.maxProgress}"
@@ -238,7 +242,7 @@ class FeedFragment : BaseListFragment() {
}
loading_progress_bar.isIndeterminate = isIndeterminate ||
- (progressState.maxProgress > 0 && progressState.currentProgress == 0)
+ (progressState.maxProgress > 0 && progressState.currentProgress == 0)
loading_progress_bar.progress = progressState.currentProgress
loading_progress_bar.max = progressState.maxProgress
@@ -261,8 +265,10 @@ class FeedFragment : BaseListFragment() {
}
if (loadedState.itemsErrors.isNotEmpty()) {
- showSnackBarError(loadedState.itemsErrors, UserAction.REQUESTED_FEED,
- "none", "Loading feed", R.string.general_error)
+ showSnackBarError(
+ loadedState.itemsErrors, UserAction.REQUESTED_FEED,
+ "none", "Loading feed", R.string.general_error
+ )
}
if (loadedState.items.isEmpty()) {
@@ -305,9 +311,11 @@ class FeedFragment : BaseListFragment() {
override fun hasMoreItems() = false
private fun triggerUpdate() {
- getActivity()?.startService(Intent(requireContext(), FeedLoadService::class.java).apply {
- putExtra(FeedLoadService.EXTRA_GROUP_ID, groupId)
- })
+ getActivity()?.startService(
+ Intent(requireContext(), FeedLoadService::class.java).apply {
+ putExtra(FeedLoadService.EXTRA_GROUP_ID, groupId)
+ }
+ )
listState = null
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedState.kt b/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedState.kt
index a02f80265..683f1910a 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedState.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedState.kt
@@ -1,8 +1,8 @@
package org.schabi.newpipelegacy.local.feed
import androidx.annotation.StringRes
-import java.util.Calendar
import org.schabi.newpipe.extractor.stream.StreamInfoItem
+import java.util.Calendar
sealed class FeedState {
data class ProgressState(
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedViewModel.kt b/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedViewModel.kt
index 79736c5ec..c100a3857 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedViewModel.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/feed/FeedViewModel.kt
@@ -9,9 +9,6 @@ import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Function4
import io.reactivex.schedulers.Schedulers
-import java.util.Calendar
-import java.util.Date
-import java.util.concurrent.TimeUnit
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipelegacy.database.feed.model.FeedGroupEntity
import org.schabi.newpipelegacy.local.feed.service.FeedEventManager
@@ -20,6 +17,9 @@ import org.schabi.newpipelegacy.local.feed.service.FeedEventManager.Event.IdleEv
import org.schabi.newpipelegacy.local.feed.service.FeedEventManager.Event.ProgressEvent
import org.schabi.newpipelegacy.local.feed.service.FeedEventManager.Event.SuccessResultEvent
import org.schabi.newpipelegacy.util.DEFAULT_THROTTLE_TIMEOUT
+import java.util.Calendar
+import java.util.Date
+import java.util.concurrent.TimeUnit
class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModel() {
class Factory(val context: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModelProvider.Factory {
@@ -35,36 +35,38 @@ class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEn
val stateLiveData: LiveData = mutableStateLiveData
private var combineDisposable = Flowable
- .combineLatest(
- FeedEventManager.events(),
- feedDatabaseManager.asStreamItems(groupId),
- feedDatabaseManager.notLoadedCount(groupId),
- feedDatabaseManager.oldestSubscriptionUpdate(groupId),
+ .combineLatest(
+ FeedEventManager.events(),
+ feedDatabaseManager.asStreamItems(groupId),
+ feedDatabaseManager.notLoadedCount(groupId),
+ feedDatabaseManager.oldestSubscriptionUpdate(groupId),
- Function4 { t1: FeedEventManager.Event, t2: List, t3: Long, t4: List ->
- return@Function4 CombineResultHolder(t1, t2, t3, t4.firstOrNull())
- }
- )
- .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- val (event, listFromDB, notLoadedCount, oldestUpdate) = it
+ Function4 { t1: FeedEventManager.Event, t2: List, t3: Long, t4: List ->
+ return@Function4 CombineResultHolder(t1, t2, t3, t4.firstOrNull())
+ }
+ )
+ .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ val (event, listFromDB, notLoadedCount, oldestUpdate) = it
- val oldestUpdateCalendar =
- oldestUpdate?.let { Calendar.getInstance().apply { time = it } }
+ val oldestUpdateCalendar =
+ oldestUpdate?.let { Calendar.getInstance().apply { time = it } }
- mutableStateLiveData.postValue(when (event) {
+ mutableStateLiveData.postValue(
+ when (event) {
is IdleEvent -> FeedState.LoadedState(listFromDB, oldestUpdateCalendar, notLoadedCount)
is ProgressEvent -> FeedState.ProgressState(event.currentProgress, event.maxProgress, event.progressMessage)
is SuccessResultEvent -> FeedState.LoadedState(listFromDB, oldestUpdateCalendar, notLoadedCount, event.itemsErrors)
is ErrorResultEvent -> FeedState.ErrorState(event.error)
- })
-
- if (event is ErrorResultEvent || event is SuccessResultEvent) {
- FeedEventManager.reset()
}
+ )
+
+ if (event is ErrorResultEvent || event is SuccessResultEvent) {
+ FeedEventManager.reset()
}
+ }
override fun onCleared() {
super.onCleared()
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/feed/service/FeedEventManager.kt b/app/src/main/java/org/schabi/newpipelegacy/local/feed/service/FeedEventManager.kt
index cb8831249..fd0235436 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/feed/service/FeedEventManager.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/feed/service/FeedEventManager.kt
@@ -3,8 +3,8 @@ package org.schabi.newpipelegacy.local.feed.service
import androidx.annotation.StringRes
import io.reactivex.Flowable
import io.reactivex.processors.BehaviorProcessor
-import java.util.concurrent.atomic.AtomicBoolean
import org.schabi.newpipelegacy.local.feed.service.FeedEventManager.Event.IdleEvent
+import java.util.concurrent.atomic.AtomicBoolean
object FeedEventManager {
private var processor: BehaviorProcessor = BehaviorProcessor.create()
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/feed/service/FeedLoadService.kt b/app/src/main/java/org/schabi/newpipelegacy/local/feed/service/FeedLoadService.kt
index 72e601be8..72ebdd682 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/feed/service/FeedLoadService.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/feed/service/FeedLoadService.kt
@@ -40,16 +40,12 @@ import io.reactivex.functions.Consumer
import io.reactivex.functions.Function
import io.reactivex.processors.PublishProcessor
import io.reactivex.schedulers.Schedulers
-import java.io.IOException
-import java.util.Calendar
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.atomic.AtomicBoolean
-import java.util.concurrent.atomic.AtomicInteger
import org.reactivestreams.Subscriber
import org.reactivestreams.Subscription
import org.schabi.newpipe.extractor.ListInfo
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
import org.schabi.newpipe.extractor.stream.StreamInfoItem
+import org.schabi.newpipelegacy.App
import org.schabi.newpipelegacy.MainActivity.DEBUG
import org.schabi.newpipelegacy.R
import org.schabi.newpipelegacy.database.feed.model.FeedGroupEntity
@@ -62,12 +58,17 @@ import org.schabi.newpipelegacy.local.feed.service.FeedEventManager.postEvent
import org.schabi.newpipelegacy.local.subscription.SubscriptionManager
import org.schabi.newpipelegacy.util.ExceptionUtils
import org.schabi.newpipelegacy.util.ExtractorHelper
+import java.io.IOException
+import java.util.Calendar
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
class FeedLoadService : Service() {
companion object {
private val TAG = FeedLoadService::class.java.simpleName
private const val NOTIFICATION_ID = 7293450
- private const val ACTION_CANCEL = "org.schabi.newpipe.local.feed.service.FeedLoadService.CANCEL"
+ private const val ACTION_CANCEL = App.PACKAGE_NAME + ".local.feed.service.FeedLoadService.CANCEL"
/**
* How often the notification will be updated.
@@ -108,8 +109,11 @@ class FeedLoadService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (DEBUG) {
- Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "]," +
- " flags = [" + flags + "], startId = [" + startId + "]")
+ Log.d(
+ TAG,
+ "onStartCommand() called with: intent = [" + intent + "]," +
+ " flags = [" + flags + "], startId = [" + startId + "]"
+ )
}
if (intent == null || loadingSubscription != null) {
@@ -122,10 +126,10 @@ class FeedLoadService : Service() {
val groupId = intent.getLongExtra(EXTRA_GROUP_ID, FeedGroupEntity.GROUP_ALL_ID)
val useFeedExtractor = defaultSharedPreferences
- .getBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), false)
+ .getBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), false)
val thresholdOutdatedSecondsString = defaultSharedPreferences
- .getString(getString(R.string.feed_update_threshold_key), getString(R.string.feed_update_threshold_default_value))
+ .getString(getString(R.string.feed_update_threshold_key), getString(R.string.feed_update_threshold_default_value))
val thresholdOutdatedSeconds = thresholdOutdatedSecondsString!!.toInt()
startLoading(groupId, useFeedExtractor, thresholdOutdatedSeconds)
@@ -182,63 +186,63 @@ class FeedLoadService : Service() {
}
subscriptions
- .limit(1)
+ .limit(1)
- .doOnNext {
- currentProgress.set(0)
- maxProgress.set(it.size)
- }
- .filter { it.isNotEmpty() }
+ .doOnNext {
+ currentProgress.set(0)
+ maxProgress.set(it.size)
+ }
+ .filter { it.isNotEmpty() }
- .observeOn(AndroidSchedulers.mainThread())
- .doOnNext {
- startForeground(NOTIFICATION_ID, notificationBuilder.build())
- updateNotificationProgress(null)
- broadcastProgress()
- }
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnNext {
+ startForeground(NOTIFICATION_ID, notificationBuilder.build())
+ updateNotificationProgress(null)
+ broadcastProgress()
+ }
- .observeOn(Schedulers.io())
- .flatMap { Flowable.fromIterable(it) }
- .takeWhile { !cancelSignal.get() }
-
- .parallel(PARALLEL_EXTRACTIONS, PARALLEL_EXTRACTIONS * 2)
- .runOn(Schedulers.io(), PARALLEL_EXTRACTIONS * 2)
- .filter { !cancelSignal.get() }
-
- .map { subscriptionEntity ->
- try {
- val listInfo = if (useFeedExtractor) {
- ExtractorHelper
- .getFeedInfoFallbackToChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url)
- .blockingGet()
- } else {
- ExtractorHelper
- .getChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url, true)
- .blockingGet()
- } as ListInfo
-
- return@map Notification.createOnNext(Pair(subscriptionEntity.uid, listInfo))
- } catch (e: Throwable) {
- val request = "${subscriptionEntity.serviceId}:${subscriptionEntity.url}"
- val wrapper = RequestException(subscriptionEntity.uid, request, e)
- return@map Notification.createOnError>>(wrapper)
- }
+ .observeOn(Schedulers.io())
+ .flatMap { Flowable.fromIterable(it) }
+ .takeWhile { !cancelSignal.get() }
+
+ .parallel(PARALLEL_EXTRACTIONS, PARALLEL_EXTRACTIONS * 2)
+ .runOn(Schedulers.io(), PARALLEL_EXTRACTIONS * 2)
+ .filter { !cancelSignal.get() }
+
+ .map { subscriptionEntity ->
+ try {
+ val listInfo = if (useFeedExtractor) {
+ ExtractorHelper
+ .getFeedInfoFallbackToChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url)
+ .blockingGet()
+ } else {
+ ExtractorHelper
+ .getChannelInfo(subscriptionEntity.serviceId, subscriptionEntity.url, true)
+ .blockingGet()
+ } as ListInfo
+
+ return@map Notification.createOnNext(Pair(subscriptionEntity.uid, listInfo))
+ } catch (e: Throwable) {
+ val request = "${subscriptionEntity.serviceId}:${subscriptionEntity.url}"
+ val wrapper = RequestException(subscriptionEntity.uid, request, e)
+ return@map Notification.createOnError>>(wrapper)
}
- .sequential()
+ }
+ .sequential()
- .observeOn(AndroidSchedulers.mainThread())
- .doOnNext(errorHandlingConsumer)
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnNext(errorHandlingConsumer)
- .observeOn(AndroidSchedulers.mainThread())
- .doOnNext(notificationsConsumer)
+ .observeOn(AndroidSchedulers.mainThread())
+ .doOnNext(notificationsConsumer)
- .observeOn(Schedulers.io())
- .buffer(BUFFER_COUNT_BEFORE_INSERT)
- .doOnNext(databaseConsumer)
+ .observeOn(Schedulers.io())
+ .buffer(BUFFER_COUNT_BEFORE_INSERT)
+ .doOnNext(databaseConsumer)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(resultSubscriber)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(resultSubscriber)
}
private fun broadcastProgress() {
@@ -275,7 +279,8 @@ class FeedLoadService : Service() {
notificationUpdater.onNext(getString(R.string.feed_processing_message))
postEvent(ProgressEvent(R.string.feed_processing_message))
- disposables.add(Single
+ disposables.add(
+ Single
.fromCallable {
feedResultsHolder.ready()
@@ -294,7 +299,8 @@ class FeedLoadService : Service() {
return@subscribe
}
stopService()
- })
+ }
+ )
}
}
@@ -365,16 +371,18 @@ class FeedLoadService : Service() {
private var maxProgress = AtomicInteger(-1)
private fun createNotification(): NotificationCompat.Builder {
- val cancelActionIntent = PendingIntent.getBroadcast(this,
- NOTIFICATION_ID, Intent(ACTION_CANCEL), 0)
+ val cancelActionIntent = PendingIntent.getBroadcast(
+ this,
+ NOTIFICATION_ID, Intent(ACTION_CANCEL), 0
+ )
return NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
- .setOngoing(true)
- .setProgress(-1, -1, true)
- .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .addAction(0, getString(R.string.cancel), cancelActionIntent)
- .setContentTitle(getString(R.string.feed_notification_loading))
+ .setOngoing(true)
+ .setProgress(-1, -1, true)
+ .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .addAction(0, getString(R.string.cancel), cancelActionIntent)
+ .setContentTitle(getString(R.string.feed_notification_loading))
}
private fun setupNotification() {
@@ -385,10 +393,12 @@ class FeedLoadService : Service() {
flow.limit(1).concatWith(flow.skip(1).throttleLatest(NOTIFICATION_SAMPLING_PERIOD.toLong(), TimeUnit.MILLISECONDS))
}
- disposables.add(notificationUpdater
+ disposables.add(
+ notificationUpdater
.publish(throttleAfterFirstEmission)
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(this::updateNotificationProgress))
+ .subscribe(this::updateNotificationProgress)
+ )
}
private fun updateNotificationProgress(updateDescription: String?) {
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/holder/LocalPlaylistStreamItemHolder.java b/app/src/main/java/org/schabi/newpipelegacy/local/holder/LocalPlaylistStreamItemHolder.java
index 8441805bf..56166b42b 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/holder/LocalPlaylistStreamItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/holder/LocalPlaylistStreamItemHolder.java
@@ -11,17 +11,15 @@
import org.schabi.newpipelegacy.R;
import org.schabi.newpipelegacy.database.LocalItem;
import org.schabi.newpipelegacy.database.playlist.PlaylistStreamEntry;
-import org.schabi.newpipelegacy.database.stream.model.StreamStateEntity;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipelegacy.local.LocalItemBuilder;
import org.schabi.newpipelegacy.local.history.HistoryRecordManager;
import org.schabi.newpipelegacy.util.AnimationUtils;
import org.schabi.newpipelegacy.util.ImageDisplayConstants;
import org.schabi.newpipelegacy.util.Localization;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import org.schabi.newpipelegacy.views.AnimatedProgressBar;
import java.text.DateFormat;
-import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
@@ -61,7 +59,7 @@ public void updateFromItem(final LocalItem localItem,
itemVideoTitleView.setText(item.getStreamEntity().getTitle());
itemAdditionalDetailsView.setText(Localization
.concatenateStrings(item.getStreamEntity().getUploader(),
- NewPipe.getNameOfService(item.getStreamEntity().getServiceId())));
+ ServiceHelper.getNameOfServiceById(item.getStreamEntity().getServiceId())));
if (item.getStreamEntity().getDuration() > 0) {
itemDurationView.setText(Localization
@@ -70,15 +68,11 @@ public void updateFromItem(final LocalItem localItem,
R.color.duration_background_color));
itemDurationView.setVisibility(View.VISIBLE);
- StreamStateEntity state = historyRecordManager
- .loadLocalStreamStateBatch(new ArrayList() {{
- add(localItem);
- }}).blockingGet().get(0);
- if (state != null) {
+ if (item.getProgressTime() > 0) {
itemProgressView.setVisibility(View.VISIBLE);
itemProgressView.setMax((int) item.getStreamEntity().getDuration());
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS
- .toSeconds(state.getProgressTime()));
+ .toSeconds(item.getProgressTime()));
} else {
itemProgressView.setVisibility(View.GONE);
}
@@ -116,18 +110,14 @@ public void updateState(final LocalItem localItem,
}
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
- StreamStateEntity state = historyRecordManager
- .loadLocalStreamStateBatch(new ArrayList() {{
- add(localItem);
- }}).blockingGet().get(0);
- if (state != null && item.getStreamEntity().getDuration() > 0) {
+ if (item.getProgressTime() > 0 && item.getStreamEntity().getDuration() > 0) {
itemProgressView.setMax((int) item.getStreamEntity().getDuration());
if (itemProgressView.getVisibility() == View.VISIBLE) {
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS
- .toSeconds(state.getProgressTime()));
+ .toSeconds(item.getProgressTime()));
} else {
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS
- .toSeconds(state.getProgressTime()));
+ .toSeconds(item.getProgressTime()));
AnimationUtils.animateView(itemProgressView, true, 500);
}
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/holder/LocalStatisticStreamItemHolder.java b/app/src/main/java/org/schabi/newpipelegacy/local/holder/LocalStatisticStreamItemHolder.java
index 33d3e2682..f373277ad 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/holder/LocalStatisticStreamItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/holder/LocalStatisticStreamItemHolder.java
@@ -11,17 +11,15 @@
import org.schabi.newpipelegacy.R;
import org.schabi.newpipelegacy.database.LocalItem;
import org.schabi.newpipelegacy.database.stream.StreamStatisticsEntry;
-import org.schabi.newpipelegacy.database.stream.model.StreamStateEntity;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipelegacy.local.LocalItemBuilder;
import org.schabi.newpipelegacy.local.history.HistoryRecordManager;
import org.schabi.newpipelegacy.util.AnimationUtils;
import org.schabi.newpipelegacy.util.ImageDisplayConstants;
import org.schabi.newpipelegacy.util.Localization;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import org.schabi.newpipelegacy.views.AnimatedProgressBar;
import java.text.DateFormat;
-import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
/*
@@ -75,7 +73,9 @@ private String getStreamInfoDetailLine(final StreamStatisticsEntry entry,
final String watchCount = Localization
.shortViewCount(itemBuilder.getContext(), entry.getWatchCount());
final String uploadDate = dateFormat.format(entry.getLatestAccessDate());
- final String serviceName = NewPipe.getNameOfService(entry.getStreamEntity().getServiceId());
+ final String serviceName = ServiceHelper.getNameOfServiceById(
+ entry.getStreamEntity().getServiceId()
+ );
return Localization.concatenateStrings(watchCount, uploadDate, serviceName);
}
@@ -98,15 +98,11 @@ public void updateFromItem(final LocalItem localItem,
R.color.duration_background_color));
itemDurationView.setVisibility(View.VISIBLE);
- StreamStateEntity state = historyRecordManager
- .loadLocalStreamStateBatch(new ArrayList() {{
- add(localItem);
- }}).blockingGet().get(0);
- if (state != null) {
+ if (item.getProgressTime() > 0) {
itemProgressView.setVisibility(View.VISIBLE);
itemProgressView.setMax((int) item.getStreamEntity().getDuration());
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS
- .toSeconds(state.getProgressTime()));
+ .toSeconds(item.getProgressTime()));
} else {
itemProgressView.setVisibility(View.GONE);
}
@@ -146,18 +142,14 @@ public void updateState(final LocalItem localItem,
}
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
- StreamStateEntity state = historyRecordManager
- .loadLocalStreamStateBatch(new ArrayList() {{
- add(localItem);
- }}).blockingGet().get(0);
- if (state != null && item.getStreamEntity().getDuration() > 0) {
+ if (item.getProgressTime() > 0 && item.getStreamEntity().getDuration() > 0) {
itemProgressView.setMax((int) item.getStreamEntity().getDuration());
if (itemProgressView.getVisibility() == View.VISIBLE) {
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS
- .toSeconds(state.getProgressTime()));
+ .toSeconds(item.getProgressTime()));
} else {
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS
- .toSeconds(state.getProgressTime()));
+ .toSeconds(item.getProgressTime()));
AnimationUtils.animateView(itemProgressView, true, 500);
}
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipelegacy/local/holder/RemotePlaylistItemHolder.java
index bb09ff659..003ba7df8 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/holder/RemotePlaylistItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/holder/RemotePlaylistItemHolder.java
@@ -5,11 +5,11 @@
import org.schabi.newpipelegacy.database.LocalItem;
import org.schabi.newpipelegacy.database.playlist.model.PlaylistRemoteEntity;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipelegacy.local.LocalItemBuilder;
import org.schabi.newpipelegacy.local.history.HistoryRecordManager;
import org.schabi.newpipelegacy.util.ImageDisplayConstants;
import org.schabi.newpipelegacy.util.Localization;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import java.text.DateFormat;
@@ -39,9 +39,9 @@ public void updateFromItem(final LocalItem localItem,
// Here is where the uploader name is set in the bookmarked playlists library
if (!TextUtils.isEmpty(item.getUploader())) {
itemUploaderView.setText(Localization.concatenateStrings(item.getUploader(),
- NewPipe.getNameOfService(item.getServiceId())));
+ ServiceHelper.getNameOfServiceById(item.getServiceId())));
} else {
- itemUploaderView.setText(NewPipe.getNameOfService(item.getServiceId()));
+ itemUploaderView.setText(ServiceHelper.getNameOfServiceById(item.getServiceId()));
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipelegacy/local/playlist/LocalPlaylistFragment.java
index a6d8e6a5f..f162710fa 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/playlist/LocalPlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/playlist/LocalPlaylistFragment.java
@@ -350,31 +350,32 @@ public void onError(final Throwable exception) {
}
@Override
- public void onComplete() { }
+ public void onComplete() {
+ }
};
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_item_remove_watched:
- if (!isRemovingWatched) {
- new AlertDialog.Builder(requireContext())
- .setMessage(R.string.remove_watched_popup_warning)
- .setTitle(R.string.remove_watched_popup_title)
- .setPositiveButton(R.string.yes,
- (DialogInterface d, int id) -> removeWatchedStreams(false))
- .setNeutralButton(
- R.string.remove_watched_popup_yes_and_partially_watched_videos,
- (DialogInterface d, int id) -> removeWatchedStreams(true))
- .setNegativeButton(R.string.cancel,
- (DialogInterface d, int id) -> d.cancel())
- .create()
- .show();
- }
- break;
- default:
- return super.onOptionsItemSelected(item);
+ if (item.getItemId() == R.id.menu_item_remove_watched) {
+ if (!isRemovingWatched) {
+ new AlertDialog.Builder(requireContext())
+ .setMessage(R.string.remove_watched_popup_warning)
+ .setTitle(R.string.remove_watched_popup_title)
+ .setPositiveButton(R.string.yes,
+ (DialogInterface d, int id) -> removeWatchedStreams(false))
+ .setNeutralButton(
+ R.string.remove_watched_popup_yes_and_partially_watched_videos,
+ (DialogInterface d, int id) -> removeWatchedStreams(true))
+ .setNegativeButton(R.string.cancel,
+ (DialogInterface d, int id) -> d.cancel())
+ .create()
+ .show();
+ }
+ } else if (item.getItemId() == R.id.menu_item_rename_playlist) {
+ createRenameDialog();
+ } else {
+ return super.onOptionsItemSelected(item);
}
return true;
}
@@ -432,7 +433,7 @@ public void removeWatchedStreams(final boolean removePartiallyWatched) {
playlistItem.getStreamId());
final boolean hasState = streamStatesIter.next() != null;
- if (indexInHistory < 0 || hasState) {
+ if (indexInHistory < 0 || hasState) {
notWatchedItems.add(playlistItem);
} else if (!thumbnailVideoRemoved
&& playlistManager.getPlaylistThumbnail(playlistId)
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionFragment.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionFragment.kt
index 74d8e5de9..d9645d32d 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionFragment.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionFragment.kt
@@ -29,12 +29,6 @@ import com.xwray.groupie.Section
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import icepick.State
import io.reactivex.disposables.CompositeDisposable
-import java.io.File
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-import kotlin.math.floor
-import kotlin.math.max
import kotlinx.android.synthetic.main.dialog_title.view.itemAdditionalDetails
import kotlinx.android.synthetic.main.dialog_title.view.itemTitleView
import kotlinx.android.synthetic.main.fragment_subscription.items_list
@@ -68,6 +62,12 @@ import org.schabi.newpipelegacy.util.NavigationHelper
import org.schabi.newpipelegacy.util.OnClickGesture
import org.schabi.newpipelegacy.util.ShareUtils
import org.schabi.newpipelegacy.util.ThemeHelper
+import java.io.File
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import kotlin.math.floor
+import kotlin.math.max
class SubscriptionFragment : BaseStateFragment() {
private lateinit var viewModel: SubscriptionViewModel
@@ -208,14 +208,19 @@ class SubscriptionFragment : BaseStateFragment() {
if (!exportFile.parentFile.canWrite() || !exportFile.parentFile.canRead()) {
Toast.makeText(activity, R.string.invalid_directory, Toast.LENGTH_SHORT).show()
} else {
- activity.startService(Intent(activity, SubscriptionsExportService::class.java)
- .putExtra(KEY_FILE_PATH, exportFile.absolutePath))
+ activity.startService(
+ Intent(activity, SubscriptionsExportService::class.java)
+ .putExtra(KEY_FILE_PATH, exportFile.absolutePath)
+ )
}
} else if (requestCode == REQUEST_IMPORT_CODE) {
val path = Utils.getFileForUri(data.data!!).absolutePath
- ImportConfirmationDialog.show(this, Intent(activity, SubscriptionsImportService::class.java)
+ ImportConfirmationDialog.show(
+ this,
+ Intent(activity, SubscriptionsImportService::class.java)
.putExtra(KEY_MODE, PREVIOUS_EXPORT_MODE)
- .putExtra(KEY_VALUE, path))
+ .putExtra(KEY_VALUE, path)
+ )
}
}
}
@@ -247,9 +252,9 @@ class SubscriptionFragment : BaseStateFragment() {
feedGroupsCarousel = FeedGroupCarouselItem(requireContext(), carouselAdapter)
feedGroupsSortMenuItem = HeaderWithMenuItem(
- getString(R.string.feed_groups_header_title),
- ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_sort),
- menuItemOnClickListener = ::openReorderDialog
+ getString(R.string.feed_groups_header_title),
+ ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_sort),
+ menuItemOnClickListener = ::openReorderDialog
)
add(Section(feedGroupsSortMenuItem, listOf(feedGroupsCarousel)))
@@ -260,10 +265,11 @@ class SubscriptionFragment : BaseStateFragment() {
subscriptionsSection.setHideWhenEmpty(true)
importExportItem = FeedImportExportItem(
- { onImportPreviousSelected() },
- { onImportFromServiceSelected(it) },
- { onExportSelected() },
- importExportItemExpandedState ?: false)
+ { onImportPreviousSelected() },
+ { onImportFromServiceSelected(it) },
+ { onExportSelected() },
+ importExportItemExpandedState ?: false
+ )
groupAdapter.add(Section(importExportItem, listOf(subscriptionsSection)))
}
@@ -284,8 +290,8 @@ class SubscriptionFragment : BaseStateFragment() {
private fun showLongTapDialog(selectedItem: ChannelInfoItem) {
val commands = arrayOf(
- getString(R.string.share),
- getString(R.string.unsubscribe)
+ getString(R.string.share),
+ getString(R.string.unsubscribe)
)
val actions = DialogInterface.OnClickListener { _, i ->
@@ -301,16 +307,18 @@ class SubscriptionFragment : BaseStateFragment() {
bannerView.itemAdditionalDetails.visibility = View.GONE
AlertDialog.Builder(requireContext())
- .setCustomTitle(bannerView)
- .setItems(commands, actions)
- .create()
- .show()
+ .setCustomTitle(bannerView)
+ .setItems(commands, actions)
+ .create()
+ .show()
}
private fun deleteChannel(selectedItem: ChannelInfoItem) {
- disposables.add(subscriptionManager.deleteSubscription(selectedItem.serviceId, selectedItem.url).subscribe {
- Toast.makeText(requireContext(), getString(R.string.channel_unsubscribed), Toast.LENGTH_SHORT).show()
- })
+ disposables.add(
+ subscriptionManager.deleteSubscription(selectedItem.serviceId, selectedItem.url).subscribe {
+ Toast.makeText(requireContext(), getString(R.string.channel_unsubscribed), Toast.LENGTH_SHORT).show()
+ }
+ )
}
override fun doInitialLoadLogic() = Unit
@@ -332,8 +340,10 @@ class SubscriptionFragment : BaseStateFragment() {
}
private val listenerChannelItem = object : OnClickGesture() {
- override fun selected(selectedItem: ChannelInfoItem) = NavigationHelper.openChannelFragment(fm,
- selectedItem.serviceId, selectedItem.url, selectedItem.name)
+ override fun selected(selectedItem: ChannelInfoItem) = NavigationHelper.openChannelFragment(
+ fm,
+ selectedItem.serviceId, selectedItem.url, selectedItem.name
+ )
override fun held(selectedItem: ChannelInfoItem) = showLongTapDialog(selectedItem)
}
@@ -420,14 +430,16 @@ class SubscriptionFragment : BaseStateFragment() {
private fun shouldUseGridLayout(): Boolean {
val listMode = PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value))
+ .getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value))
return when (listMode) {
getString(R.string.list_view_mode_auto_key) -> {
val configuration = resources.configuration
- (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE &&
- configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE))
+ (
+ configuration.orientation == Configuration.ORIENTATION_LANDSCAPE &&
+ configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE)
+ )
}
getString(R.string.list_view_mode_grid_key) -> true
else -> false
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionManager.kt
index 643a521c7..61edeba54 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionManager.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionManager.kt
@@ -32,7 +32,8 @@ class SubscriptionManager(context: Context) {
filterQuery.isNotEmpty() -> {
return if (showOnlyUngrouped) {
subscriptionTable.getSubscriptionsOnlyUngroupedFiltered(
- currentGroupId, filterQuery)
+ currentGroupId, filterQuery
+ )
} else {
subscriptionTable.getSubscriptionsFiltered(filterQuery)
}
@@ -44,7 +45,8 @@ class SubscriptionManager(context: Context) {
fun upsertAll(infoList: List): List {
val listEntities = subscriptionTable.upsertAll(
- infoList.map { SubscriptionEntity.from(it) })
+ infoList.map { SubscriptionEntity.from(it) }
+ )
database.runInTransaction {
infoList.forEachIndexed { index, info ->
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionViewModel.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionViewModel.kt
index 3bf7ef918..44c2d763b 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionViewModel.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionViewModel.kt
@@ -6,11 +6,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.xwray.groupie.Group
import io.reactivex.schedulers.Schedulers
-import java.util.concurrent.TimeUnit
import org.schabi.newpipelegacy.local.feed.FeedDatabaseManager
import org.schabi.newpipelegacy.local.subscription.item.ChannelItem
import org.schabi.newpipelegacy.local.subscription.item.FeedGroupCardItem
import org.schabi.newpipelegacy.util.DEFAULT_THROTTLE_TIMEOUT
+import java.util.concurrent.TimeUnit
class SubscriptionViewModel(application: Application) : AndroidViewModel(application) {
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(application)
@@ -22,22 +22,22 @@ class SubscriptionViewModel(application: Application) : AndroidViewModel(applica
val feedGroupsLiveData: LiveData> = mutableFeedGroupsLiveData
private var feedGroupItemsDisposable = feedDatabaseManager.groups()
- .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
- .map { it.map(::FeedGroupCardItem) }
- .subscribeOn(Schedulers.io())
- .subscribe(
- { mutableFeedGroupsLiveData.postValue(it) },
- { mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
- )
+ .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
+ .map { it.map(::FeedGroupCardItem) }
+ .subscribeOn(Schedulers.io())
+ .subscribe(
+ { mutableFeedGroupsLiveData.postValue(it) },
+ { mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
+ )
private var stateItemsDisposable = subscriptionManager.subscriptions()
- .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
- .map { it.map { entity -> ChannelItem(entity.toChannelInfoItem(), entity.uid, ChannelItem.ItemVersion.MINI) } }
- .subscribeOn(Schedulers.io())
- .subscribe(
- { mutableStateLiveData.postValue(SubscriptionState.LoadedState(it)) },
- { mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
- )
+ .throttleLatest(DEFAULT_THROTTLE_TIMEOUT, TimeUnit.MILLISECONDS)
+ .map { it.map { entity -> ChannelItem(entity.toChannelInfoItem(), entity.uid, ChannelItem.ItemVersion.MINI) } }
+ .subscribeOn(Schedulers.io())
+ .subscribe(
+ { mutableStateLiveData.postValue(SubscriptionState.LoadedState(it)) },
+ { mutableStateLiveData.postValue(SubscriptionState.ErrorState(it)) }
+ )
override fun onCleared() {
super.onCleared()
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionsImportFragment.java b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionsImportFragment.java
index bff85f6e4..49d7d270e 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionsImportFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/SubscriptionsImportFragment.java
@@ -85,7 +85,7 @@ public void onCreate(final Bundle savedInstanceState) {
if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) {
ErrorActivity.reportError(activity, Collections.emptyList(), null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE,
- NewPipe.getNameOfService(currentServiceId),
+ ServiceHelper.getNameOfServiceById(currentServiceId),
"Service don't support importing", R.string.general_error));
activity.finish();
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupDialog.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupDialog.kt
index 775599e6c..847cc1723 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupDialog.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupDialog.kt
@@ -23,8 +23,6 @@ import com.xwray.groupie.Section
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import icepick.Icepick
import icepick.State
-import java.io.Serializable
-import kotlin.collections.contains
import kotlinx.android.synthetic.main.dialog_feed_group_create.*
import kotlinx.android.synthetic.main.toolbar_search_layout.*
import org.schabi.newpipelegacy.R
@@ -42,6 +40,8 @@ import org.schabi.newpipelegacy.local.subscription.item.PickerIconItem
import org.schabi.newpipelegacy.local.subscription.item.PickerSubscriptionItem
import org.schabi.newpipelegacy.util.AndroidTvUtils
import org.schabi.newpipelegacy.util.ThemeHelper
+import java.io.Serializable
+import kotlin.collections.contains
class FeedGroupDialog : DialogFragment(), BackPressable {
private lateinit var viewModel: FeedGroupDialogViewModel
@@ -115,21 +115,30 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- viewModel = ViewModelProvider(this,
- FeedGroupDialogViewModel.Factory(requireContext(),
- groupId, subscriptionsCurrentSearchQuery, subscriptionsShowOnlyUngrouped)
+ viewModel = ViewModelProvider(
+ this,
+ FeedGroupDialogViewModel.Factory(
+ requireContext(),
+ groupId, subscriptionsCurrentSearchQuery, subscriptionsShowOnlyUngrouped
+ )
).get(FeedGroupDialogViewModel::class.java)
viewModel.groupLiveData.observe(viewLifecycleOwner, Observer(::handleGroup))
- viewModel.subscriptionsLiveData.observe(viewLifecycleOwner, Observer {
- setupSubscriptionPicker(it.first, it.second)
- })
- viewModel.dialogEventLiveData.observe(viewLifecycleOwner, Observer {
- when (it) {
- ProcessingEvent -> disableInput()
- SuccessEvent -> dismiss()
+ viewModel.subscriptionsLiveData.observe(
+ viewLifecycleOwner,
+ Observer {
+ setupSubscriptionPicker(it.first, it.second)
}
- })
+ )
+ viewModel.dialogEventLiveData.observe(
+ viewLifecycleOwner,
+ Observer {
+ when (it) {
+ ProcessingEvent -> disableInput()
+ SuccessEvent -> dismiss()
+ }
+ }
+ )
subscriptionGroupAdapter = GroupAdapter().apply {
add(subscriptionMainSection)
@@ -140,8 +149,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
// Disable animations, too distracting.
itemAnimator = null
adapter = subscriptionGroupAdapter
- layoutManager = GridLayoutManager(requireContext(), subscriptionGroupAdapter.spanCount,
- RecyclerView.VERTICAL, false).apply {
+ layoutManager = GridLayoutManager(
+ requireContext(), subscriptionGroupAdapter.spanCount,
+ RecyclerView.VERTICAL, false
+ ).apply {
spanSizeLookup = subscriptionGroupAdapter.spanSizeLookup
}
}
@@ -354,7 +365,8 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
val selectedCount = this.selectedSubscriptions.size
val selectedCountText = resources.getQuantityString(
R.plurals.feed_group_dialog_selection_count,
- selectedCount, selectedCount)
+ selectedCount, selectedCount
+ )
selected_subscription_count_view.text = selectedCountText
subscriptions_header_info.text = selectedCountText
}
@@ -409,10 +421,12 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
separator.onlyVisibleIn(SubscriptionsPickerScreen, IconPickerScreen)
cancel_button.onlyVisibleIn(InitialScreen, DeleteScreen)
- confirm_button.setText(when {
- currentScreen == InitialScreen && groupId == NO_GROUP_SELECTED -> R.string.create
- else -> android.R.string.ok
- })
+ confirm_button.setText(
+ when {
+ currentScreen == InitialScreen && groupId == NO_GROUP_SELECTED -> R.string.create
+ else -> android.R.string.ok
+ }
+ )
delete_button.visibility = when {
currentScreen != InitialScreen -> View.GONE
@@ -469,8 +483,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
}
private fun hideKeyboardSearch() {
- inputMethodManager.hideSoftInputFromWindow(toolbar_search_edit_text.windowToken,
- InputMethodManager.RESULT_UNCHANGED_SHOWN)
+ inputMethodManager.hideSoftInputFromWindow(
+ toolbar_search_edit_text.windowToken,
+ InputMethodManager.RESULT_UNCHANGED_SHOWN
+ )
toolbar_search_edit_text.clearFocus()
}
@@ -481,8 +497,10 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
}
private fun hideKeyboard() {
- inputMethodManager.hideSoftInputFromWindow(group_name_input.windowToken,
- InputMethodManager.RESULT_UNCHANGED_SHOWN)
+ inputMethodManager.hideSoftInputFromWindow(
+ group_name_input.windowToken,
+ InputMethodManager.RESULT_UNCHANGED_SHOWN
+ )
group_name_input.clearFocus()
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupDialogViewModel.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupDialogViewModel.kt
index 8c8a3e463..7aaccfcdb 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupDialogViewModel.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupDialogViewModel.kt
@@ -32,9 +32,9 @@ class FeedGroupDialogViewModel(
private var subscriptionsFlowable = Flowable
.combineLatest(
- filterSubscriptions.startWith(initialQuery),
- toggleShowOnlyUngrouped.startWith(initialShowOnlyUngrouped),
- BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) }
+ filterSubscriptions.startWith(initialQuery),
+ toggleShowOnlyUngrouped.startWith(initialShowOnlyUngrouped),
+ BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) }
)
.distinctUntilChanged()
.switchMap { filter ->
@@ -55,8 +55,10 @@ class FeedGroupDialogViewModel(
.subscribe(mutableGroupLiveData::postValue)
private var subscriptionsDisposable = Flowable
- .combineLatest(subscriptionsFlowable, feedDatabaseManager.subscriptionIdsForGroup(groupId),
- BiFunction { t1: List, t2: List -> t1 to t2.toSet() })
+ .combineLatest(
+ subscriptionsFlowable, feedDatabaseManager.subscriptionIdsForGroup(groupId),
+ BiFunction { t1: List, t2: List -> t1 to t2.toSet() }
+ )
.subscribeOn(Schedulers.io())
.subscribe(mutableSubscriptionsLiveData::postValue)
@@ -68,15 +70,19 @@ class FeedGroupDialogViewModel(
}
fun createGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set) {
- doAction(feedDatabaseManager.createGroup(name, selectedIcon)
- .flatMapCompletable {
- feedDatabaseManager.updateSubscriptionsForGroup(it, selectedSubscriptions.toList())
- })
+ doAction(
+ feedDatabaseManager.createGroup(name, selectedIcon)
+ .flatMapCompletable {
+ feedDatabaseManager.updateSubscriptionsForGroup(it, selectedSubscriptions.toList())
+ }
+ )
}
fun updateGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set, sortOrder: Long) {
- doAction(feedDatabaseManager.updateSubscriptionsForGroup(groupId, selectedSubscriptions.toList())
- .andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder))))
+ doAction(
+ feedDatabaseManager.updateSubscriptionsForGroup(groupId, selectedSubscriptions.toList())
+ .andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder)))
+ )
}
fun deleteGroup() {
@@ -120,8 +126,10 @@ class FeedGroupDialogViewModel(
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun create(modelClass: Class): T {
- return FeedGroupDialogViewModel(context.applicationContext,
- groupId, initialQuery, initialShowOnlyUngrouped) as T
+ return FeedGroupDialogViewModel(
+ context.applicationContext,
+ groupId, initialQuery, initialShowOnlyUngrouped
+ ) as T
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupReorderDialog.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupReorderDialog.kt
index dcc37eba7..516c2f567 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupReorderDialog.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupReorderDialog.kt
@@ -16,7 +16,6 @@ import com.xwray.groupie.TouchCallback
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import icepick.Icepick
import icepick.State
-import java.util.Collections
import kotlinx.android.synthetic.main.dialog_feed_group_reorder.confirm_button
import kotlinx.android.synthetic.main.dialog_feed_group_reorder.feed_groups_list
import org.schabi.newpipelegacy.R
@@ -25,6 +24,7 @@ import org.schabi.newpipelegacy.local.subscription.dialog.FeedGroupReorderDialog
import org.schabi.newpipelegacy.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.SuccessEvent
import org.schabi.newpipelegacy.local.subscription.item.FeedGroupReorderItem
import org.schabi.newpipelegacy.util.ThemeHelper
+import java.util.Collections
class FeedGroupReorderDialog : DialogFragment() {
private lateinit var viewModel: FeedGroupReorderDialogViewModel
@@ -51,12 +51,15 @@ class FeedGroupReorderDialog : DialogFragment() {
viewModel = ViewModelProviders.of(this).get(FeedGroupReorderDialogViewModel::class.java)
viewModel.groupsLiveData.observe(viewLifecycleOwner, Observer(::handleGroups))
- viewModel.dialogEventLiveData.observe(viewLifecycleOwner, Observer {
- when (it) {
- ProcessingEvent -> disableInput()
- SuccessEvent -> dismiss()
+ viewModel.dialogEventLiveData.observe(
+ viewLifecycleOwner,
+ Observer {
+ when (it) {
+ ProcessingEvent -> disableInput()
+ SuccessEvent -> dismiss()
+ }
}
- })
+ )
feed_groups_list.layoutManager = LinearLayoutManager(requireContext())
feed_groups_list.adapter = groupAdapter
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupReorderDialogViewModel.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupReorderDialogViewModel.kt
index 6c99dabb2..1456e0353 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupReorderDialogViewModel.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/dialog/FeedGroupReorderDialogViewModel.kt
@@ -21,9 +21,9 @@ class FeedGroupReorderDialogViewModel(application: Application) : AndroidViewMod
private var actionProcessingDisposable: Disposable? = null
private var groupsDisposable = feedDatabaseManager.groups()
- .limit(1)
- .subscribeOn(Schedulers.io())
- .subscribe(mutableGroupsLiveData::postValue)
+ .limit(1)
+ .subscribeOn(Schedulers.io())
+ .subscribe(mutableGroupsLiveData::postValue)
override fun onCleared() {
super.onCleared()
@@ -40,8 +40,8 @@ class FeedGroupReorderDialogViewModel(application: Application) : AndroidViewMod
mutableDialogEventLiveData.value = DialogEvent.ProcessingEvent
actionProcessingDisposable = completable
- .subscribeOn(Schedulers.io())
- .subscribe { mutableDialogEventLiveData.postValue(DialogEvent.SuccessEvent) }
+ .subscribeOn(Schedulers.io())
+ .subscribe { mutableDialogEventLiveData.postValue(DialogEvent.SuccessEvent) }
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/ChannelItem.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/ChannelItem.kt
index d9d4c21f5..49c661e13 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/ChannelItem.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/ChannelItem.kt
@@ -36,8 +36,10 @@ class ChannelItem(
viewHolder.itemAdditionalDetails.text = getDetailLine(viewHolder.root.context)
if (itemVersion == ItemVersion.NORMAL) viewHolder.itemChannelDescriptionView.text = infoItem.description
- ImageLoader.getInstance().displayImage(infoItem.thumbnailUrl, viewHolder.itemThumbnailView,
- ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS)
+ ImageLoader.getInstance().displayImage(
+ infoItem.thumbnailUrl, viewHolder.itemThumbnailView,
+ ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS
+ )
gesturesListener?.run {
viewHolder.containerView.setOnClickListener { selected(infoItem) }
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/FeedGroupReorderItem.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/FeedGroupReorderItem.kt
index c4481dccf..d33241b6d 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/FeedGroupReorderItem.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/FeedGroupReorderItem.kt
@@ -20,7 +20,7 @@ data class FeedGroupReorderItem(
val dragCallback: ItemTouchHelper
) : Item() {
constructor (feedGroupEntity: FeedGroupEntity, dragCallback: ItemTouchHelper) :
- this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon, dragCallback)
+ this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon, dragCallback)
override fun getId(): Long {
return when (groupId) {
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/FeedImportExportItem.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/FeedImportExportItem.kt
index e55ed05a4..e1d6bcf28 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/FeedImportExportItem.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/FeedImportExportItem.kt
@@ -49,8 +49,10 @@ class FeedImportExportItem(
expandIconListener?.let { viewHolder.import_export_options.removeListener(it) }
expandIconListener = CollapsibleView.StateListener { newState ->
- AnimationUtils.animateRotation(viewHolder.import_export_expand_icon,
- 250, if (newState == CollapsibleView.COLLAPSED) 0 else 180)
+ AnimationUtils.animateRotation(
+ viewHolder.import_export_expand_icon,
+ 250, if (newState == CollapsibleView.COLLAPSED) 0 else 180
+ )
}
viewHolder.import_export_options.currentState = if (isExpanded) CollapsibleView.EXPANDED else CollapsibleView.COLLAPSED
@@ -85,8 +87,10 @@ class FeedImportExportItem(
}
private fun setupImportFromItems(listHolder: ViewGroup) {
- val previousBackupItem = addItemView(listHolder.context.getString(R.string.previous_export),
- ThemeHelper.resolveResourceIdFromAttr(listHolder.context, R.attr.ic_backup), listHolder)
+ val previousBackupItem = addItemView(
+ listHolder.context.getString(R.string.previous_export),
+ ThemeHelper.resolveResourceIdFromAttr(listHolder.context, R.attr.ic_backup), listHolder
+ )
previousBackupItem.setOnClickListener { onImportPreviousSelected() }
val iconColor = if (ThemeHelper.isLightThemeSelected(listHolder.context)) Color.BLACK else Color.WHITE
@@ -112,8 +116,10 @@ class FeedImportExportItem(
}
private fun setupExportToItems(listHolder: ViewGroup) {
- val previousBackupItem = addItemView(listHolder.context.getString(R.string.file),
- ThemeHelper.resolveResourceIdFromAttr(listHolder.context, R.attr.ic_save), listHolder)
+ val previousBackupItem = addItemView(
+ listHolder.context.getString(R.string.file),
+ ThemeHelper.resolveResourceIdFromAttr(listHolder.context, R.attr.ic_save), listHolder
+ )
previousBackupItem.setOnClickListener { onExportSelected() }
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/HeaderWithMenuItem.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/HeaderWithMenuItem.kt
index 5d01b5146..ac519c302 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/HeaderWithMenuItem.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/HeaderWithMenuItem.kt
@@ -37,11 +37,11 @@ class HeaderWithMenuItem(
viewHolder.header_menu_item.setImageResource(itemIcon)
val listener: OnClickListener? =
- onClickListener?.let { OnClickListener { onClickListener.invoke() } }
+ onClickListener?.let { OnClickListener { onClickListener.invoke() } }
viewHolder.root.setOnClickListener(listener)
val menuItemListener: OnClickListener? =
- menuItemOnClickListener?.let { OnClickListener { menuItemOnClickListener.invoke() } }
+ menuItemOnClickListener?.let { OnClickListener { menuItemOnClickListener.invoke() } }
viewHolder.header_menu_item.setOnClickListener(menuItemListener)
updateMenuItemVisibility(viewHolder)
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/PickerSubscriptionItem.kt b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/PickerSubscriptionItem.kt
index 1c2c857db..2851f06ff 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/PickerSubscriptionItem.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/item/PickerSubscriptionItem.kt
@@ -21,8 +21,10 @@ data class PickerSubscriptionItem(
override fun getSpanSize(spanCount: Int, position: Int): Int = 1
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
- ImageLoader.getInstance().displayImage(subscriptionEntity.avatarUrl,
- viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS)
+ ImageLoader.getInstance().displayImage(
+ subscriptionEntity.avatarUrl,
+ viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS
+ )
viewHolder.title_view.text = subscriptionEntity.name
viewHolder.selected_highlight.visibility = if (isSelected) View.VISIBLE else View.GONE
@@ -38,7 +40,9 @@ data class PickerSubscriptionItem(
fun updateSelected(containerView: View, isSelected: Boolean) {
this.isSelected = isSelected
- animateView(containerView.selected_highlight,
- AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150)
+ animateView(
+ containerView.selected_highlight,
+ AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150
+ )
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/services/SubscriptionsExportService.java b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/services/SubscriptionsExportService.java
index 1a0b99f67..4e0b66b52 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/services/SubscriptionsExportService.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/services/SubscriptionsExportService.java
@@ -27,6 +27,7 @@
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
+import org.schabi.newpipelegacy.App;
import org.schabi.newpipelegacy.R;
import org.schabi.newpipelegacy.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
@@ -50,7 +51,7 @@ public class SubscriptionsExportService extends BaseImportExportService {
* A {@link LocalBroadcastManager local broadcast} will be made with this action
* when the export is successfully completed.
*/
- public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipelegacy.local"
+ public static final String EXPORT_COMPLETE_ACTION = App.PACKAGE_NAME + ".local"
+ ".subscription.services.SubscriptionsExportService.EXPORT_COMPLETE";
private Subscription subscription;
diff --git a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/services/SubscriptionsImportService.java b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/services/SubscriptionsImportService.java
index cd2d65fa4..09571055e 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/local/subscription/services/SubscriptionsImportService.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/local/subscription/services/SubscriptionsImportService.java
@@ -29,6 +29,7 @@
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
+import org.schabi.newpipelegacy.App;
import org.schabi.newpipelegacy.R;
import org.schabi.newpipelegacy.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.extractor.NewPipe;
@@ -66,7 +67,7 @@ public class SubscriptionsImportService extends BaseImportExportService {
* A {@link LocalBroadcastManager local broadcast} will be made with this action
* when the import is successfully completed.
*/
- public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipelegacy.local"
+ public static final String IMPORT_COMPLETE_ACTION = App.PACKAGE_NAME + ".local"
+ ".subscription.services.SubscriptionsImportService.IMPORT_COMPLETE";
/**
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipelegacy/player/BackgroundPlayer.java
index f43d9493b..ebe760ebf 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/BackgroundPlayer.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/BackgroundPlayer.java
@@ -29,6 +29,8 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
@@ -40,6 +42,9 @@
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
+
+import androidx.core.app.NotificationCompat.DecoratedCustomViewStyle;
+import androidx.media.app.NotificationCompat.MediaStyle;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSource;
@@ -224,6 +229,11 @@ private NotificationCompat.Builder createNotification() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
builder.setPriority(NotificationCompat.PRIORITY_MAX);
}
+
+ if (VERSION.SDK_INT > VERSION_CODES.Q) {
+ builder.setStyle(new MediaStyle());
+ }
+
return builder;
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/BackgroundPlayerActivity.java b/app/src/main/java/org/schabi/newpipelegacy/player/BackgroundPlayerActivity.java
index d7551708e..414b71809 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/BackgroundPlayerActivity.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/BackgroundPlayerActivity.java
@@ -1,6 +1,7 @@
package org.schabi.newpipelegacy.player;
import android.content.Intent;
+import android.view.Menu;
import android.view.MenuItem;
import org.schabi.newpipelegacy.R;
@@ -70,4 +71,9 @@ public boolean onPlayerOptionSelected(final MenuItem item) {
public Intent getPlayerShutdownIntent() {
return new Intent(ACTION_CLOSE);
}
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return super.onCreateOptionsMenu(menu);
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipelegacy/player/BasePlayer.java
index 1ef1dc24b..4f60f7ace 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/BasePlayer.java
@@ -157,7 +157,7 @@ public abstract class BasePlayer implements
//////////////////////////////////////////////////////////////////////////*/
protected static final int PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds
- protected static final int PROGRESS_LOOP_INTERVAL_MILLIS = 500;
+ protected static final int PROGRESS_LOOP_INTERVAL_MILLIS = 2000; // 2 seconds
protected SimpleExoPlayer simpleExoPlayer;
protected AudioReactor audioReactor;
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipelegacy/player/MainVideoPlayer.java
index 66951f794..64a52435f 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/MainVideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/MainVideoPlayer.java
@@ -48,6 +48,7 @@
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
@@ -86,6 +87,7 @@
import org.schabi.newpipelegacy.util.ListHelper;
import org.schabi.newpipelegacy.util.NavigationHelper;
import org.schabi.newpipelegacy.util.PermissionHelper;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import org.schabi.newpipelegacy.util.ShareUtils;
import org.schabi.newpipelegacy.util.StateSaver;
import org.schabi.newpipelegacy.util.ThemeHelper;
@@ -517,6 +519,7 @@ public void onPlaybackParameterChanged(final float playbackTempo, final float pl
private class VideoPlayerImpl extends VideoPlayer {
private static final float MAX_GESTURE_LENGTH = 0.75f;
+ private LinearLayout metadata;
private TextView titleTextView;
private TextView channelTextView;
private RelativeLayout volumeRelativeLayout;
@@ -561,6 +564,7 @@ private class VideoPlayerImpl extends VideoPlayer {
@Override
public void initViews(final View view) {
super.initViews(view);
+ this.metadata = view.findViewById(R.id.metadataView);
this.titleTextView = view.findViewById(R.id.titleTextView);
this.channelTextView = view.findViewById(R.id.channelTextView);
this.volumeRelativeLayout = view.findViewById(R.id.volumeRelativeLayout);
@@ -619,6 +623,7 @@ public void initListeners() {
gestureDetector.setIsLongpressEnabled(false);
getRootView().setOnTouchListener(listener);
+ metadata.setOnClickListener(this);
queueButton.setOnClickListener(this);
repeatButton.setOnClickListener(this);
shuffleButton.setOnClickListener(this);
@@ -702,6 +707,12 @@ public void onShuffleClicked() {
updatePlaybackButtons();
}
+ @Override
+ public void onPlay() {
+ super.onPlay();
+ showControlsThenHide();
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
@@ -814,6 +825,15 @@ public void onMuteUnmuteButtonClicked() {
setMuteButton(muteButton, playerImpl.isMuted());
}
+ public void onMetadataClicked() {
+ NavigationHelper.openVideoDetail(context,
+ ServiceHelper.getSelectedServiceId(context),
+ playerImpl.getVideoUrl(), playerImpl.getVideoTitle());
+
+ ((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
+ destroy();
+ finish();
+ }
@Override
public void onClick(final View v) {
@@ -850,6 +870,8 @@ public void onClick(final View v) {
return;
} else if (v.getId() == kodiButton.getId()) {
onKodiShare();
+ } else if (v.getId() == metadata.getId()) {
+ onMetadataClicked();
}
if (getCurrentState() != STATE_COMPLETED) {
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipelegacy/player/ServicePlayerActivity.java
index dd2e5e7c7..d147332d3 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/ServicePlayerActivity.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/ServicePlayerActivity.java
@@ -18,6 +18,7 @@
import android.widget.SeekBar;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.ItemTouchHelper;
@@ -40,6 +41,7 @@
import org.schabi.newpipelegacy.player.playqueue.PlayQueueItemTouchCallback;
import org.schabi.newpipelegacy.util.Localization;
import org.schabi.newpipelegacy.util.NavigationHelper;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import org.schabi.newpipelegacy.util.ThemeHelper;
import java.util.Collections;
@@ -149,6 +151,10 @@ public boolean onCreateOptionsMenu(final Menu m) {
getMenuInflater().inflate(R.menu.menu_play_queue, m);
getMenuInflater().inflate(getPlayerOptionMenuResource(), m);
onMaybeMuteChanged();
+ // to avoid null reference
+ if (player != null) {
+ onPlaybackParameterChanged(player.getPlaybackParameters());
+ }
return true;
}
@@ -485,7 +491,8 @@ public void onClick(final View view) {
} else if (view.getId() == shuffleButton.getId()) {
player.onShuffleClicked();
} else if (view.getId() == metadata.getId()) {
- scrollToSelected();
+ onOpenDetail(ServiceHelper.getSelectedServiceId(getApplicationContext()),
+ player.getVideoUrl(), player.getVideoTitle());
} else if (view.getId() == progressLiveSync.getId()) {
player.seekToDefault();
}
@@ -508,6 +515,7 @@ public void onPlaybackParameterChanged(final float playbackTempo, final float pl
final boolean playbackSkipSilence) {
if (player != null) {
player.setPlaybackParameters(playbackTempo, playbackPitch, playbackSkipSilence);
+ onPlaybackParameterChanged(player.getPlaybackParameters());
}
}
@@ -689,7 +697,7 @@ private void onPlayModeChanged(final int repeatMode, final boolean shuffled) {
shuffleButton.setImageAlpha(shuffleAlpha);
}
- private void onPlaybackParameterChanged(final PlaybackParameters parameters) {
+ private void onPlaybackParameterChanged(@Nullable final PlaybackParameters parameters) {
if (parameters != null) {
if (menu != null && player != null) {
final MenuItem item = menu.findItem(R.id.action_playback_speed);
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipelegacy/player/VideoPlayer.java
index 46a018ebc..9be8a9e5e 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/VideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/VideoPlayer.java
@@ -99,7 +99,7 @@ public abstract class VideoPlayer extends BasePlayer
public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
- public static final int DPAD_CONTROLS_HIDE_TIME = 7000; // 7 Seconds
+ public static final int DPAD_CONTROLS_HIDE_TIME = 5000; // 5 seconds
protected static final int RENDERER_UNAVAILABLE = -1;
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/helper/AudioReactor.java b/app/src/main/java/org/schabi/newpipelegacy/player/helper/AudioReactor.java
index e98d56920..5bb4d9e43 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/helper/AudioReactor.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/helper/AudioReactor.java
@@ -5,35 +5,33 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
-import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.media.audiofx.AudioEffect;
-import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.media.AudioFocusRequestCompat;
+import androidx.media.AudioManagerCompat;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
+import com.google.android.exoplayer2.decoder.DecoderCounters;
public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, AnalyticsListener {
private static final String TAG = "AudioFocusReactor";
- private static final boolean SHOULD_BUILD_FOCUS_REQUEST =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
-
private static final int DUCK_DURATION = 1500;
private static final float DUCK_AUDIO_TO = .2f;
- private static final int FOCUS_GAIN_TYPE = AudioManager.AUDIOFOCUS_GAIN;
+ private static final int FOCUS_GAIN_TYPE = AudioManagerCompat.AUDIOFOCUS_GAIN;
private static final int STREAM_TYPE = AudioManager.STREAM_MUSIC;
private final SimpleExoPlayer player;
private final Context context;
private final AudioManager audioManager;
- private final AudioFocusRequest request;
+ private final AudioFocusRequestCompat request;
public AudioReactor(@NonNull final Context context,
@NonNull final SimpleExoPlayer player) {
@@ -42,20 +40,17 @@ public AudioReactor(@NonNull final Context context,
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
player.addAnalyticsListener(this);
- if (SHOULD_BUILD_FOCUS_REQUEST) {
- request = new AudioFocusRequest.Builder(FOCUS_GAIN_TYPE)
- .setAcceptsDelayedFocusGain(true)
- .setWillPauseWhenDucked(true)
- .setOnAudioFocusChangeListener(this)
- .build();
- } else {
- request = null;
- }
+ request = new AudioFocusRequestCompat.Builder(FOCUS_GAIN_TYPE)
+ //.setAcceptsDelayedFocusGain(true)
+ .setWillPauseWhenDucked(true)
+ .setOnAudioFocusChangeListener(this)
+ .build();
}
public void dispose() {
abandonAudioFocus();
player.removeAnalyticsListener(this);
+ notifyAudioSessionUpdate(false, player.getAudioSessionId());
}
/*//////////////////////////////////////////////////////////////////////////
@@ -63,19 +58,11 @@ public void dispose() {
//////////////////////////////////////////////////////////////////////////*/
public void requestAudioFocus() {
- if (SHOULD_BUILD_FOCUS_REQUEST) {
- audioManager.requestAudioFocus(request);
- } else {
- audioManager.requestAudioFocus(this, STREAM_TYPE, FOCUS_GAIN_TYPE);
- }
+ AudioManagerCompat.requestAudioFocus(audioManager, request);
}
public void abandonAudioFocus() {
- if (SHOULD_BUILD_FOCUS_REQUEST) {
- audioManager.abandonAudioFocusRequest(request);
- } else {
- audioManager.abandonAudioFocus(this);
- }
+ AudioManagerCompat.abandonAudioFocusRequest(audioManager, request);
}
public int getVolume() {
@@ -87,7 +74,7 @@ public void setVolume(final int volume) {
}
public int getMaxVolume() {
- return audioManager.getStreamMaxVolume(STREAM_TYPE);
+ return AudioManagerCompat.getStreamMaxVolume(audioManager, STREAM_TYPE);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -163,11 +150,20 @@ public void onAnimationEnd(final Animator animation) {
@Override
public void onAudioSessionId(final EventTime eventTime, final int audioSessionId) {
+ notifyAudioSessionUpdate(true, audioSessionId);
+ }
+
+ public void onAudioDisabled(final EventTime eventTime, final DecoderCounters counters) {
+ notifyAudioSessionUpdate(false, player.getAudioSessionId());
+ }
+
+ private void notifyAudioSessionUpdate(final boolean active, final int audioSessionId) {
if (!PlayerHelper.isUsingDSP(context)) {
return;
}
-
- final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
+ final Intent intent = new Intent(active
+ ? AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION
+ : AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId);
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
context.sendBroadcast(intent);
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/helper/LoadController.java b/app/src/main/java/org/schabi/newpipelegacy/player/helper/LoadController.java
index a205befb2..ea8068db4 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/helper/LoadController.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/helper/LoadController.java
@@ -31,7 +31,8 @@ private LoadController(final int initialPlaybackBufferMs,
DefaultLoadControl.Builder builder = new DefaultLoadControl.Builder();
builder.setBufferDurationsMs(minimumPlaybackbufferMs, optimalPlaybackBufferMs,
- initialPlaybackBufferMs, initialPlaybackBufferMs);
+ initialPlaybackBufferMs,
+ DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);
internalLoadControl = builder.createDefaultLoadControl();
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlaybackParameterDialog.java
index 80ac308c0..dbe94a289 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlaybackParameterDialog.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlaybackParameterDialog.java
@@ -1,5 +1,7 @@
package org.schabi.newpipelegacy.player.helper;
+import static org.schabi.newpipelegacy.player.BasePlayer.DEBUG;
+import static org.schabi.newpipelegacy.util.Localization.assureCorrectAppLanguage;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
@@ -18,9 +20,6 @@
import org.schabi.newpipelegacy.R;
import org.schabi.newpipelegacy.util.SliderStrategy;
-import static org.schabi.newpipelegacy.player.BasePlayer.DEBUG;
-import static org.schabi.newpipelegacy.util.Localization.assureCorrectAppLanguage;
-
public class PlaybackParameterDialog extends DialogFragment {
// Minimum allowable range in ExoPlayer
private static final double MINIMUM_PLAYBACK_VALUE = 0.10f;
@@ -155,7 +154,6 @@ public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
setupControlViews(view);
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireActivity())
- .setTitle(R.string.playback_speed_control)
.setView(view)
.setCancelable(true)
.setNegativeButton(R.string.cancel, (dialogInterface, i) ->
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlayerDataSource.java b/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlayerDataSource.java
index 09bdab3f7..48e47d3f9 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlayerDataSource.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlayerDataSource.java
@@ -21,11 +21,13 @@ public class PlayerDataSource {
private static final int EXTRACTOR_MINIMUM_RETRY = Integer.MAX_VALUE;
private static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000;
+ private final Integer continueLoadingCheckIntervalBytes;
private final DataSource.Factory cacheDataSourceFactory;
private final DataSource.Factory cachelessDataSourceFactory;
public PlayerDataSource(@NonNull final Context context, @NonNull final String userAgent,
@NonNull final TransferListener transferListener) {
+ continueLoadingCheckIntervalBytes = PlayerHelper.getProgressiveLoadIntervalBytes(context);
cacheDataSourceFactory = new CacheFactory(context, userAgent, transferListener);
cachelessDataSourceFactory
= new DefaultDataSourceFactory(context, userAgent, transferListener);
@@ -70,6 +72,7 @@ public DashMediaSource.Factory getDashMediaSourceFactory() {
public ProgressiveMediaSource.Factory getExtractorMediaSourceFactory() {
return new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
+ .setContinueLoadingCheckIntervalBytes(continueLoadingCheckIntervalBytes)
.setLoadErrorHandlingPolicy(
new DefaultLoadErrorHandlingPolicy(EXTRACTOR_MINIMUM_RETRY));
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlayerHelper.java
index 21c57aada..6bf6cc920 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/helper/PlayerHelper.java
@@ -38,6 +38,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -229,7 +230,7 @@ public static long getPreferredCacheSize() {
}
public static long getPreferredFileSize() {
- return 512 * 1024L;
+ return 2 * 1024 * 1024L; // ExoPlayer CacheDataSink.MIN_RECOMMENDED_FRAGMENT_SIZE
}
/**
@@ -324,6 +325,13 @@ public static void setScreenBrightness(@NonNull final Context context,
setScreenBrightness(context, setScreenBrightness, System.currentTimeMillis());
}
+ @NonNull
+ public static Integer getProgressiveLoadIntervalBytes(@NonNull final Context context) {
+ return Integer.parseInt(Objects.requireNonNull(getPreferences(context).getString(
+ context.getString(R.string.progressive_load_interval_key),
+ context.getString(R.string.progressive_load_interval_bytes_default_value))));
+ }
+
////////////////////////////////////////////////////////////////////////////
// Private helpers
////////////////////////////////////////////////////////////////////////////
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/playqueue/PlayQueueItemBuilder.java b/app/src/main/java/org/schabi/newpipelegacy/player/playqueue/PlayQueueItemBuilder.java
index d95a19b65..4a842af6e 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/playqueue/PlayQueueItemBuilder.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/playqueue/PlayQueueItemBuilder.java
@@ -7,9 +7,9 @@
import com.nostra13.universalimageloader.core.ImageLoader;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipelegacy.util.ImageDisplayConstants;
import org.schabi.newpipelegacy.util.Localization;
+import org.schabi.newpipelegacy.util.ServiceHelper;
public class PlayQueueItemBuilder {
private static final String TAG = PlayQueueItemBuilder.class.toString();
@@ -27,7 +27,7 @@ public void buildStreamInfoItem(final PlayQueueItemHolder holder, final PlayQueu
holder.itemVideoTitleView.setText(item.getTitle());
}
holder.itemAdditionalDetailsView.setText(Localization.concatenateStrings(item.getUploader(),
- NewPipe.getNameOfService(item.getServiceId())));
+ ServiceHelper.getNameOfServiceById(item.getServiceId())));
if (item.getDuration() > 0) {
holder.itemDurationView.setText(Localization.getDurationString(item.getDuration()));
diff --git a/app/src/main/java/org/schabi/newpipelegacy/player/resolver/VideoPlaybackResolver.java b/app/src/main/java/org/schabi/newpipelegacy/player/resolver/VideoPlaybackResolver.java
index 0b9a2aa8a..2653e9878 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/player/resolver/VideoPlaybackResolver.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/player/resolver/VideoPlaybackResolver.java
@@ -106,7 +106,9 @@ public MediaSource resolve(@NonNull final StreamInfo info) {
SELECTION_FLAG_AUTOSELECT,
PlayerHelper.captionLanguageOf(context, subtitle));
final MediaSource textSource = dataSource.getSampleMediaSourceFactory()
- .createMediaSource(Uri.parse(subtitle.getURL()), textFormat, TIME_UNSET);
+ .createMediaSource(
+ Uri.parse(subtitle.getContent()), textFormat, TIME_UNSET
+ );
mediaSources.add(textSource);
}
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/report/AcraReportSenderFactory.java b/app/src/main/java/org/schabi/newpipelegacy/report/AcraReportSenderFactory.java
index e0562bae4..c0088774a 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/report/AcraReportSenderFactory.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/report/AcraReportSenderFactory.java
@@ -4,9 +4,12 @@
import androidx.annotation.NonNull;
+import com.google.auto.service.AutoService;
+
import org.acra.config.CoreConfiguration;
import org.acra.sender.ReportSender;
import org.acra.sender.ReportSenderFactory;
+import org.schabi.newpipelegacy.App;
/*
* Created by Christian Schabesberger on 13.09.16.
@@ -28,6 +31,10 @@
* along with NewPipe. If not, see .
*/
+/**
+ * Used by ACRA in {@link App}.initAcra() as the factory for report senders.
+ */
+@AutoService(ReportSenderFactory.class)
public class AcraReportSenderFactory implements ReportSenderFactory {
@NonNull
public ReportSender create(@NonNull final Context context,
diff --git a/app/src/main/java/org/schabi/newpipelegacy/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipelegacy/settings/ContentSettingsFragment.java
index d087f683c..febd8be98 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/settings/ContentSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/settings/ContentSettingsFragment.java
@@ -21,6 +21,7 @@
import org.schabi.newpipelegacy.DownloaderImpl;
import org.schabi.newpipelegacy.NewPipeDatabase;
import org.schabi.newpipelegacy.R;
+import org.schabi.newpipelegacy.ReCaptchaActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.localization.ContentCountry;
import org.schabi.newpipe.extractor.localization.Localization;
@@ -76,6 +77,22 @@ public void onCreate(@Nullable final Bundle savedInstanceState) {
.getPreferredContentCountry(requireContext());
initialLanguage = PreferenceManager
.getDefaultSharedPreferences(getContext()).getString("app_language_key", "en");
+
+ final Preference clearCookiePref = findPreference(getString(R.string.clear_cookie_key));
+
+ clearCookiePref.setOnPreferenceClickListener(preference -> {
+ defaultPreferences.edit()
+ .putString(getString(R.string.recaptcha_cookies_key), "").apply();
+ DownloaderImpl.getInstance().setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, "");
+ Toast.makeText(getActivity(), R.string.recaptcha_cookies_cleared,
+ Toast.LENGTH_SHORT).show();
+ clearCookiePref.setVisible(false);
+ return true;
+ });
+
+ if (defaultPreferences.getString(getString(R.string.recaptcha_cookies_key), "").isEmpty()) {
+ clearCookiePref.setVisible(false);
+ }
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipelegacy/settings/PeertubeInstanceListFragment.java b/app/src/main/java/org/schabi/newpipelegacy/settings/PeertubeInstanceListFragment.java
index bf9508d8b..1415e4b21 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/settings/PeertubeInstanceListFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/settings/PeertubeInstanceListFragment.java
@@ -204,7 +204,7 @@ private void restoreDefaults() {
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.yes, (dialog, which) -> {
sharedPreferences.edit().remove(savedInstanceListKey).apply();
- selectInstance(PeertubeInstance.defaultInstance);
+ selectInstance(PeertubeInstance.DEFAULT_INSTANCE);
updateInstanceList();
instanceListAdapter.notifyDataSetChanged();
})
diff --git a/app/src/main/java/org/schabi/newpipelegacy/settings/VideoAudioSettingsFragment.java b/app/src/main/java/org/schabi/newpipelegacy/settings/VideoAudioSettingsFragment.java
index 4159ac5c3..03e1d48f6 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/settings/VideoAudioSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/settings/VideoAudioSettingsFragment.java
@@ -16,11 +16,14 @@
import org.schabi.newpipelegacy.R;
import org.schabi.newpipelegacy.util.PermissionHelper;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.LinkedList;
public class VideoAudioSettingsFragment extends BasePreferenceFragment {
private SharedPreferences.OnSharedPreferenceChangeListener listener;
+ private ListPreference defaultRes,defaultPopupRes,limitMobDataUsage;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@@ -29,6 +32,9 @@ public void onCreate(@Nullable final Bundle savedInstanceState) {
updateSeekOptions();
listener = (sharedPreferences, s) -> {
+ defaultRes = (ListPreference) findPreference(getString(R.string.default_resolution_key));
+ defaultPopupRes = (ListPreference) findPreference(getString(R.string.default_popup_resolution_key));
+ limitMobDataUsage = (ListPreference) findPreference(getString(R.string.limit_mobile_data_usage_key));
// on M and above, if user chooses to minimise to popup player on exit
// and the app doesn't have display over other apps permission,
@@ -50,7 +56,72 @@ public void onCreate(@Nullable final Bundle savedInstanceState) {
} else if (s.equals(getString(R.string.use_inexact_seek_key))) {
updateSeekOptions();
}
+
+ //check if "show higher resolutions" was changed
+ if(s.equals(getString(R.string.show_higher_resolutions_key))){
+
+ if(checkIfShowHighRes()){
+ showHigherResolutions(true);
+ }
+ else {
+
+ //if the setting was turned off and any of the defaults is set to 1440p or 2160p, change them to 1080p60
+ //(the next highest value)
+ if(defaultRes.getValue().equals("1440p") || defaultRes.getValue().equals("1440p60") ||
+ defaultRes.getValue().equals("2160p") || defaultRes.getValue().equals("2160p60")){
+ defaultRes.setValueIndex(3);
+ }
+ if(defaultPopupRes.getValue().equals("1440p") || defaultPopupRes.getValue().equals("1440p60") ||
+ defaultPopupRes.getValue().equals("2160p") || defaultPopupRes.getValue().equals("2160p60")){
+ defaultPopupRes.setValueIndex(3);
+ }
+ if(limitMobDataUsage.getValue().equals("1440p") || limitMobDataUsage.getValue().equals("1440p60") ||
+ limitMobDataUsage.getValue().equals("2160p") || limitMobDataUsage.getValue().equals("2160p60")){
+ limitMobDataUsage.setValueIndex(3);
+ }
+
+ showHigherResolutions(false);
+
+ }
+ }
+
};
+ if(!checkIfShowHighRes()){
+ showHigherResolutions(false);
+ }
+ }
+
+ private boolean checkIfShowHighRes(){
+ return getPreferenceManager().getSharedPreferences().getBoolean(getString(R.string.show_higher_resolutions_key),false);
+ }
+
+ private void showHigherResolutions(boolean show){
+
+ Resources res = getResources();
+ ArrayList resolutions = new ArrayList(Arrays.asList(res.getStringArray(R.array.resolution_list_description)));
+ ArrayList resolutionValues = new ArrayList(Arrays.asList(res.getStringArray(R.array.resolution_list_values)));
+
+ ArrayList mobileDataResolutions = new ArrayList(Arrays.asList(res.getStringArray(R.array.limit_data_usage_description_list)));
+ ArrayList mobileDataResolutionValues = new ArrayList(Arrays.asList(res.getStringArray(R.array.limit_data_usage_values_list)));
+
+ if(!show) {
+ List higherResolutions = Arrays.asList("1440p", "1440p60", "2160p", "2160p60");
+
+ resolutions.removeAll(higherResolutions);
+ resolutionValues.removeAll(higherResolutions);
+
+ mobileDataResolutions.removeAll(higherResolutions);
+ mobileDataResolutionValues.removeAll(higherResolutions);
+ }
+
+ defaultRes.setEntries(resolutions.toArray(new String[resolutions.size()]));
+ defaultRes.setEntryValues(resolutionValues.toArray(new String[resolutionValues.size()]));
+
+ defaultPopupRes.setEntries(resolutions.toArray(new String[resolutions.size()]));
+ defaultPopupRes.setEntryValues(resolutionValues.toArray(new String[resolutionValues.size()]));
+
+ limitMobDataUsage.setEntries(mobileDataResolutions.toArray(new String[mobileDataResolutions.size()]));
+ limitMobDataUsage.setEntryValues(mobileDataResolutionValues.toArray(new String[mobileDataResolutionValues.size()]));
}
/**
@@ -91,6 +162,11 @@ private void updateSeekOptions() {
getString(R.string.seek_duration_key));
durations.setEntryValues(displayedDurationValues.toArray(new CharSequence[0]));
durations.setEntries(displayedDescriptionValues.toArray(new CharSequence[0]));
+ defaultRes = (ListPreference) findPreference(getString(R.string.default_resolution_key));
+ defaultPopupRes = (ListPreference) findPreference(
+ getString(R.string.default_popup_resolution_key));
+ limitMobDataUsage = (ListPreference) findPreference(
+ getString(R.string.limit_mobile_data_usage_key));
final int selectedDuration = Integer.parseInt(durations.getValue());
if (inexactSeek && selectedDuration / (int) DateUtils.SECOND_IN_MILLIS % 10 == 5) {
final int newDuration = selectedDuration / (int) DateUtils.SECOND_IN_MILLIS + 5;
diff --git a/app/src/main/java/org/schabi/newpipelegacy/settings/tabs/ChooseTabsFragment.java b/app/src/main/java/org/schabi/newpipelegacy/settings/tabs/ChooseTabsFragment.java
index feb9e9f94..fc78bebd5 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/settings/tabs/ChooseTabsFragment.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/settings/tabs/ChooseTabsFragment.java
@@ -29,12 +29,12 @@
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.schabi.newpipelegacy.R;
-import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipelegacy.report.ErrorActivity;
import org.schabi.newpipelegacy.report.UserAction;
import org.schabi.newpipelegacy.settings.SelectChannelFragment;
import org.schabi.newpipelegacy.settings.SelectKioskFragment;
import org.schabi.newpipelegacy.settings.SelectPlaylistFragment;
+import org.schabi.newpipelegacy.util.ServiceHelper;
import org.schabi.newpipelegacy.settings.tabs.AddTabDialog.ChooseTabListItem;
import org.schabi.newpipelegacy.util.ThemeHelper;
@@ -409,18 +409,18 @@ void bind(final int position, final TabViewHolder holder) {
tabName = getString(R.string.default_kiosk_page_summary);
break;
case KIOSK:
- tabName = NewPipe.getNameOfService(((Tab.KioskTab) tab)
+ tabName = ServiceHelper.getNameOfServiceById(((Tab.KioskTab) tab)
.getKioskServiceId()) + "/" + tab.getTabName(requireContext());
break;
case CHANNEL:
- tabName = NewPipe.getNameOfService(((Tab.ChannelTab) tab)
+ tabName = ServiceHelper.getNameOfServiceById(((Tab.ChannelTab) tab)
.getChannelServiceId()) + "/" + tab.getTabName(requireContext());
break;
case PLAYLIST:
final int serviceId = ((Tab.PlaylistTab) tab).getPlaylistServiceId();
final String serviceName = serviceId == -1
? getString(R.string.local)
- : NewPipe.getNameOfService(serviceId);
+ : ServiceHelper.getNameOfServiceById(serviceId);
tabName = serviceName + "/" + tab.getTabName(requireContext());
break;
default:
diff --git a/app/src/main/java/org/schabi/newpipelegacy/util/ExceptionUtils.kt b/app/src/main/java/org/schabi/newpipelegacy/util/ExceptionUtils.kt
index 9cc4ee369..82c3f3969 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/util/ExceptionUtils.kt
+++ b/app/src/main/java/org/schabi/newpipelegacy/util/ExceptionUtils.kt
@@ -10,9 +10,11 @@ class ExceptionUtils {
*/
@JvmStatic
fun isInterruptedCaused(throwable: Throwable): Boolean {
- return hasExactCause(throwable,
- InterruptedIOException::class.java,
- InterruptedException::class.java)
+ return hasExactCause(
+ throwable,
+ InterruptedIOException::class.java,
+ InterruptedException::class.java
+ )
}
/**
@@ -20,10 +22,11 @@ class ExceptionUtils {
*/
@JvmStatic
fun isNetworkRelated(throwable: Throwable): Boolean {
- return hasAssignableCause(throwable,
- IOException::class.java)
+ return hasAssignableCause(
+ throwable,
+ IOException::class.java
+ )
}
-
/**
* Calls [hasCause] with the `checkSubtypes` parameter set to false.
*/
diff --git a/app/src/main/java/org/schabi/newpipelegacy/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipelegacy/util/ExtractorHelper.java
index 7604eb9f3..e0a3a7a08 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/util/ExtractorHelper.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/util/ExtractorHelper.java
@@ -1,6 +1,6 @@
/*
* Copyright 2017 Mauricio Colli
- * Extractors.java is part of NewPipe
+ * ExtractorHelper.java is part of NewPipe
*
* License: GPL-3.0+
* This program is free software: you can redistribute it and/or modify
@@ -37,10 +37,16 @@
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.comments.CommentsInfo;
+import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
+import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
+import org.schabi.newpipe.extractor.exceptions.PaidContentException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
+import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
+import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
+import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
import org.schabi.newpipe.extractor.feed.FeedExtractor;
import org.schabi.newpipe.extractor.feed.FeedInfo;
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
@@ -288,18 +294,33 @@ public static void handleGeneralException(final Context context, final int servi
context.startActivity(intent);
} else if (ExceptionUtils.isNetworkRelated(exception)) {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
+ } else if (exception instanceof AgeRestrictedContentException) {
+ Toast.makeText(context, R.string.restricted_video_no_stream,
+ Toast.LENGTH_LONG).show();
+ } else if (exception instanceof GeographicRestrictionException) {
+ Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show();
+ } else if (exception instanceof PaidContentException) {
+ Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show();
+ } else if (exception instanceof PrivateContentException) {
+ Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show();
+ } else if (exception instanceof SoundCloudGoPlusContentException) {
+ Toast.makeText(context, R.string.soundcloud_go_plus_content,
+ Toast.LENGTH_LONG).show();
+ } else if (exception instanceof YoutubeMusicPremiumContentException) {
+ Toast.makeText(context, R.string.youtube_music_premium_content,
+ Toast.LENGTH_LONG).show();
} else if (exception instanceof ContentNotAvailableException) {
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
} else if (exception instanceof ContentNotSupportedException) {
Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show();
} else {
- int errorId = exception instanceof YoutubeStreamExtractor.DecryptException
+ int errorId = exception instanceof YoutubeStreamExtractor.DeobfuscateException
? R.string.youtube_signature_decryption_error
: exception instanceof ParsingException
? R.string.parsing_error : R.string.general_error;
ErrorActivity.reportError(handler, context, exception, MainActivity.class, null,
ErrorActivity.ErrorInfo.make(userAction, serviceId == -1 ? "none"
- : NewPipe.getNameOfService(serviceId),
+ : ServiceHelper.getNameOfServiceById(serviceId),
url + (optionalErrorMessage == null ? ""
: optionalErrorMessage), errorId));
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/util/KioskTranslator.java b/app/src/main/java/org/schabi/newpipelegacy/util/KioskTranslator.java
index dde686970..6552baea0 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/util/KioskTranslator.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/util/KioskTranslator.java
@@ -44,6 +44,14 @@ public static String getTranslatedKioskName(final String kioskId, final Context
return c.getString(R.string.most_liked);
case "conferences":
return c.getString(R.string.conferences);
+ case "recent":
+ return c.getString(R.string.recent);
+ case "live":
+ return c.getString(R.string.duration_live);
+ case "Featured":
+ return c.getString(R.string.featured);
+ case "Radio":
+ return c.getString(R.string.radio);
default:
return kioskId;
}
@@ -59,9 +67,16 @@ public static int getKioskIcon(final String kioskId, final Context c) {
case "Local":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_local);
case "Recently added":
+ case "recent":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_recent);
case "Most liked":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_thumb_up);
+ case "live":
+ return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_live_tv);
+ case "Featured":
+ return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_stars);
+ case "Radio":
+ return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_radio);
default:
return 0;
}
diff --git a/app/src/main/java/org/schabi/newpipelegacy/util/Localization.java b/app/src/main/java/org/schabi/newpipelegacy/util/Localization.java
index 6abf785cb..8cd0e3bae 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/util/Localization.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/util/Localization.java
@@ -5,6 +5,7 @@
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.icu.text.CompactDecimalFormat;
import android.os.Build;
import android.preference.PreferenceManager;
import android.text.TextUtils;
@@ -185,6 +186,11 @@ public static String localizeWatchingCount(final Context context, final long wat
}
public static String shortCount(final Context context, final long count) {
+ if (Build.VERSION.SDK_INT >= 24) {
+ return CompactDecimalFormat.getInstance(getAppLocale(context),
+ CompactDecimalFormat.CompactStyle.SHORT).format(count);
+ }
+
double value = (double) count;
if (count >= 1000000000) {
return localizeNumber(context, round(value / 1000000000, 1))
diff --git a/app/src/main/java/org/schabi/newpipelegacy/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipelegacy/util/NavigationHelper.java
index f04d6b2f4..bfc16780a 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/util/NavigationHelper.java
@@ -124,7 +124,8 @@ public static Intent getPlayerIntent(@NonNull final Context context,
.putExtra(BasePlayer.IS_MUTED, isMuted);
}
- public static void playOnMainPlayer(final Context context, final PlayQueue queue,
+ public static void playOnMainPlayer(final Context context,
+ final PlayQueue queue,
final boolean resumePlayback) {
final Intent playerIntent
= getPlayerIntent(context, MainVideoPlayer.class, queue, resumePlayback);
@@ -132,7 +133,8 @@ public static void playOnMainPlayer(final Context context, final PlayQueue queue
context.startActivity(playerIntent);
}
- public static void playOnPopupPlayer(final Context context, final PlayQueue queue,
+ public static void playOnPopupPlayer(final Context context,
+ final PlayQueue queue,
final boolean resumePlayback) {
if (!PermissionHelper.isPopupEnabled(context)) {
PermissionHelper.showPopupEnablementToast(context);
@@ -144,7 +146,8 @@ public static void playOnPopupPlayer(final Context context, final PlayQueue queu
getPlayerIntent(context, PopupVideoPlayer.class, queue, resumePlayback));
}
- public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue,
+ public static void playOnBackgroundPlayer(final Context context,
+ final PlayQueue queue,
final boolean resumePlayback) {
Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT)
.show();
@@ -481,6 +484,11 @@ public static void openRouterActivity(final Context context, final String url) {
context.startActivity(mIntent);
}
+ public static void openBackgroundPlayer(final Context context) {
+ final Intent intent = new Intent(context, BackgroundPlayerActivity.class);
+ context.startActivity(intent);
+ }
+
public static void openAbout(final Context context) {
Intent intent = new Intent(context, AboutActivity.class);
context.startActivity(intent);
diff --git a/app/src/main/java/org/schabi/newpipelegacy/util/ServiceHelper.java b/app/src/main/java/org/schabi/newpipelegacy/util/ServiceHelper.java
index b2b32c2d5..15fa7028a 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/util/ServiceHelper.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/util/ServiceHelper.java
@@ -6,6 +6,8 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
@@ -38,6 +40,8 @@ public static int getIcon(final int serviceId) {
return R.drawable.place_holder_gadse;
case 3:
return R.drawable.place_holder_peertube;
+ case 4:
+ return R.drawable.place_holder_bandcamp;
default:
return R.drawable.place_holder_circle;
}
@@ -48,6 +52,7 @@ public static String getTranslatedFilterString(final String filter, final Contex
case "all":
return c.getString(R.string.all);
case "videos":
+ case "sepia_videos":
case "music_videos":
return c.getString(R.string.videos_string);
case "channels":
@@ -124,6 +129,29 @@ public static int getSelectedServiceId(final Context context) {
return serviceId;
}
+ @Nullable
+ public static StreamingService getSelectedService(final Context context) {
+ final String serviceName = PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(context.getString(R.string.current_service_key),
+ context.getString(R.string.default_service_value));
+
+ try {
+ return NewPipe.getService(serviceName);
+ } catch (final ExtractionException e) {
+ return null;
+ }
+ }
+
+ @NonNull
+ public static String getNameOfServiceById(final int serviceId) {
+ return ServiceList.all().stream()
+ .filter(s -> s.getServiceId() == serviceId)
+ .findFirst()
+ .map(StreamingService::getServiceInfo)
+ .map(StreamingService.ServiceInfo::getName)
+ .orElse("");
+ }
+
public static void setSelectedServiceId(final Context context, final int serviceId) {
String serviceName;
try {
@@ -135,8 +163,10 @@ public static void setSelectedServiceId(final Context context, final int service
setSelectedServicePreferences(context, serviceName);
}
- public static void setSelectedServiceId(final Context context, final String serviceName) {
- int serviceId = NewPipe.getIdOfService(serviceName);
+ public static void setSelectedServiceId(
+ final Context context, final String serviceName)
+ throws ExtractionException {
+ int serviceId = NewPipe.getService(serviceName).getServiceId();
if (serviceId == -1) {
setSelectedServicePreferences(context,
DEFAULT_FALLBACK_SERVICE.getServiceInfo().getName());
diff --git a/app/src/main/java/org/schabi/newpipelegacy/views/FocusOverlayView.java b/app/src/main/java/org/schabi/newpipelegacy/views/FocusOverlayView.java
index 30c8b8338..ac2b60a62 100644
--- a/app/src/main/java/org/schabi/newpipelegacy/views/FocusOverlayView.java
+++ b/app/src/main/java/org/schabi/newpipelegacy/views/FocusOverlayView.java
@@ -171,6 +171,8 @@ public void setColorFilter(final ColorFilter colorFilter) {
}
public static void setupFocusObserver(final Dialog dialog) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
+ return;
Rect displayRect = new Rect();
Window window = dialog.getWindow();
@@ -186,6 +188,8 @@ public static void setupFocusObserver(final Dialog dialog) {
}
public static void setupFocusObserver(final Activity activity) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
+ return;
Rect displayRect = new Rect();
Window window = activity.getWindow();
diff --git a/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java b/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java
index 83389e489..f088991bd 100644
--- a/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java
+++ b/app/src/main/java/us/shandian/giga/get/DownloadMissionRecover.java
@@ -132,7 +132,7 @@ private void resolveStream() throws IOException, ExtractionException, HttpError
switch (mRecovery.kind) {
case 'a':
for (AudioStream audio : mExtractor.getAudioStreams()) {
- if (audio.average_bitrate == mRecovery.desiredBitrate && audio.getFormat() == mRecovery.format) {
+ if (audio.getAverageBitrate() == mRecovery.desiredBitrate && audio.getFormat() == mRecovery.format) {
url = audio.getUrl();
break;
}
@@ -155,7 +155,7 @@ private void resolveStream() throws IOException, ExtractionException, HttpError
for (SubtitlesStream subtitles : mExtractor.getSubtitles(mRecovery.format)) {
String tag = subtitles.getLanguageTag();
if (tag.equals(mRecovery.desired) && subtitles.isAutoGenerated() == mRecovery.desired2) {
- url = subtitles.getURL();
+ url = subtitles.getUrl();
break;
}
}
diff --git a/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.java b/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.java
index 4a2948131..b8304cd6b 100644
--- a/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.java
+++ b/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.java
@@ -25,7 +25,7 @@ public class MissionRecoveryInfo implements Serializable, Parcelable {
public MissionRecoveryInfo(@NonNull Stream stream) {
if (stream instanceof AudioStream) {
- desiredBitrate = ((AudioStream) stream).average_bitrate;
+ desiredBitrate = ((AudioStream) stream).getAverageBitrate();
desired2 = false;
kind = 'a';
} else if (stream instanceof VideoStream) {
diff --git a/app/src/main/res/drawable-nodpi/place_holder_bandcamp.png b/app/src/main/res/drawable-nodpi/place_holder_bandcamp.png
new file mode 100644
index 000000000..848e109c2
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/place_holder_bandcamp.png differ
diff --git a/app/src/main/res/drawable/ic_heart.xml b/app/src/main/res/drawable/ic_heart.xml
new file mode 100644
index 000000000..86d1f0527
--- /dev/null
+++ b/app/src/main/res/drawable/ic_heart.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_live_tv_black_24dp.xml b/app/src/main/res/drawable/ic_live_tv_black_24dp.xml
new file mode 100644
index 000000000..1f7957c4a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_live_tv_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_live_tv_white_24dp.xml b/app/src/main/res/drawable/ic_live_tv_white_24dp.xml
new file mode 100644
index 000000000..303858f9d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_live_tv_white_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml
index c5aa806ed..f3930a3ad 100644
--- a/app/src/main/res/layout-large-land/activity_main_player.xml
+++ b/app/src/main/res/layout-large-land/activity_main_player.xml
@@ -159,6 +159,8 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/qualityTextView"
+ android:clickable="true"
+ android:focusable="true"
android:gravity="top"
android:orientation="vertical"
android:paddingLeft="8dp"
@@ -169,15 +171,16 @@
android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clickable="false"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
+ android:focusable="true"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="15sp"
android:textStyle="bold"
- android:clickable="true"
tools:ignore="RtlHardcoded"
tools:text="The Video Title LONG very LONG"/>
@@ -185,14 +188,15 @@
android:id="@+id/channelTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clickable="false"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
+ android:focusable="true"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="12sp"
- android:clickable="true"
tools:text="The Video Artist LONG very LONG very Long"/>
diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml
index 4bae123ee..799913d8d 100644
--- a/app/src/main/res/layout/activity_main_player.xml
+++ b/app/src/main/res/layout/activity_main_player.xml
@@ -157,6 +157,8 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/qualityTextView"
+ android:clickable="true"
+ android:focusable="true"
android:gravity="top"
android:orientation="vertical"
android:paddingLeft="8dp"
@@ -167,6 +169,7 @@
android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clickable="false"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:marqueeRepeatLimit="marquee_forever"
@@ -175,7 +178,6 @@
android:textColor="@android:color/white"
android:textSize="15sp"
android:textStyle="bold"
- android:clickable="true"
android:focusable="true"
tools:ignore="RtlHardcoded"
tools:text="The Video Title LONG very LONG"/>
@@ -184,6 +186,7 @@
android:id="@+id/channelTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clickable="false"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:marqueeRepeatLimit="marquee_forever"
@@ -191,7 +194,6 @@
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="12sp"
- android:clickable="true"
android:focusable="true"
tools:text="The Video Artist LONG very LONG very Long"/>
diff --git a/app/src/main/res/layout/dialog_feed_group_create.xml b/app/src/main/res/layout/dialog_feed_group_create.xml
index 17893fecc..255417730 100644
--- a/app/src/main/res/layout/dialog_feed_group_create.xml
+++ b/app/src/main/res/layout/dialog_feed_group_create.xml
@@ -37,6 +37,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
android:gravity="center_vertical"
android:hint="@string/feed_group_dialog_name_input"
android:paddingTop="6dp"
@@ -193,6 +194,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:minWidth="0dp"
android:scaleType="centerInside"
@@ -207,7 +209,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/confirm_button"
- android:text="@android:string/cancel" />
+ android:layout_toLeftOf="@+id/confirm_button"
+ android:text="@string/cancel" />
diff --git a/app/src/main/res/layout/dialog_playback_parameter.xml b/app/src/main/res/layout/dialog_playback_parameter.xml
index cdea6a8cc..7018248f2 100644
--- a/app/src/main/res/layout/dialog_playback_parameter.xml
+++ b/app/src/main/res/layout/dialog_playback_parameter.xml
@@ -4,9 +4,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
- android:paddingLeft="@dimen/video_item_search_padding"
- android:paddingRight="@dimen/video_item_search_padding"
- android:paddingTop="@dimen/video_item_search_padding">
+ android:paddingStart="6dp"
+ android:paddingTop="4dp"
+ android:paddingEnd="6dp">
@@ -346,32 +352,37 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/stepSizeSelector"
- android:layout_margin="@dimen/video_item_search_padding"
+ android:layout_marginStart="12dp"
+ android:layout_marginTop="6dp"
+ android:layout_marginEnd="12dp"
+ android:layout_marginBottom="6dp"
android:background="?attr/separator_color"/>
-
+ android:layout_height="match_parent"
+ android:layout_below="@id/separatorCheckbox"
+ android:orientation="vertical">
-
+
+
+
+
diff --git a/app/src/main/res/layout/error_retry.xml b/app/src/main/res/layout/error_retry.xml
index 583b304f7..c92879808 100644
--- a/app/src/main/res/layout/error_retry.xml
+++ b/app/src/main/res/layout/error_retry.xml
@@ -24,7 +24,6 @@
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/retry"
- android:textAlignment="center"
android:textAllCaps="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textSize="16sp"
diff --git a/app/src/main/res/layout/fragment_comments.xml b/app/src/main/res/layout/fragment_comments.xml
index 2e9b3ea2a..371f83335 100644
--- a/app/src/main/res/layout/fragment_comments.xml
+++ b/app/src/main/res/layout/fragment_comments.xml
@@ -42,6 +42,7 @@
tools:ignore="HardcodedText,UnusedAttribute"/>
@@ -55,8 +57,11 @@
android:layout_alignTop="@+id/refresh_info_container"
android:layout_alignBottom="@+id/refresh_info_container"
android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
android:layout_marginStart="6dp"
+ android:layout_marginLeft="6dp"
android:layout_marginEnd="12dp"
+ android:layout_marginRight="12dp"
app:srcCompat="?attr/ic_refresh"
tools:ignore="ContentDescription" />
@@ -70,15 +75,22 @@
android:background="?attr/separator_color" />
-
+ android:layout_below="@+id/refresh_root_view">
+
+
+
+
+ app:tabGravity="fill"
+ app:tabMinWidth="60dp"/>
diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml
index 7448fc205..8c91a19a6 100644
--- a/app/src/main/res/layout/fragment_video_detail.xml
+++ b/app/src/main/res/layout/fragment_video_detail.xml
@@ -42,6 +42,7 @@
android:id="@+id/detail_thumbnail_image_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="200dp"
android:background="@android:color/transparent"
android:contentDescription="@string/detail_thumbnail_view_description"
android:scaleType="fitCenter"
diff --git a/app/src/main/res/layout/header_divider_item_layout.xml b/app/src/main/res/layout/header_divider_item_layout.xml
index 29e080d7c..b0dab1da4 100644
--- a/app/src/main/res/layout/header_divider_item_layout.xml
+++ b/app/src/main/res/layout/header_divider_item_layout.xml
@@ -18,6 +18,7 @@
android:layout_gravity="center_vertical"
android:textColor="?android:attr/textColorPrimary"
android:layout_marginStart="16dp"
+ android:layout_marginLeft="16dp"
android:textSize="14sp"
android:textStyle="bold"
tools:text="@string/fragment_feed_title"/>
@@ -27,6 +28,8 @@
android:layout_height="2dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
+ android:layout_marginLeft="16dp"
android:layout_marginEnd="4dp"
+ android:layout_marginRight="4dp"
android:background="?attr/separator_color"/>
\ No newline at end of file
diff --git a/app/src/main/res/layout/header_with_menu_item.xml b/app/src/main/res/layout/header_with_menu_item.xml
index 580e8db4d..8ab6c7187 100644
--- a/app/src/main/res/layout/header_with_menu_item.xml
+++ b/app/src/main/res/layout/header_with_menu_item.xml
@@ -27,6 +27,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
+ android:layout_marginLeft="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
tools:src="?attr/ic_bookmark" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_channel_item.xml b/app/src/main/res/layout/list_channel_item.xml
index 547054cc0..86c3c34cc 100644
--- a/app/src/main/res/layout/list_channel_item.xml
+++ b/app/src/main/res/layout/list_channel_item.xml
@@ -1,32 +1,19 @@
-
-
-
-
+
+
+
diff --git a/app/src/main/res/layout/list_comments_item.xml b/app/src/main/res/layout/list_comments_item.xml
index 1cbafdb6b..2e8f4afe7 100644
--- a/app/src/main/res/layout/list_comments_item.xml
+++ b/app/src/main/res/layout/list_comments_item.xml
@@ -72,6 +72,18 @@
tools:ignore="RtlHardcoded"
tools:text="12M" />
+
+
@@ -106,7 +118,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/itemCommentContentView"
android:layout_marginLeft="12dp"
- android:layout_toRightOf="@id/detail_thumbs_up_count_view"
+ android:layout_toRightOf="@id/detail_heart_image_view"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
diff --git a/app/src/main/res/layout/list_radio_icon_item.xml b/app/src/main/res/layout/list_radio_icon_item.xml
index d4d05d104..6d92a3217 100644
--- a/app/src/main/res/layout/list_radio_icon_item.xml
+++ b/app/src/main/res/layout/list_radio_icon_item.xml
@@ -17,5 +17,5 @@
android:paddingStart="?attr/listPreferredItemPaddingLeft"
android:background="?attr/checked_selector"
android:textColor="?attr/textColorAlertDialogListItem"
- tools:drawableLeft="?attr/ic_play"
+ tools:drawableLeft="?attr/ic_play_arrow"
tools:text="Lorem ipsum dolor sit amet" />
diff --git a/app/src/main/res/layout/list_stream_item.xml b/app/src/main/res/layout/list_stream_item.xml
index ca3db5b1e..8e521bbac 100644
--- a/app/src/main/res/layout/list_stream_item.xml
+++ b/app/src/main/res/layout/list_stream_item.xml
@@ -1,10 +1,10 @@
-
+ android:progressDrawable="?progress_horizontal_drawable"
+ app:layout_constraintEnd_toEndOf="@+id/itemThumbnailView"
+ app:layout_constraintStart_toStartOf="@+id/itemThumbnailView"
+ app:layout_constraintTop_toBottomOf="@+id/itemThumbnailView" />
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/download_menu.xml b/app/src/main/res/menu/download_menu.xml
index ced932bcc..82150635b 100644
--- a/app/src/main/res/menu/download_menu.xml
+++ b/app/src/main/res/menu/download_menu.xml
@@ -26,4 +26,8 @@
android:icon="?attr/ic_delete"
android:title="@string/clear_download_history"
app:showAsAction="ifRoom" />
+
+
diff --git a/app/src/main/res/menu/menu_local_playlist.xml b/app/src/main/res/menu/menu_local_playlist.xml
index a955e9539..41791b791 100644
--- a/app/src/main/res/menu/menu_local_playlist.xml
+++ b/app/src/main/res/menu/menu_local_playlist.xml
@@ -2,8 +2,12 @@
+
+ app:showAsAction="never" />
diff --git a/app/src/main/res/menu/menu_playlist.xml b/app/src/main/res/menu/menu_playlist.xml
index 9a60caf54..e4c20a5ea 100644
--- a/app/src/main/res/menu/menu_playlist.xml
+++ b/app/src/main/res/menu/menu_playlist.xml
@@ -7,7 +7,7 @@
android:id="@+id/menu_item_share"
android:icon="?attr/ic_share"
android:title="@string/share"
- app:showAsAction="ifRoom"/>
+ app:showAsAction="ifRoom" />
+ tools:visible="true" />
+ app:showAsAction="never" />
+ app:showAsAction="never" />
\ No newline at end of file
diff --git a/app/src/main/res/menu/video_detail_menu.xml b/app/src/main/res/menu/video_detail_menu.xml
index 609b5a08c..6fd00b13f 100644
--- a/app/src/main/res/menu/video_detail_menu.xml
+++ b/app/src/main/res/menu/video_detail_menu.xml
@@ -25,4 +25,10 @@
android:orderInCategory="2"
android:title="@string/open_in_browser"
app:showAsAction="never"/>
+
+
diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml
deleted file mode 100644
index a6b3daec9..000000000
--- a/app/src/main/res/values-af/strings.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 2d007a043..8e70afd10 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -46,7 +46,7 @@
عرض خيار التشغيل بواسطة كودي
السمة
تم النشر بتاريخ %1$s
- رابط URL غير معتمد
+ رابط URL غير معتمد
استخدام مشغل صوت خارجي
استخدام مشغل فيديو خارجي
(إختبارية) إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية ( تشغيل الفيديو المباشر غير مدعوم حتى الأن ).
diff --git a/app/src/main/res/values-b+ast/strings.xml b/app/src/main/res/values-b+ast/strings.xml
index 213c6e884..5d7de2856 100644
--- a/app/src/main/res/values-b+ast/strings.xml
+++ b/app/src/main/res/values-b+ast/strings.xml
@@ -276,7 +276,7 @@
Tempu
Tonu
Refugar
- La URL nun se sofita
+ La URL nun se sofita
Reproduciendo en segundu planu
Reproduciendo nel mou ventanu
Atroxa llocalmente les consultes de gueta
diff --git a/app/src/main/res/values-b+zh+HANS+CN/strings.xml b/app/src/main/res/values-b+zh+HANS+CN/strings.xml
index 40712041e..fa7dbd6d6 100644
--- a/app/src/main/res/values-b+zh+HANS+CN/strings.xml
+++ b/app/src/main/res/values-b+zh+HANS+CN/strings.xml
@@ -29,7 +29,7 @@
黑色
下载
下一个
- 不支持的 URL
+ 不支持的 URL
外观
其他
全部
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index fbd7a99c0..c8f026ab9 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -85,7 +85,7 @@
\"Наступнае\" и \"Прапанаванае\" відэа
\"Зацісніце, каб дадаць\"
Паказаць падказку пры націсканні \"У акне\" ці \"У фоне\" на старонцы звестак аб відэа
- URL не падтрымліваецца
+ URL не падтрымліваецца
Краіна кантэнту па змаўчанні
Сэрвіс
Мова кантэнту па змаўчанні
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index 400c8d280..0d41f3186 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -68,7 +68,7 @@
Следващ клип
Показвай „следващ“ и „подобни“
Показвай съвет „задръж за добавяне“
- Непознат URL
+ Непознат URL
Език на съдържанието по подразбиране
Плейър
Поведение
diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml
index 0299457c3..264b767c2 100644
--- a/app/src/main/res/values-bn-rBD/strings.xml
+++ b/app/src/main/res/values-bn-rBD/strings.xml
@@ -49,7 +49,7 @@
ডাউনলোড
পরবর্তী ভিডিও
পরবর্তী এবং অনুরূপ ভিডিওগুলি দেখাও
- URL সমর্থিত নয়
+ URL সমর্থিত নয়
কন্টেন্ট এর জন্য পছন্দসই ভাষা
ভিডিও এবং অডিও
পপআপ
diff --git a/app/src/main/res/values-bn-rIN/strings.xml b/app/src/main/res/values-bn-rIN/strings.xml
index 49993d209..0a1ebab09 100644
--- a/app/src/main/res/values-bn-rIN/strings.xml
+++ b/app/src/main/res/values-bn-rIN/strings.xml
@@ -98,7 +98,7 @@
প্লেয়ার
কন্টেন্ট এর জন্য পছন্দসই ভাষা
সেবা
- URL সমর্থিত নয়
+ URL সমর্থিত নয়
পরবর্তী এবং অনুরূপ ভিডিওগুলি দেখাও
পরবর্তী ভিডিও
ডাউনলোড
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index d77c6a63a..abb65418d 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -281,7 +281,7 @@
Emmagatzema les cerques localment
Crea un historial de vídeos visualitzats
Reprèn automàticament
- Aquest URL no és compatible
+ Aquest URL no és compatible
Informe d\'error
Més tard
Filtra
diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml
index c913207cc..ba7b73e22 100644
--- a/app/src/main/res/values-ckb/strings.xml
+++ b/app/src/main/res/values-ckb/strings.xml
@@ -557,7 +557,7 @@
دامەزراندن
شوێنی داگرتن دانرا \'%1$s\'
ڤیدیۆکان
- بەستەرەکە پشتگیری نەکراوە
+ بەستەرەکە پشتگیری نەکراوە
شەپۆلی دەنگ
کرداری کاتی گۆڕین بۆ داوانامەیەکی تر لە کارپێکەری ڤیدیۆییەوە — %s
لەبەرگیرایەوە
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index f18da7ba0..b0d16cc44 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -35,7 +35,7 @@
Stáhnout
Další
Zobrazovat \'další\' a \'podobná\' videa
- URL není podporováno
+ URL není podporováno
Preferovaný jazyk obsahu
Video a zvuk
Vzhled
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 91ca1040f..3cc1cef30 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -92,7 +92,7 @@
Vis \'Næste\' og \'Lignende\' videoer
Vis \"Hold for at tilføje\"-tip
Vis et tip når der trykkes på baggrunds- eller pop op-knappen på siden med videodetaljer
- Denne webadresse er ikke understøttet
+ Denne webadresse er ikke understøttet
Standardland for indhold
Tjeneste
Standardsprog for indhold
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 20c4e3286..edbfe61c3 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -27,7 +27,7 @@
Herunterladen
Nächste
„Nächste“ und „Ähnliche“ Videos anzeigen
- Nicht unterstützte URL
+ Nicht unterstützte URL
Video & Audio
Bevorzugte Sprache des Inhalts
Video-Vorschaubild
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index ed5a6b299..ca14bcd31 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -35,7 +35,7 @@
Λήψη
Επόμενο
Εμφάνιση \"Επόμενου\" και \"Σχετικών\" βίντεο
- Δεν υποστηρίζεται η διεύθυνση URL
+ Δεν υποστηρίζεται η διεύθυνση URL
Προεπιλεγμένη γλώσσα περιεχομένου
Βίντεο & Ήχος
Εμφάνιση
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index f219ee2e8..44158131f 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -25,7 +25,7 @@
Luma
Elŝuti
Vica filmeto
- Ligilo ne subtenita
+ Ligilo ne subtenita
Preferata enhavlingvo
Filmeto kaj sono
Apero
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index cbff74f70..8beea383e 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -27,7 +27,7 @@
Formato de audio predeterminado
Descargar
Siguiente
- No se admite el URL
+ No se admite el URL
Usar reproductor de vídeo externo
Usar reproductor de audio externo
Tema
diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml
index a32d26af2..383c16e89 100644
--- a/app/src/main/res/values-et/strings.xml
+++ b/app/src/main/res/values-et/strings.xml
@@ -84,7 +84,7 @@
Kuva \'järgmine\' ja \'sarnased\' videod
Kuva vihjet \"lisamiseks hoia\"
Kuva vihje, kui videoandmete lehel vajutatakse tausta või hüpikakna nupule
- Mitte toetatud URL
+ Mitte toetatud URL
Sisu vaikimisi riik
Teenus
Sisu vaikimisi keel
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index db02eaedc..2419cecd1 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -25,7 +25,7 @@
Deskargatu
Hurrengoa
Erakutsi \'hurrengo\' eta \'antzeko\' bideoak
- URLak ez du euskarririk
+ URLak ez du euskarririk
Edukiaren hizkuntz lehenetsia
Bideoa eta Audioa
Erreproduzitu
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index 8f40e68ff..4ebc7ebfb 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -38,7 +38,7 @@
بارگیری
بعدی
نماش ویدیوهای «بعدی» و «مشابه»
- نشانی پشتیبانی نشده
+ نشانی پشتیبانی نشده
زبان محتوای ترجیحی
ویدیو و صدا
ظاهر
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index d325f7ee4..d2822b5e9 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -67,7 +67,7 @@
Lataus
Seuraava
Näytä seuraavia ja samankaltaisia videoita
- URL ei tuettu
+ URL ei tuettu
Oletus-sisällon kieli
Soitin
Käyttäytyminen
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 0d14f1390..a3b1e880b 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -27,7 +27,7 @@
Télécharger
Suivant
Afficher les vidéos « Suivantes » et « Similaires »
- URL non pris en charge
+ URL non pris en charge
Vidéo et audio
Autre
Miniature d’aperçu vidéo
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 8450d60c8..9c6ac9a8d 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -90,7 +90,7 @@
Mostrar vídeos «seguintes» e «semellantes»
Mostrar a suxestión «Manteña presionado para engadir á cola»
Mostrar unha suxestión ao premer o botón de segundo plano ou o de popup na páxina de detalles do vídeo
- Este URL non está soportado
+ Este URL non está soportado
País predeterminado para o contido
Servizo
Reprodutor
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 0ac642b19..18a3447ab 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -54,7 +54,7 @@
הורדה
הבא
להציג סרטונים דומים ובאים בתור
- כתובת לא נתמכת
+ כתובת לא נתמכת
שפת התוכן המועדפת
סרטונים ושמע
חלון צף
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index ad1f3f5fc..58095e006 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -96,7 +96,7 @@
\'अगला\' और \'पहले समान\' वीडियो दिखाए
\"जोड़ने के लिए पकड़ें रहे\" दिखाए
जब बैकग्राउंड और पॉपअप बटन विडियो के विवरण पन्ने में दबाई जाए तो tip को दिखाए
- ये वाला URL इसमें नहीं चलेगा
+ ये वाला URL इसमें नहीं चलेगा
डिफ़ॉल्ट विषय की भाषा
प्लेयर
चाल चलन
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 4512e265b..2cfb65def 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -68,7 +68,7 @@
Preuzmi
Sljedeće
Prikaži \'Sljedeće\' i \'Slične\' videozapise
- URL nije podržan
+ URL nije podržan
Zadani jezik sadržaja
Video i zvuk
Skočni prozor
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 1dcfb9413..ec0a99b6a 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -26,7 +26,7 @@
Alapértelmezett hang formátum
Letöltés
Következő
- Nem támogatott webcím
+ Nem támogatott webcím
Külső videólejátszó használata
Külső hanglejátszó használata
Válaszd ki a hangfájlok letöltési helyét
diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml
index 150046340..25eb5c65e 100644
--- a/app/src/main/res/values-ia/strings.xml
+++ b/app/src/main/res/values-ia/strings.xml
@@ -66,7 +66,7 @@
Sequente
Reproduction automatic
Monstrar le videos sequente e simile
- URL non supportate
+ URL non supportate
Pais predefinite del contentos
Servicio
Lingua predefinite del contento
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index cc976ecff..1f77505c3 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -34,7 +34,7 @@
Unduh
Berikutnya
Tampilkan video \'Berikutnya\' dan \'Serupa\'
- URL tidak didukung
+ URL tidak didukung
Bahasa konten
Video & Audio
Tampilan
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 0b2b3b44b..5f020e57e 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -27,7 +27,7 @@
Scarica
Prossimo
Mostra video \"Prossimo\" e \"Simili\"
- URL non supportato
+ URL non supportato
Lingua Predefinita per Contenuti
Video e Audio
Copertina di anteprima video
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index f66930a0c..7a150f639 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -26,7 +26,7 @@
保存
次
「次の動画」と「関連動画」を表示
- URLは使用できません
+ URLは使用できません
優先言語
動画と音声
%1$s ビュー
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 78550ca5e..e7b13c1c6 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -27,7 +27,7 @@
다운로드
다음
다음/유사한 비디오 표시
- 지원하지 않는 URL입니다
+ 지원하지 않는 URL입니다
기본 컨텐츠 언어
비디오 & 오디오
비디오 미리보기 썸네일
diff --git a/app/src/main/res/values-ku/strings.xml b/app/src/main/res/values-ku/strings.xml
index 47c46cc49..7fee584fc 100644
--- a/app/src/main/res/values-ku/strings.xml
+++ b/app/src/main/res/values-ku/strings.xml
@@ -81,7 +81,7 @@
داگرتن
دواتر
پیشاندانی ’دواتر’ و ڤیدیۆ ’هاوشێوەکان’
- بەستەرەکە پشتگیری نەکراوە
+ بەستەرەکە پشتگیری نەکراوە
وڵاتی بنەڕەتی
خزمەتگوزاری
کارپێکەر
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index fb05da09c..562fce75c 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -56,7 +56,7 @@
Atsisiųsti
Kitas vaizdo įrašas
Rodyti kitus panašius vaizdo įrašus
- URL nepalaikoma
+ URL nepalaikoma
Numatytoji tūrinio kalba
Vaizdas ir garsas
Iššokantis langas
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 660148f01..95614bd56 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -85,7 +85,7 @@
Прикажи „следни“ и „слични“ видеа
Прикажи „задржи за прикачување“
Покажи совет при притискање на позадината или кога скок копчето е притиснато на видео \"Детали:\"
- Неподдржана URL врска
+ Неподдржана URL врска
Земја на прикажани видеа
Услуга
Стандарден јазик на содржина
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index bc27edaa4..443182030 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -366,7 +366,7 @@
സ്ഥിര കന്റെന്റ് ഭാഷ
സേവനം
സ്ഥിര കന്റെന്റ് രാജ്യം
- അനുയോജ്യമല്ലാത്ത URL
+ അനുയോജ്യമല്ലാത്ത URL
പോപ്പപ്പ്/ബാക്ക്ഗ്രൗണ്ട് ബട്ടൺ അമർത്തുമ്പോൾ \"വിശദാംശങ്ങൾ\" എന്ന ടിപ് കാണിക്കും
\"ഹോൾഡ് ടു അപ്പെൻഡ്\" എന്ന ടിപ് കാണിക്കുക
\'അടുത്ത\' , \'സമാനമായ\' വീഡിയോകൾ കാണിക്കുക
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index 1944877f8..c9c24174f 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -92,7 +92,7 @@
Paparkan video \'Seterusnya\' dan \'Berkaitan\'
Tunjukkan tip \"Pegang untuk menambahkan\"
Tunjukkan tip apabila butang latar belakang atau popup ditekan pada halaman butiran video
- URL tidak disokong
+ URL tidak disokong
Negara kandungan utama
Perkhidmatan
Bahasa kandungan utama
diff --git a/app/src/main/res/values-nap/strings.xml b/app/src/main/res/values-nap/strings.xml
deleted file mode 100644
index a6b3daec9..000000000
--- a/app/src/main/res/values-nap/strings.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 6adb1c9f8..f8efff3f9 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -35,7 +35,7 @@
Last ned
Neste
Vis \"Neste\" og \"Lignende\" -videoer
- Nettadressen støttes ikke
+ Nettadressen støttes ikke
Foretrukket innholdsspråk
Video og lyd
Utseende
diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml
index aac2a27ae..5cfba6eff 100644
--- a/app/src/main/res/values-ne/strings.xml
+++ b/app/src/main/res/values-ne/strings.xml
@@ -94,7 +94,7 @@
\'अर्को\' र \'समान\' भिडियो देखाउन
\"होल्ड संलग्न गर्न\" टिप देखाउन
पृष्ठभूमि वा भिडियो मा पपअप बटन थिच्दा टिप देखाउन \"विवरण:\"
- असमर्थित URL
+ असमर्थित URL
पूर्वनिर्धारित सामग्री देश
सेवा
पूर्वनिर्धारित सामग्री भाषा
diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml
index a80cec9cc..813831a6b 100644
--- a/app/src/main/res/values-nl-rBE/strings.xml
+++ b/app/src/main/res/values-nl-rBE/strings.xml
@@ -85,7 +85,7 @@
Toont ‘Volgende’ en ‘Vergelijkbare’ video’s
Toont tip ‘Ingedrukt houden voor toe te voegen’
Toont tip wanneer dat den achtergrond- of pop-upknop wordt ingedrukt op de videogegevenspagina
- URL wordt niet ondersteund
+ URL wordt niet ondersteund
Standaardinhoudsland
Dienst
Standaardtaal voor inhoud
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index ca4539a60..195b6d068 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -26,7 +26,7 @@
Standaardaudioformaat
Downloaden
Volgende
- URL wordt niet ondersteund
+ URL wordt niet ondersteund
‘volgende’ en ‘vergelijkbare’ video’s weergeven
Standaardtaal voor inhoud
Externe videospeler gebruiken
diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml
index 6a84b99a4..bbc3d739e 100644
--- a/app/src/main/res/values-pa/strings.xml
+++ b/app/src/main/res/values-pa/strings.xml
@@ -1,113 +1,120 @@
- ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਸਰਚ ਦਬਾਓ
- %1$s VIEWS
- %1$s ਨੂੰ ਪਬਲਿਸ਼ ਕੀਤੀ ਗਈ
- ਸਟ੍ਰੀਮ ਪਲੇਅਰ ਨਹੀਂ ਮਿਲਿਆ। ਤੁਸੀਂ VLC ਭਰਨਾ ਚਾਹੋਗੇ \?
- ਸਟ੍ਰੀਮ ਪਲੇਅਰ ਨਹੀਂ ਮਿਲਿਆ ਤੁਸੀਂ VLC ਇੰਸਟਾਲ ਕਰ ਸਕਦੇ ਹੋ.
- ਇੰਸਟਾਲ
+ ਵੱਡਦਰਸ਼ੀ ਕੱਚ \'ਤੇ ਟੈਪ ਕਰਕੇ ਸਰਚ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰੋ।
+ %1$s ਵਿਊਜ਼
+ %1$s ਨੂੰ ਜਾਰੀ ਕੀਤੀ ਗਈ
+ ਕੋਈ ਸਟ੍ਰੀਮ ਪਲੇਅਰ ਨਹੀਂ ਮਿਲਿਆ । ਤੁਸੀਂ ਵੀਐੱਲਸੀ ਇੰਸਟਾਲ ਕਰਨਾ ਚਾਹੋਗੇ \?
+ ਸਟ੍ਰੀਮ ਪਲੇਅਰ ਨਹੀਂ ਮਿਲਿਆ (ਤੁਸੀਂ ਵੀਐੱਲਸੀ ਇੰਸਟਾਲ ਕਰਕੇ ਇਸਨੂੰ ਚਲਾ ਸਕਦੇ ਹੋ)
+ ਇੰਸਟਾਲ ਕਰੋ
ਰੱਦ ਕਰੋ
- ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਖੋਲੋ
- ਪੌਪ-ਅਪ ਵਿਚ ਖੋਲੋ
+ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਖੋਲ੍ਹੋ
+ ਪੌਪ-ਅਪ ਵਿੱਚ ਖੋਲੋ
ਸਾਂਝਾ ਕਰੋ
+ ਮੀਡੀਆ URL ਸਾਂਝਾ ਕਰੋ
ਡਾਊਨਲੋਡ
- ਡਾਊਨਲੋਡ ਸਟ੍ਰੀਮ ਫਾਈਲ
+ ਸਟ੍ਰੀਮ ਫ਼ਾਈਲ ਡਾਊਨਲੋਡ ਕਰੋ
ਖੋਜੋ
ਸੈਟਿੰਗਾਂ
ਕੀ ਤੁਹਾਡਾ ਮਤਲਬ: %1$s\?
ਸਾਂਝਾ ਕਰੋ
ਬ੍ਰਾਊਜ਼ਰ ਚੁਣੋ
- ਰੋਟੇਸ਼ਨ
- ਹੋਰ ਵੀਡੀਓ ਪਲੇਅਰ ਦੀ ਵਰਤੋਂ ਕਰੋ
- ਕੁਝ ਰੇਸੋਲਿਯੁਸ਼ਨ ਤੇ ਆਵਾਜ਼ ਨੂੰ ਹਟਾਉਂਦਾ ਹੈ
- ਬਾਹਰੀ ਆਡੀਓ ਪਲੇਅਰ ਦੀ ਵਰਤੋਂ ਕਰੋ
- NewPipe ਪੌਪ-ਅਪ ਮੋਡ
- ਸਬਸਕ੍ਰਾਈਬ
- ਸਬਸਕ੍ਰਾਈਬ ਕੀਤਾ ਹੈ
- ਚੈਨਲ ਅਨ-ਸਬਸਕ੍ਰਾਈਬ
- ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਬਦਲਣ ਵਿਚ ਅਸਮਰੱਥ
- ਜਾਣਕਾਰੀ ਵੇਖੋ
+ ਘੁਮਾਉਣ
+ ਬਾਹਰੀ ਵੀਡੀਓ ਪਲੇਅਰ ਵਰਤੋ
+ ਕੁਝ ਰੈਜ਼ੋਲਿਊਸ਼ਨਾਂ\'ਤੇ ਆਵਾਜ਼ ਨੂੰ ਹਟਾ ਦਿੰਦਾ ਹੈ
+ ਬਾਹਰੀ ਆਡੀਓ ਪਲੇਅਰ ਵਰਤੋ
+ ਪੌਪ-ਅਪ ਮੋਡ
+ ਸਬਸਕ੍ਰਾਈਬ ਕਰੋ
+ ਸਬਸਕ੍ਰਾਈਬ ਹੈ
+ ਅਨ-ਸਬਸਕ੍ਰਾਈਬ ਕਰੋ
+ ਚੈਨਲ ਅਨ-ਸਬਸਕ੍ਰਾਈਬ ਹੋ ਗਿਆ
+ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਬਦਲਣ ਵਿੱਚ ਨਾਕਾਮੀ
+ ਜਾਣਕਾਰੀ ਵਿਖਾਓ
ਮੁੱਖ
- ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਅੱਪਡੇਟ ਕਰਨ ਵਿਚ ਅਸਮਰੱਥ
- ਸਬਸਕ੍ਰਿਪਸ਼ਨ
+ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਅੱਪਡੇਟ ਕਰਨ ਵਿਚ ਨਾਕਾਮੀ
+ ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ
ਬੁੱਕਮਾਰਕ ਪਲੇਲਿਸਟਾਂ
+ ਨਵੀਂ ਟੈਬ
+ ਟੈਬ ਚੁਣੋ
ਨਵਾਂ ਕੀ ਹੈ
- ਬੈਕਗਰਾਊਂਡ
- ਪੋਪ-ਅਪ
+ ਬੈਕਗ੍ਰਾਊਂਡ ਆਡੀਓ
+ ਪੌਪ-ਅਪ
ਸ਼ਾਮਿਲ ਕਰੋ
- ਵੀਡੀਓ ਦਾ ਡਾਊਨਲੋਡ ਮਾਰਗ
- ਡਾਉਨਲੋਡ ਕੀਤੇ ਵੀਡੀਓ ਨੂੰ ਸਟੋਰ ਕਰਨ ਦਾ ਮਾਰਗ
- ਵੀਡੀਓ ਲਈ ਡਾਊਨਲੋਡ ਮਾਰਗ ਭਰੋ
- ਆਡੀਓ ਦਾ ਡਾਊਨਲੋਡ ਫੋਲਡਰ
- ਡਾਊਨਲੋਡ ਕੀਤੇ ਆਡੀਓ ਇੱਥੇ ਸਟੋਰ ਹਨ
- ਆਡੀਓ ਫਾਈਲਾਂ ਲਈ ਡਾਊਨਲੋਡ ਮਾਰਗ ਭਰੋ
- ਆਟੋ ਪਲੇ
- ਜਦੋਂ ਕਿਸੇ ਹੋਰ ਐਪ ਜਰੀਏ NewPipe ਨੂੰ ਖੋਲਿਆ ਜਾਂਦਾ ਹੈ ਤਾਂ ਇਹ ਵੀਡੀਓ ਚਲਾਉਂਦਾ ਹੈ
- Default Resolution
- Default ਪੌਪ-ਅਪ Resolution
- ਉੱਚ ਰੇਸੋਲਿਯੁਸ਼ਨਾਂ ਵੀ ਦਿਖਾਓ
- ਸਿਰਫ ਕੁੱਝ ਉਪਕਰਣ ਹੀ 2K/4K ਵੀਡੀਓ ਵਿਖਾਉਣ ਵਿਚ ਸਮਰਥਨ ਹੁੰਦੇ ਹਨ
- Kodi ਨਾਲ ਚਲਾਓ
- Kore ਐਪ ਮੌਜੂਦ ਨਹੀਂ ਹੈ। ਕੀ ਤੁਸੀਂ ਇਸਨੂੰ ਇੰਸਟਾਲ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ \?
- \"Kodi ਨਾਲ ਚਲਾਓ\" ਵਿਕਲਪ ਦਿਖਾਓ
- Kodi ਮੀਡੀਆ ਸੈਂਟਰ ਰਾਹੀਂ ਵੀਡੀਓ ਚਲਾਉਣ ਲਈ ਇੱਕ ਵਿਕਲਪ ਵਿਖਾਓ
+ ਵੀਡੀਓ ਲਈ ਡਾਊਨਲੋਡ ਫ਼ੋਲਡਰ
+ ਡਾਉਨਲੋਡ ਕੀਤੇ ਵੀਡੀਓ ਇਸ ਥਾਂ ਤੇ ਸਟੋਰ ਹੋਣਗੇ
+ ਵੀਡੀਓ ਫ਼ਾਈਲਾਂ ਲਈ ਡਾਊਨਲੋਡ ਫ਼ੋਲਡਰ ਚੁਣੋ
+ ਆਡੀਓ ਲਈ ਡਾਊਨਲੋਡ ਫ਼ੋਲਡਰ
+ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਆਡੀਓ ਇੱਥੇ ਜਮ੍ਹਾਂ ਹੁੰਦੀਆਂ ਹਨ
+ ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਲਈ ਡਾਊਨਲੋਡ ਫ਼ੋਲਡਰ ਚੁਣੋ
+ ਆਟੋ-ਪਲੇਅ
+ ਜਦੋਂ ਕਿਸੇ ਹੋਰ ਐਪ ਜਰੀਏ ਨਿਊਪਾਈਪ ਨੂੰ ਖੋਲਿਆ ਜਾਂਦਾ ਹੈ ਤਾਂ ਇਹ ਵੀਡੀਓ ਚਲਾਉਂਦਾ ਹੈ
+ ਡਿਫ਼ਾਲਟ ਰੈਜ਼ੋਲਿਊਸ਼ਨ
+ ਪੌਪ-ਅਪ ਲਈ ਡਿਫ਼ਾਲਟ ਰੈਜ਼ੋਲਿਊਸ਼ਨ
+ ਉੱਚ ਰੈਜ਼ੋਲਿਊਸ਼ਨਾਂ ਵੀ ਵਿਖਾਓ
+ ਸਿਰਫ਼ ਕੁਝ ਹੀ ਡਿਵਾਈਸ 2K/4K ਵੀਡੀਓ ਨੂੰ ਚਲਾ ਸਕਦੇ ਹਨ
+ Kodi ਵਿੱਚ ਚਲਾਓ
+ Kore ਐਪ ਇੰਸਟਾਲ ਨਹੀਂ ਹੈ। ਕੀ ਤੁਸੀਂ ਇਸਨੂੰ ਇੰਸਟਾਲ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ \?
+ \"Kodi ਵਿੱਚ ਚਲਾਓ\" ਵਿਕਲਪ ਵਿਖਾਓ
+ Kodi ਮੀਡੀਆ ਸੈਂਟਰ ਰਾਹੀਂ ਵੀਡੀਓ ਚਲਾਉਣ ਲਈ ਵਿਕਲਪ ਵਿਖਾਓ
+ ਲਾਕ ਸਕਰੀਨ ਤੇ ਵੀਡੀਓ ਥੰਮਨੇਲ
+ ਬੈਕਗ੍ਰਾਊਂਡ ਪਲੇਅਰ ਵਰਤਦੇ ਸਮੇਂ ਲਾਕ ਸਕਰੀਨ ਤੇ ਵੀਡੀਓ ਥੰਮਨੇਲ ਵਿਖਾਉਂਦਾ ਹੈ
ਆਡੀਓ
- Default ਆਡੀਓ ਫਾਰਮੈਟ
- "Default ਵੀਡੀਓ ਫਾਰਮੈਟ"
+ ਡਿਫ਼ਾਲਟ ਆਡੀਓ ਫਾਰਮੈਟ
+ ਡਿਫ਼ਾਲਟ ਵੀਡੀਓ ਫਾਰਮੈਟ"
ਥੀਮ
ਸਫੈਦ
ਗੂੜਾ
- ਕਾਲਾ
+ ਕਾਲ੍ਹਾ
ਪੌਪ-ਅਪ ਦਾ ਆਕਾਰ ਅਤੇ ਸਥਿਤੀ ਯਾਦ ਰੱਖੋ
ਪੌਪ-ਅਪ ਦਾ ਆਖਰੀ ਅਕਾਰ ਅਤੇ ਸਥਿਤੀ ਯਾਦ ਰੱਖੋ
- ਤੇਜ਼ ਪਰ inexact seek ਵਰਤੋ
- Inexact seek ਵੀਡੀਓ ਨੂੰ ਤੇਜ਼ ਪਰ ਅਣ-ਸਟੀਕ ਢੰਗ ਨਾਲ ਅੱਗੇ-ਪਿੱਛੇ ਲਿਜਾਂਦਾ ਹੈ
- ਥੰਬਨੇਲ ਲੋਡ ਕਰੋ
- ਥੰਬਨੇਲ ਲੋਡ, ਡਾਟਾ ਦੀ ਬਚਤ ਅਤੇ ਮੈਮੋਰੀ ਦੀ ਵਰਤੋਂ ਨੂੰ ਰੋਕਣ ਲਈ ਇਸਨੂੰ ਬੰਦ ਕਰੋ। ਇਸ ਵਿਚ ਤਬਦੀਲੀ ਕਰਨ ਨਾਲ ਇਨ-ਮੈਮੋਰੀ ਅਤੇ ਆਨ-ਡਿਸਕ ਚਿੱਤਰ cache ਦੋਵੇਂ ਮਿਟ ਜਾਣਗੇ।
- ਚਿੱਤਰ cache ਮਿਟਾ ਦਿੱਤੀ ਗਈ ਹੈ
- Cached metadata ਮਿਟਾਓ
- ਸਾਰੇ cached ਵੈੱਬ-ਪੇਜਾਂ ਦਾ ਡਾਟਾ ਮਿਟਾਓ
- Metadata cache ਮਿਟਾ ਦਿੱਤੀ ਗਈ ਹੈ
+ ਤੇਜ਼ ਪਰ ਅਸਪੱਸ਼ਟ ਸੀਕ ਵਰਤੋ
+ ਅਸਪੱਸ਼ਟ ਸੀਕ ਵੀਡੀਓ ਨੂੰ ਤੇਜ਼ ਪਰ ਅਣ-ਸਟੀਕ ਢੰਗ ਨਾਲ ਅੱਗੇ-ਪਿੱਛੇ ਲਿਜਾਂਦਾ ਹੈ। ਇਸ ਨਾਲ ਅੱਗੇ-ਪਿੱਛੇ 5,15 ਜਾਂ 25 ਸੈਕੰਡ ਜਾਣਾ ਕੰਮ ਨਹੀਂ ਕਰੇਗਾ।
+ ਅੱਗੇ ਲੰਘਾਉਣ/ਪਿੱਛੇ ਕਰਨ ਦੀ ਸਮਾਂ ਮਿਆਦ
+ ਥੰਮਨੇਲ ਲੋਡ ਕਰੋ
+ ਥੰਮਨੇਲ ਲੋਡ, ਡਾਟਾ ਦੀ ਬਚਤ ਅਤੇ ਮੈਮੋਰੀ ਦੀ ਵਰਤੋਂ ਨੂੰ ਰੋਕਣ ਲਈ ਇਸਨੂੰ ਬੰਦ ਕਰੋ। ਇਸ ਵਿਚ ਤਬਦੀਲੀ ਕਰਨ ਨਾਲ ਇਨ-ਮੈਮੋਰੀ ਅਤੇ ਆਨ-ਡਿਸਕ ਚਿੱਤਰ ਕੈਸ਼ ਦੋਵੇਂ ਮਿਟ ਜਾਣਗੇ।
+ ਚਿੱਤਰ ਕੈਸ਼ ਮਿਟਾ ਦਿੱਤੀ ਗਈ ਹੈ
+ ਕੈਸ਼ ਕੀਤਾ ਮੈਟਾ-ਡਾਟਾ ਮਿਟਾਓ
+ ਸਾਰੇ ਕੈਸ਼ ਕੀਤੇ ਵੈੱਬ-ਪੇਜਾਂ ਦਾ ਡਾਟਾ ਮਿਟਾਓ
+ ਮੈਟਾ-ਡਾਟਾ ਕੈਸ਼ ਮਿਟਾ ਦਿੱਤੀ ਗਈ ਹੈ
ਅਗਲੀ ਸਟ੍ਰੀਮ ਨੂੰ ਆਟੋ-ਕਤਾਰਬੱਧ ਕਰੋ
- ਇੱਕ ਨਾ-ਦੁਹਰਾਉਣ ਵਾਲੀ ਕਤਾਰ ਵਿੱਚ ਆਖਰੀ ਸਟ੍ਰੀਮ ਨੂੰ ਚਲਾਉਣ ਵੇਲੇ ਆਪਣੇ-ਆਪ ਸ਼ਾਮਿਲ ਕਰੋ
- ਵੀਡੀਓ ਪਲੇਯਰ gesture ਕੰਟਰੋਲ
- ਸਕ੍ਰੀਨ ਲਾਈਟ ਅਤੇ ਆਵਾਜ਼ ਨੂੰ ਕੰਟਰੋਲ ਕਰਨ ਲਈ gestures ਦੀ ਵਰਤੋਂ ਕਰੋ
+ ਇੱਕ ਮੁੱਕਣ ਵਾਲੀ ਪਰ ਨਾ-ਦੁਹਰਾਉਣ ਵਾਲੀ ਕਤਾਰ ਨੂੰ, ਸੰਬੰਧਤ ਸਟ੍ਰੀਮ ਜੋੜਦਿਆਂ, ਚਲਾਉਂਦੇ ਜਾਓ
+ ਵੀਡੀਓ ਪਲੇਅਰ ਸੰਕੇਤ ਕੰਟਰੋਲ
+ ਸਕ੍ਰੀਨ ਦੀ ਚਮਕ ਅਤੇ ਆਵਾਜ਼ ਨੂੰ ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਸੰਕੇਤਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ
ਖੋਜ ਸੁਝਾਅ
- ਖੋਜ ਕਰਨ ਵੇਲੇ ਸੁਝਾਅ ਦਿਖਾਓ
+ ਖੋਜ ਕਰਨ ਵੇਲੇ ਸੁਝਾਅ ਵਿਖਾਓ
ਖੋਜ ਸੂਚੀ
- ਖੋਜ ਸੂਚੀ ਨੂੰ locally ਸਟੋਰ ਕਰੋ
+ ਖੋਜ ਇਤਲਾਹਾਂ ਨੂੰ ਸਥਾਨਕ ਤੌਰ ਤੇ ਸਟੋਰ ਕਰੋ
ਪਹਿਲਾਂ ਤੋਂ ਵੇਖੀਆਂ ਹੋਈਆਂ ਚੀਜ਼ਾਂ ਦੀ ਸੂਚੀ
- ਦੇਖੇ ਗਏ ਵਿਡੀਓਜ਼ ਦੀ ਸੂਚੀ ਰੱਖੋ
- Focus gain ਹੋਣ ਤੇ ਦੋਬਾਰਾ ਚਲਾਓ
- ਰੁਕਾਵਟਾਂ (ਜਿਵੇਂ ਕਿ ਫੋਨ ਕਾਲਾਂ,ਮੈਸੇਜ) ਤੋਂ ਬਾਅਦ ਪਲੇਅ-ਬੈਕ ਜਾਰੀ ਰੱਖੋ
+ ਵੇਖੇ ਗਏ ਵਿਡੀਓਜ਼ ਦੀ ਸੂਚੀ ਰੱਖੋ
+ ਫੋਕਸ ਮਿਲਣ ਤੇ ਚਲਾਉਣਾ ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ
+ ਵਿਘਨਾਂ (ਜਿਵੇਂ ਕਿ ਫ਼ੋਨ-ਕਾਲਾਂ, ਮੈਸੇਜ) ਤੋਂ ਬਾਅਦ ਚਲਾਉਣਾ ਜਾਰੀ ਰੱਖੋ
ਡਾਊਨਲੋਡ
ਅੱਗੇ
- \'ਅੱਗੇ\' ਅਤੇ \'ਸਮਾਨਅੰਤਰ\' ਵੀਡੀਓ ਦਿਖਾਓ
- \"Hold to append\" ਸੁਝਾਅ ਦਿਖਾਓ
- ਵੀਡੀਓ ਵੇਰਵੇ ਪੰਨੇ ਤੇ ਬੈਕਗ੍ਰਾਉਂਡ ਜਾਂ ਪੌਪ-ਅਪ ਬਟਨ ਦਬਾਉਣ ਤੇ ਸੰਕੇਤ ਦਿਖਾਓ
- ਅਣ-ਸਹਾਇਕ URL
- ਮੂਲ ਦੇਸ਼ Content
+ \'ਅੱਗੇ\' ਅਤੇ \'ਸਮਾਨਅੰਤਰ\' ਵੀਡੀਓ ਵਿਖਾਓ
+ \"ਜੋੜਨ ਲਈ ਬਟਨ ਦਬਾ ਕੇ ਰੱਖੋ\" ਵਿਖਾਓ
+ ਵੀਡੀਓ ਦੇ ਵੇਰਵੇ ਪੰਨੇ \'ਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਜਾਂ ਪੌਪ-ਅਪ ਬਟਨ ਨੱਪਣ \'ਤੇ ਸੰਕੇਤ ਵਿਖਾਓ
+ ਅਣ-ਸਹਾਇਕ URL
+ ਮੂਲ ਦੇਸ਼ ਸਮੱਗਰੀ
ਸੇਵਾ
- ਪਲੇਯਰ
+ ਪਲੇਅਰ
ਵਿਵਹਾਰ
ਵੀਡੀਓ ਅਤੇ ਆਡੀਓ
- ਹਿਸਟਰੀ ਅਤੇ cache
+ ਅਤੀਤ ਅਤੇ ਕੈਸ਼
ਪੌਪ-ਅਪ
ਦਿੱਖ
ਹੋਰ
ਡੀ-ਬੱਗ
- ਬੈਕਗ੍ਰਾਉਂਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ
+ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ
ਪੌਪ-ਅਪ ਮੋਡ ਵਿੱਚ ਚੱਲ ਰਿਹਾ ਹੈ
- ਬੈਕਗ੍ਰਾਉਂਡ ਪਲੇਅਰ ਵਿੱਚ ਕਤਾਰਬੱਧ
- ਪੌਪ-ਅਪ ਪਲੇਯਰ ਵਿੱਚ ਕਤਾਰਬੱਧ
- ਪਲੇ
- Content
- ਉਮਰ ਪ੍ਰਤੀਬੰਧਿਤ Content
- ਉਮਰ ਪ੍ਰਤੀਬੰਧਿਤ ਵੀਡੀਓ ਦਿਖਾਓ ਸੈਟਿੰਗਸ ਤੋਂ ਅਜਿਹੀ ਸਮੱਗਰੀ ਦੀ ਆਗਿਆ ਦੇਣੀ ਸੰਭਵ ਹੈ.
+ ਬੈਕਗ੍ਰਾਊਂਡ ਪਲੇਅਰ ਵਿੱਚ ਕਤਾਰਬੱਧ
+ ਪੌਪ-ਅਪ ਪਲੇਅਰ ਵਿੱਚ ਕਤਾਰਬੱਧ
+ ਪਲੇਅ
+ ਸਮੱਗਰੀ
+ ਉਮਰ-ਮੁਤਾਬਕ-ਪਾਬੰਦੀਸ਼ੁਦਾ ਸਮੱਗਰੀ ਵਿਖਾਓ
+ ਉਮਰ ਪ੍ਰਤੀਬੰਧਿਤ ਵੀਡੀਓ ਵਿਖਾਓ ਸੈਟਿੰਗਸ ਤੋਂ ਅਜਿਹੀ ਸਮੱਗਰੀ ਦੀ ਆਗਿਆ ਦੇਣੀ ਸੰਭਵ ਹੈ।
ਲਾਈਵ
ਡਾਊਨਲੋਡਸ
ਡਾਊਨਲੋਡਸ
- Error ਰਿਪੋਰਟ
+ ਤਰੁੱਟੀ ਰਿਪੋਰਟ
ਸਾਰੇ
ਚੈਨਲ
ਪਲੇ ਸੂਚੀ
@@ -118,82 +125,82 @@
ਤਾਜ਼ਾ ਕਰੋ
ਮਿਟਾਓ
ਮੁੜ ਆਕਾਰ
- ਵਧੀਆ Resolution
- ਵਾਪਿਸ
+ ਵਧੀਆ ਰੈਜ਼ੋਲਿਊਸ਼ਨ
+ ਅਣਕੀਤਾ ਕਰੋ
ਸਾਰੇ ਚਲਾਓ
- ਹਮੇਸ਼ਾ
+ ਹਮੇਸ਼ਾਂ
ਸਿਰਫ਼ ਇਸ ਬਾਰ
ਫਾਈਲ
- NewPipe ਨੋਟੀਫਿਕੇਸ਼ਨ
- NewPipe ਬੈਕਗ੍ਰਾਉਂਡ ਅਤੇ ਪੌਪ-ਅਪ ਪਲੇਅਰਾਂ ਲਈ ਸੂਚਨਾਵਾਂ
- ਅਣਜਾਣ
- Orientation ਬਦਲੋ
- ਬੈਕਗਰਾਊਂਡ ਵਿੱਚ ਬਦਲੋ
+ ਨਿਊਪਾਈਪ ਨੋਟੀਫਿਕੇਸ਼ਨ
+ ਨਿਊਪਾਈਪ ਬੈਕਗ੍ਰਾਊਂਡ ਅਤੇ ਪੌਪ-ਅਪ ਪਲੇਅਰਾਂ ਲਈ ਸੂਚਨਾਵਾਂ
+ [ਅਣਜਾਣ]
+ ਓਰੀਐਂਟੇਸ਼ਨ ਬਦਲੋ
+ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਬਦਲੋ
ਪੌਪ-ਅਪ ਵਿੱਚ ਬਦਲੋ
- ਮੁੱਖ ਵਿੱਚ ਬਦਲੋ
- Database ਆਯਾਤ ਕਰੋ
- Database ਨਿਰਯਾਤ ਕਰੋ
- ਤੁਹਾਡੀ ਮੌਜੂਦਾ ਹਿਸਟਰੀ ਅਤੇ ਸਬਸਕ੍ਰਿਪਸ਼ਨਸ ਨੂੰ Override ਕਰਦਾ ਹੈ
- ਹਿਸਟਰੀ, ਸੁਬਸਕ੍ਰਿਪਸ਼ਨਸ ਅਤੇ ਪਲੇ-ਸੂਚੀ ਨਿਰਯਾਤ ਕਰੋ
- Watch ਹਿਸਟਰੀ ਮਿਟਾਓ
- ਚਲਾਈਆਂ ਗਈਆਂ ਸਟ੍ਰੀਮਾਂ ਦੀ ਸੂਚੀ ਮਿਟਾਉਂਦਾ ਹੈ
- ਕੀ ਸਾਰੀ watch ਹਿਸਟਰੀ ਮਿਟਾ ਦਿੱਤੀ ਜਾਵੇ \?
+ ਮੇਨ ਤੇ ਚਲਾਓ
+ ਡਾਟਾਬੇਸ ਆਯਾਤ ਕਰੋ
+ ਡਾਟਾਬੇਸ ਨਿਰਯਾਤ ਕਰੋ
+ ਤੁਹਾਡੇ ਮੌਜੂਦਾ ਇਤਿਹਾਸ, ਸਬਸਕ੍ਰਿਪਸ਼ਨਸ, ਪਲੇ-ਸੂਚੀ ਅਤੇ (ਆਪਨਸ਼ਨਲੀ) ਸੈਟਿੰਗਾਂ ਨੂੰ ਨਵਿਆਂ ਨਾਲ਼ ਬਦਲ ਦਿੰਦਾ ਹੈ
+ ਇਤਿਹਾਸ, ਸੁਬਸਕ੍ਰਿਪਸ਼ਨਸ, ਪਲੇ-ਸੂਚੀ ਅਤੇ ਸੈਟਿੰਗਾਂ ਨਿਰਯਾਤ ਕਰੋ
+ ਦੇਖੇ ਗਏ ਵੀਡੀਓਜ਼ ਦੀ ਸੂਚੀ ਮਿਟਾਓ
+ ਚਲਾਏ ਗਏ ਵੀਡੀਓਜ਼ ਦੇ ਇਤਿਹਾਸ ਅਤੇ ਪਲੇ-ਸਥਿਤੀਆਂ ਨੂੰ ਮਿਟਾਉਂਦਾ ਹੈ
+ ਕੀ ਵੇਖੇ ਗਏ ਵੀਡੀਓਜ਼ ਦਾ ਇਤਿਹਾਸ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇ\?
ਖੋਜ ਸੂਚੀ ਮਿਟਾਓ
ਖੋਜ ਸ਼ਬਦਾਂ ਦੀ ਸੂਚੀ ਮਿਟਾਉਂਦਾ ਹੈ
ਕੀ ਸਾਰੀ ਖੋਜ ਸੂਚੀ ਮਿਟਾ ਦਿਤੀ ਜਾਵੇ \?
ਖੋਜ ਸੂਚੀ ਮਿਟਾ ਦਿਤੀ ਗਈ ਹੈ.
- ERROR
- ਨੈੱਟਵਰਕ ERROR
- ਸਾਰੇ ਥੰਬਨੇਲ ਲੋਡ ਨਹੀਂ ਹੋ ਸਕੇ
+ ਤਰੁੱਟੀ
+ ਨੈੱਟਵਰਕ ਤਰੁੱਟੀ
+ ਸਾਰੇ ਥੰਮਨੇਲ ਲੋਡ ਨਹੀਂ ਹੋ ਸਕੇ
ਵੀਡੀਓ URL ਦਸਤਖਤ ਡੀਕ੍ਰਿਪਟ ਨਹੀਂ ਹੋ ਸਕਿਆ
ਵੈਬਸਾਈਟ parse ਨਹੀਂ ਹੋ ਸਕੀ
ਵੈਬਸਾਈਟ ਪੂਰੀ ਤਰਾਂ Parse ਨਹੀਂ ਹੋ ਸਕੀ
- Content ਉਪਲਬਧ ਨਹੀਂ ਹੈ
+ ਸਮੱਗਰੀ ਉਪਲਬਧ ਨਹੀਂ ਹੈ
ਡਾਊਨਲੋਡ ਮੀਨੂੰ ਸੈਟ-ਅਪ ਨਹੀਂ ਹੋ ਸਕਿਆ
ਲਾਈਵ ਸਟ੍ਰੀਮ ਅਜੇ supported ਨਹੀਂ ਹਨ
ਕੋਈ ਸਟ੍ਰੀਮ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋ ਸਕੀ
ਚਿੱਤਰ ਲੋਡ ਨਹੀਂ ਹੋ ਸਕਿਆ
ਐਪ/UI crashed
ਇਸ ਸਟ੍ਰੀਮ ਨੂੰ ਚਲਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ
- ਅਣਚਾਹਾ ਪਲੇਅਰ ERROR ਆਇਆ ਹੈ
- ਪਲੇਅਰ ERROR ਤੋਂ Recover ਹੋ ਰਿਹਾ ਹੈ
- External ਪਲੇਅਰ ਇਸ ਕਿਸਮ ਦੇ ਲਿੰਕਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੇ
+ ਅਣਚਾਹੀ ਪਲੇਅਰ ਤਰੁੱਟੀ ਆਈ ਹੈ
+ ਪਲੇਅਰ ਤਰੁੱਟੀ ਤੋਂ ਮੁੜ-ਸੁਰਜੀਤ ਹੋ ਰਿਹਾ ਹੈ
+ ਬਾਹਰੀ ਪਲੇਅਰ ਇਸ ਕਿਸਮ ਦੇ ਲਿੰਕਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੇ
ਅਵੈਧ URL
ਕੋਈ ਵੀ ਵੀਡੀਓ ਸਟ੍ਰੀਮ ਨਹੀਂ ਮਿਲੀ
ਕੋਈ ਵੀ ਆਡੀਓ ਸਟ੍ਰੀਮ ਨਹੀਂ ਮਿਲੀ
- ਅਜਿਹਾ ਕੋਈ ਫੋਲਡਰ ਨਹੀਂ
- ਅਜਿਹਾ ਕੋਈ ਫਾਈਲ/Content ਸਰੋਤ ਨਹੀਂ ਹੈ
+ ਅਜਿਹਾ ਕੋਈ ਫ਼ੋਲਡਰ ਨਹੀਂ
+ ਅਜਿਹਾ ਕੋਈ ਫਾਈਲ/ਸਮੱਗਰੀ ਸਰੋਤ ਨਹੀਂ ਹੈ
ਫਾਈਲ ਮੌਜੂਦ ਨਹੀਂ ਹੈ ਜਾਂ ਇਸ ਨੂੰ ਪੜ੍ਹਨ ਜਾਂ ਲਿਖਣ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ
ਫਾਈਲ ਨਾਮ ਖਾਲੀ ਨਹੀਂ ਹੋ ਸਕਦਾ
- ਇੱਕ ERROR ਆਇਆ ਹੈ: %1$s
+ ਇੱਕ ਤਰੁੱਟੀ ਆਈ ਹੈ: %1$s
ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ ਕੋਈ ਸਟ੍ਰੀਮ ਉਪਲਬਧ ਨਹੀਂ ਹੈ
- ਮੁਆਫ ਕਰਨਾ, ਅਜਿਹਾ ਨਹੀਂ ਹੋਣਾ ਚਾਹੀਦਾ ਸੀ.
- ਈ-ਮੇਲ ਦੁਆਰਾ ERROR ਦੀ ਰਿਪੋਰਟ ਕਰੋ
- ਮੁਆਫ ਕਰਨਾ, ਕੁਝ ERROR ਹੋਏ ਹਨ.
+ ਮੁਆਫ ਕਰਨਾ, ਅਜਿਹਾ ਨਹੀਂ ਹੋਣਾ ਚਾਹੀਦਾ ਸੀ।
+ ਈ-ਮੇਲ ਦੁਆਰਾ ਇਸ ਤਰੁੱਟੀ ਦੀ ਇਤਲਾਹ ਦਿਓ
+ ਅਫ਼ਸੋਸ ਹੈ, ਕੁਝ ਤਰੁੱਟੀਆਂ ਸਾਹਮਣੇ ਆਈਆਂ ਹਨ।
ਰਿਪੋਰਟ
ਜਾਣਕਾਰੀ:
ਕੀ ਹੋਇਆ ਹੈ:
- ਕਿਸ ਬਾਰੇ:\\nRequest:\\nContent ਭਾਸ਼ਾ:\\nService:\\nGMT ਸਮਾਂ:\\nPackage:\\nVersion:\\nOS version:
+ ਕੀ:\\nRequest:\\nContent ਭਾਸ਼ਾ/ਬੋਲੀ:\\nContent Country:\\nApp ਭਾਸ਼ਾ/ਬੋਲੀ:\\nService:\\nGMT ਸਮਾਂ:\\nPackage:\\nVersion:\\nOS version:
ਤੁਹਾਡੀ ਟਿੱਪਣੀ (ਅੰਗਰੇਜ਼ੀ ਵਿਚ):
ਵੇਰਵੇ:
- ਵੀਡੀਓ preview thumbnail
- ਵੀਡੀਓ preview thumbnail
- ਅਪਲੋਡਰ ਦਾ ਅਵਤਾਰ thumbnail
- ਪਸੰਦ
- ਨਾਪਸੰਦ
+ ਵੀਡੀਓ ਪਰੀਵਿਊ ਥੰਮਨੇਲ
+ ਵੀਡੀਓ ਚਲਾਓ, ਮਿਆਦ:
+ ਅਪਲੋਡਰ ਦਾ ਅਵਤਾਰ ਥੰਮਨੇਲ
+ ਪਸੰਦਾਂ
+ ਨਾਪਸੰਦਾਂ
TOR ਦੀ ਵਰਤੋਂ ਕਰੋ
- (ਪ੍ਰਯੋਗ ਅਧੀਨ) ਗੋਪਨੀਯਤਾ ਲਈ TOR ਦੁਆਰਾ ਟਰੈਫਿਕ ਨੂੰ ਜਬਰੀ Download ਹੋਣ ਲਈ ਮਜਬੂਰ ਕਰੋ (ਸਟ੍ਰੀਮਿੰਗ ਵੀਡੀਓ ਅਜੇ supported ਨਹੀਂ ਹਨ).
- ERROR ਰਿਪੋਰਟ ਕਰੋ
- ਯੂਸਰ ਰਿਪੋਰਟ
+ (ਪ੍ਰਯੋਗ ਅਧੀਨ) ਗੋਪਨੀਯਤਾ ਲਈ TOR ਦੁਆਰਾ ਟਰੈਫਿਕ ਨੂੰ ਜਬਰੀ ਡਾਊਨਲੋਡ ਹੋਣ ਲਈ ਮਜਬੂਰ ਕਰੋ (ਸਟ੍ਰੀਮਿੰਗ ਵੀਡੀਓ ਅਜੇ supported ਨਹੀਂ ਹਨ).
+ ERROR ਦੀ ਇਤਲਾਹ ਦਿਓ
+ ਵਰਤੋਂਕਾਰ ਦੀ ਰਿਪੋਰਟ
ਕੋਈ ਨਤੀਜੇ ਨਹੀਂ
- ਇਥੇ ਦਾ ਸੁੰਨਾਪਨ ਦੂਰ ਕਰਨ ਲਈ ਕੋਈ ਚੈਨਲ ਸਬਸਕ੍ਰਾਇਬ ਕਰੋ
- ਕਤਾਰਬੱਧ ਕਰਨ ਲਈ ਖਿੱਚੋ
+ ਇਥੇ ਦਾ ਖਾਲੀਪਣ ਦੂਰ ਕਰਨ ਲਈ ਕੁਝ ਸਰਚ ਕਰੋ ਜਾਂ ਕੋਈ ਚੈਨਲ ਸਬਸਕ੍ਰਾਇਬ ਕਰੋ
+ ਮੁੜ-ਕ੍ਰਮਬੱਧ ਕਰਨ ਲਈ ਡਰੈਗ ਕਰੋ
ਡਾਊਨਲੋਡ ਡਾਇਰੈਕਟਰੀ ਨਹੀਂ ਬਣਾਈ ਜਾ ਸਕਦੀ \'%1$s\'
ਡਾਊਨਲੋਡ ਡਾਇਰੈਕਟਰੀ ਬਣਾਈ ਗਈ \'%1$s\'
ਵੀਡੀਓ
ਆਡੀਓ
ਦੋਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ
- ਸਟੋਰੇਜ਼ Access ਨੂੰ ਅਸਵੀਕਾਰ ਕੀਤਾ ਗਿਆ
+ ਪਹਿਲਾਂ ਸਟੋਰੇਜ ਨੂੰ ਪੜ੍ਹਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ
ਹਜ਼ਾਰ
ਮਿਲੀਅਨ
ਬਿਲੀਅਨ
@@ -202,10 +209,10 @@
- %s ਸਬਸਕ੍ਰਾਇਬਰ
- %s ਸਬਸਕ੍ਰਾਇਬਰਸ
- ਕੋਈ views ਨਹੀਂ
+ ਕੋਈ ਵਿਊਜ਼ ਨਹੀਂ
- - %s view
- - %s views
+ - %s ਵਿਊ
+ - %s ਵਿਊਜ਼
ਕੋਈ ਵੀਡੀਓ ਨਹੀਂ
@@ -214,7 +221,7 @@
ਸ਼ੁਰੂ ਕਰੋ
ਰੋਕੋ
- ਪਲੇ
+ ਪਲੇਅ
ਬਣਾਓ
ਮਿਟਾਓ
ਇੱਕ ਮਿਟਾਓ
@@ -225,49 +232,49 @@
ਨਵਾਂ ਮਿਸ਼ਨ
ਠੀਕ ਹੈ
ਫਾਈਲ ਦਾ ਨਾਮ
- threads
- ERROR
- Unsupported ਸਰਵਰ
+ ਥਰੈੱਡ
+ ਤਰੁੱਟੀ
+ ਨਾ- ਸਹਾਇਕ ਸਰਵਰ
ਫਾਈਲ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ
ਖਰਾਬ URL ਜਾਂ ਇੰਟਰਨੈਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ
- NewPipe ਡਾਊਨਲੋਡ ਹੋ ਰਿਹਾ ਹੈ
+ ਨਿਊਪਾਈਪ ਡਾਊਨਲੋਡ ਹੋ ਰਹੀ ਹੈ
ਵੇਰਵਿਆਂ ਲਈ ਖੋਲੋ
ਕ੍ਰਿਪਾ ਕਰਕੇ ਉਡੀਕ ਕਰੋ…
ਕਲਿਪ-ਬੋਰਡ ਵਿੱਚ ਕਾਪੀ ਹੋ ਗਿਆ ਹੈ
- ਬਾਅਦ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਵਿਚੋਂ ਇੱਕ ਡਾਉਨਲੋਡ ਫੋਲਡਰ ਨੂੰ ਚੁਣੋ
+ ਬਾਅਦ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਵਿਚੋਂ ਇੱਕ ਡਾਉਨਲੋਡ ਫ਼ੋਲਡਰ ਨੂੰ ਚੁਣੋ
ਪੌਪ-ਅਪ ਮੋਡ ਵਿੱਚ ਖੋਲ੍ਹਣ ਵਾਸਤੇ ਇਸ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ
- 1 ਆਈਟਮ ਮਿਟਾਈ ਗਈ.
+ 1 ਆਈਟਮ ਮਿਟਾਈ ਗਈ।
ReCaptcha ਚੁਣੌਤੀ
ReCaptcha ਚੁਣੌਤੀ ਲਈ ਬੇਨਤੀ
ਡਾਊਨਲੋਡ
ਫਾਈਲ ਨਾਮ ਵਿੱਚ ਪ੍ਰਵਾਨਿਤ ਅੱਖਰ
- ਗਲਤ ਅੱਖਰ ਇਸ Value ਨਾਲ ਤਬਦੀਲ ਕੀਤੇ ਜਾਣਗੇ
- Replacement ਅੱਖਰ
+ ਗਲਤ ਅੱਖਰ ਇਸ ਚਿੰਨ-ਅੱਖਰ ਨਾਲ ਤਬਦੀਲ ਕੀਤੇ ਜਾਣਗੇ
+ ਵਟਾਂਦਰਾ ਚਿੰਨ
ਅੱਖਰ ਅਤੇ ਅੰਕ
ਬਹੁਤੇ ਖ਼ਾਸ ਅੱਖਰ
ਇਸ ਫਾਈਲ ਨੂੰ ਚਲਾਉਣ ਲਈ ਕੋਈ ਐਪ ਇੰਸਟਾਲ ਨਹੀਂ ਹੈ
- NewPipe ਬਾਰੇ
+ ਨਿਊਪਾਈਪ ਬਾਰੇ
ਸੈਟਿੰਗਾਂ
ਐਪ ਬਾਰੇ
- ਥਰਡ-ਪਾਰਟੀ ਲਾਇਸੈਂਸ
+ ਥਰਡ-ਪਾਰਟੀ ਲਾਈਸੈਂਸ
© %1$s ਵਲੋਂ %2$s, %3$s ਅਧੀਨ
- ਲਾਇਸੈਂਸ ਲੋਡ ਨਹੀਂ ਹੋ ਸਕਿਆ
+ ਲਾਈਸੈਂਸ ਲੋਡ ਨਹੀਂ ਹੋ ਸਕਿਆ
ਵੈਬਸਾਈਟ ਖੋਲ੍ਹੋ
ਐਪ ਬਾਰੇ
ਯੋਗਦਾਨ ਪਾਉਣ ਵਾਲੇ
- ਲਾਇਸੈਂਸ
- ਐਂਡਰਾਇਡ ਤੇ ਮੁਫਤ ਲਾਈਟਵੇਟ ਸਟ੍ਰੀਮਿੰਗ.
+ ਲਾਈਸੈਂਸ
+ ਐਂਡਰਾਇਡ ਤੇ ਮੁਫ਼ਤ ਹਲਕੀ-ਫੁਲਕੀ ਸਟ੍ਰੀਮਿੰਗ।
ਯੋਗਦਾਨ ਪਾਓ
ਭਾਵੇਂ ਤੁਹਾਡੇ ਕੋਲ ਵਿਚਾਰ ਹਨ; ਅਨੁਵਾਦ, ਡਿਜ਼ਾਈਨ ਬਦਲਾਵ, ਕੋਡ ਦੀ ਸਫਾਈ, ਜਾਂ ਅਸਲ ਭਾਰੀ ਕੋਡ ਬਦਲਾਵ — ਹਰ ਮਦਦ ਦਾ ਸਦਾ ਸਵਾਗਤ ਹੈ. ਜਿੰਨਾ ਇਸ ਨੂੰ ਜ਼ਿਆਦਾ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਉੱਨਾ ਹੀ ਇਹ ਬਿਹਤਰ ਹੁੰਦਾ ਹੈ!
GitHub ਤੇ ਵੇਖੋ
ਦਾਨ ਕਰੋ
- NewPipe ਵਲੰਟੀਅਰਾਂ ਦੁਆਰਾ ਵਿਕਸਤ ਕੀਤੀ ਗਈ ਹੈ ਜੋ ਤੁਹਾਡੇ ਲਈ ਬਿਹਤਰ ਅਨੁਭਵ ਲਿਆਉਣ ਲਈ ਸਮਾਂ ਬਿਤਾਉਂਦੇ ਹਨ. ਇੱਕ ਕੱਪ ਕਾਫੀ ਦਾ ਆਨੰਦ ਲੈਂਦੇ ਹੋਏ ਡਿਵੈਲਪਰਾਂ ਨੂੰ NewPipe ਨੂੰ ਹੋਰ ਵਧੀਆ ਬਣਾਉਣ ਵਿੱਚ ਸਹਾਇਤਾ ਲਈ ਵਾਪਸ ਦਿਓ.
+ ਨਿਊਪਾਈਪ ਵਲੰਟੀਅਰਾਂ ਦੁਆਰਾ ਵਿਕਸਤ ਕੀਤੀ ਗਈ ਹੈ ਜੋ ਤੁਹਾਡੇ ਲਈ ਬਿਹਤਰ ਵਰਤੋਂਕਾਰ ਤਜਰਬਾ ਲਿਆਉਣ ਲਈ ਸਮਾਂ ਬਿਤਾਉਂਦੇ ਹਨ। ਡਿਵੈਲਪਰਾਂ ਵਾਸਤੇ ਇੱਕ ਕੱਪ ਕਾਫ਼ੀ ਖ਼ਰੀਦ ਦਿਓ ਤਾਂ ਕਿ ਇਸਦਾ ਆਨੰਦ ਲੈਂਦੇ ਹੋਏ ਉਹ ਨਿਊ-ਪਾਈਪ ਨੂੰ ਹੋਰ ਵਧੀਆ ਬਣਾ ਸਕਣ।
ਵਾਪਸ ਦਿਓ
ਵੈਬਸਾਈਟ
- ਵਧੇਰੇ ਜਾਣਕਾਰੀ ਅਤੇ ਖ਼ਬਰਾਂ ਲਈ NewPipe ਵੈਬਸਾਈਟ ਵੇਖੋ.
- NewPipe\'s ਲਾਇਸੈਂਸ
- NewPipe ਇੱਕ ਕਾੱਪੀਲਿਫਟ ਮੁਫ਼ਤ ਸਾੱਫਟਵੇਅਰ ਹੈ: ਤੁਸੀਂ ਇਸ ਦੀ ਵਰਤੋਂ, ਅਧਿਐਨ ਅਤੇ ਇਸ ਨੂੰ ਸਾਂਝਾ ਕਰ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸ ਵਿੱਚ ਆਪਣੀ ਮਰਜ਼ੀ ਅਨੁਸਾਰ ਸੁਧਾਰ ਸਕਦੇ ਹੋ. ਖਾਸ ਤੌਰ \'ਤੇ ਤੁਸੀਂ ਇਸ ਨੂੰ GNU ਜਨਰਲ ਪਬਲਿਕ ਲਾਇਸੈਂਸ ਦੀਆਂ ਸ਼ਰਤਾਂ ਦੇ ਅਧੀਨ ਵੰਡ ਸਕਦੇ ਹੋ / ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰ ਸਕਦੇ ਹੋ ਜਿਵੇਂ ਕਿ ਮੁਫਤ ਸਾੱਫਟਵੇਅਰ ਫਾਊਂਡੇਸ਼ਨ ਦੁਆਰਾ ਪ੍ਰਕਾਸ਼ਤ ਕੀਤਾ ਗਿਆ ਹੈ, ਜਾਂ ਤਾਂ ਲਾਇਸੈਂਸ ਦਾ ਵਰਜ਼ਨ 3, ਜਾਂ (ਤੁਹਾਡੇ ਵਿਕਲਪ\' ਤੇ) ਬਾਅਦ ਦਾ ਕੋਈ ਸੰਸਕਰਣ.
- ਲਾਇਸੈਂਸ ਪੜ੍ਹੋ
+ ਵਧੇਰੇ ਜਾਣਕਾਰੀ ਅਤੇ ਖ਼ਬਰਾਂ ਲਈ ਨਿਊਪਾਈਪ ਵੈਬਸਾਈਟ ਵੇਖੋ।
+ ਨਿਊਪਾਈਪ ਦਾ ਲਾਈਸੈਂਸ
+ ਨਿਊਪਾਈਪ ਇੱਕ ਕਾੱਪੀਲਿਫਟ ਮੁਫ਼ਤ ਸਾੱਫਟਵੇਅਰ ਹੈ: ਤੁਸੀਂ ਇਸ ਦੀ ਵਰਤੋਂ, ਅਧਿਐਨ ਅਤੇ ਇਸ ਨੂੰ ਸਾਂਝਾ ਕਰ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸ ਵਿੱਚ ਆਪਣੀ ਮਰਜ਼ੀ ਅਨੁਸਾਰ ਸੁਧਾਰ ਸਕਦੇ ਹੋ. ਖਾਸ ਤੌਰ \'ਤੇ ਤੁਸੀਂ ਇਸ ਨੂੰ GNU ਜਨਰਲ ਪਬਲਿਕ ਲਾਈਸੈਂਸ ਦੀਆਂ ਸ਼ਰਤਾਂ ਦੇ ਅਧੀਨ ਵੰਡ ਸਕਦੇ ਹੋ / ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰ ਸਕਦੇ ਹੋ ਜਿਵੇਂ ਕਿ ਮੁਫਤ ਸਾੱਫਟਵੇਅਰ ਫਾਊਂਡੇਸ਼ਨ ਦੁਆਰਾ ਪ੍ਰਕਾਸ਼ਤ ਕੀਤਾ ਗਿਆ ਹੈ, ਜਾਂ ਤਾਂ ਲਾਈਸੈਂਸ ਦਾ ਵਰਜ਼ਨ 3, ਜਾਂ (ਤੁਹਾਡੇ ਵਿਕਲਪ\' ਤੇ) ਬਾਅਦ ਦਾ ਕੋਈ ਸੰਸਕਰਣ।
+ ਲਾਈਸੈਂਸ ਪੜ੍ਹੋ
ਹਿਸਟਰੀ
ਖੋਜਿਆ ਗਿਆ
ਵੇਖਿਆ ਗਿਆ
@@ -282,65 +289,75 @@
ਆਖਰੀ ਚਲਾਈ ਗਈ
ਸਭ ਤੋਂ ਜਿਆਦਾ ਚਲਾਈ ਗਈ
ਮੁੱਖ ਪੰਨੇ ਦੀ ਸਮੱਗਰੀ
- ਖਾਲੀ ਪੇਜ
- Kiosk ਪੇਜ
- ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਪੇਜ
- ਫੀਡ ਪੇਜ
- ਚੈਨਲ ਪੇਜ
+ ਖਾਲੀ ਪੰਨਾ
+ ਕਿਓਸਕ ਪੰਨਾ
+ ਡਿਫ਼ਾਲਟ ਕਿਓਸਕ
+ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਪੰਨਾ
+ ਫੀਡ ਪੰਨਾ
+ ਚੈਨਲ ਪੰਨਾ
ਚੈਨਲ ਚੁਣੋ
ਅਜੇ ਤੱਕ ਕੋਈ ਚੈਨਲ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨਹੀਂ
- ਇੱਕ Kiosk ਚੁਣੋ
+ ਪਲੇ-ਸੂਚੀ ਚੁਣੋ
+ ਹਾਲੇ ਕੋਈ ਵੀ ਪਲੇ-ਸੂਚੀ ਬੁੱਕਮਾਰਕ ਨਹੀਂ ਕੀਤੀ ਹੋਈ
+ ਇੱਕ ਕਿਓਸਕ ਚੁਣੋ
ਐਕਸਪੋਰਟ ਕੀਤਾ ਗਿਆ
ਇੰਪੋਰਟ ਕੀਤਾ ਗਿਆ
ਕੋਈ ਵੈਧ ZIP ਫਾਈਲ ਨਹੀਂ ਹੈ
- ਚੇਤਾਵਨੀ: ਸਾਰੀਆਂ ਫਾਈਲਾਂ ਇੰਪੋਰਟ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾ ਸਕੀਆਂ.
- ਇਹ ਤੁਹਾਡੇ ਮੌਜੂਦਾ ਸੈਟ-ਅਪ ਨੂੰ Override ਕਰ ਦੇਵੇਗਾ.
- kiosk
+ ਚੇਤਾਵਨੀ: ਸਾਰੀਆਂ ਫਾਈਲਾਂ ਇੰਪੋਰਟ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾ ਸਕੀਆਂ।
+ ਇਹ ਤੁਹਾਡੇ ਮੌਜੂਦਾ ਸੈਟ-ਅਪ ਨੂੰ ਓਵਰ-ਰਾਈਡ ਕਰ ਦੇਵੇਗਾ।
+ ਭਾਸ਼ਾ, ਐਪ ਨੂੰ ਦੋਬਾਰਾ ਚਲਾਉਣ \'ਤੇ ਬਦਲੇਗੀ।
+ ਕਿਓਸਕ
ਰੁਝਾਨ ਵਿੱਚ
- ਟੌਪ 50
- ਨਵਾਂ ਅਤੇ ਗਰਮਾ-ਗਰਮ
- ਬੈਕਗ੍ਰਾਉਂਡ ਪਲੇਅਰ
+ ਸਿਖਰਲੇ 50
+ ਨਵਾਂ ਅਤੇ ਤਾਜ਼ਾ-ਤਰੀਨ
+ ਲੋਕਲ
+ ਹਾਲ ਹੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਹੋਇਆ
+ ਸਭ ਤੋਂ ਵੱਧ ਪਸੰਦ ਕੀਤਾ ਹੋਇਆ
+ ਬੈਕਗ੍ਰਾਊਂਡ ਪਲੇਅਰ
ਪੌਪ-ਅਪ ਪਲੇਅਰ
ਹਟਾਓ
ਵੇਰਵੇ
ਆਡੀਓ ਸੈਟਿੰਗਾਂ
ਕਤਾਰਬੱਧ ਕਰਨ ਵਾਸਤੇ ਦਬਾ ਕੇ ਰੱਖੋ
- ਬੈਕਗ੍ਰਆਊਂਡ ਵਿੱਚ ਕਤਾਰਬੱਧ ਕਰੋ
+ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਕਤਾਰਬੱਧ ਕਰੋ
ਨਵੇਂ ਪੌਪ-ਅਪ ਵਿੱਚ ਕਤਾਰਬੱਧ ਕਰੋ
- ਇਥੇ ਚਲਾਉ
- "ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚਲਾਉ"
+ ਇੱਥੇ ਚਲਾਓ
+ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚਲਾਓ
ਨਵੇਂ ਪੌਪ-ਅਪ ਵਿੱਚ ਚਲਾਓ
- Drawer ਖੋਲੋ
- Drawer ਬੰਦ ਕਰੋ
+ ਡਰਾਅਰ ਖੋਲੋ
+ ਡਰਾਅਰ ਬੰਦ ਕਰੋ
ਜਲਦੀ ਹੀ ਇੱਥੇ ਕੁੱਝ ਦਿਖਾਈ ਦੇਵੇਗਾ ;D
- ਤਰਜੀਹੀ \'OPEN\' ਐਕਸ਼ਨ
- Content ਖੋਲ੍ਹਣ ਵੇਲੇ Default ਕਾਰਵਾਈ — %s
+ ਤਰਜੀਹੀ \'ਖੋਲ੍ਹੋ\' ਐਕਸ਼ਨ
+ ਸਮੱਗਰੀ ਖੋਲ੍ਹਣ ਵੇਲੇ ਡਿਫ਼ਾਲਟ ਕਾਰਵਾਈ — %s
ਵੀਡੀਓ ਪਲੇਅਰ
ਬੈਕਗ੍ਰਾਊਂਡ ਪਲੇਅਰ
ਪੌਪ-ਅਪ ਪਲੇਅਰ
- ਹਮੇਸ਼ਾ ਪੁੱਛੋ
+ ਹਮੇਸ਼ਾਂ ਪੁੱਛੋ
ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰ ਰਹੇ ਹਾਂ…
- ਬੇਨਤੀ ਕੀਤਾ Content ਲੋਡ ਕੀਤੀ ਜਾ ਰਿਹਾ ਹੈ
+ ਬੇਨਤੀ ਕੀਤੀ ਸਮੱਗਰੀ ਲੋਡ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ
ਨਵੀਂ ਪਲੇ-ਲਿਸਟ
ਮਿਟਾਓ
ਨਾਮ ਬਦਲੋ
ਨਾਮ
ਪਲੇ-ਲਿਸਟ ਵਿੱਚ ਸ਼ਾਮਿਲ ਕਰੋ
- ਪਲੇ-ਲਿਸਟ thumbnail ਦੇ ਤੌਰ ਤੇ ਸੈੱਟ ਕਰੋ
+ ਅਵਾਜ਼ ਬੰਦ ਕਰੋ
+ ਅਵਾਜ਼ ਚਾਲੂ ਕਰੋ
+ ਬਤੌਰ ਪਲੇ-ਸੂਚੀ ਥੰਮਨੇਲ ਸੈੱਟ ਕਰੋ
ਬੁੱਕਮਾਰਕ ਪਲੇ-ਲਿਸਟ
ਬੁੱਕਮਾਰਕ ਹਟਾਓ
ਇਸ ਪਲੇ-ਲਿਸਟ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ \?
ਪਲੇ-ਲਿਸਟ ਬਣਾਈ ਗਈ
ਪਲੇ-ਲਿਸਟ ਕੀਤਾ ਗਿਆ
- ਪਲੇ-ਲਿਸਟ thumbnail ਬਦਲਿਆ ਗਿਆ.
- ਪਲੇ-ਲਿਸਟ ਨੂੰ ਮਿਟਾ ਨਹੀਂ ਸਕੇ.
- No captions
+ ਪਲੇ-ਲਿਸਟ ਥੰਮਨੇਲ ਬਦਲਿਆ ਗਿਆ।
+ ਪਲੇ-ਲਿਸਟ ਨੂੰ ਮਿਟਾ ਨਹੀਂ ਸਕੇ।
+ ਆਪਣੇ-ਆਪ ਬਣੀ (ਕੋਈ ਅਪਲੋਡਰ ਨਹੀਂ ਲੱਭਿਆ)
+ ਕੋਈ ਕੈਪਸ਼ਨਾਂ ਨਹੀਂ
ਫਿੱਟ
ਭਰੋ
ਜ਼ੂਮ
- Auto-Generated
- captions
- ਪਲੇਅਰ caption, text ਸਕੇਲ ਅਤੇ ਬੈਕਗ੍ਰਾਉਂਡ ਸਟਾਈਲ ਨੂੰ ਸੋਧੋ. ਪ੍ਰਭਾਵ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਐਪ ਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਜ਼ਰੂਰਤ ਹੈ.
+ ਸਵੈ-ਉਤਪੰਨ
+ ਕੈਪਸ਼ਨਾਂ
+ ਪਲੇਅਰ ਕੈਪਸ਼ਨ , ਟੈਕਸਟ ਸਕੇਲ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਟਾਈਲ ਨੂੰ ਸੋਧੋ. ਪ੍ਰਭਾਵ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਐਪ ਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਜ਼ਰੂਰਤ ਹੈ।
LeakCanary ਚਲਾਓ
ਮੈਮੋਰੀ ਲੀਕ monitoring, ਐਪ ਨੂੰ Unresponsive ਬਣਾ ਸਕਦੀ ਹੈ ਜਦੋਂ ਹੀਪ dumping ਹੁੰਦੀ ਹੈ
Out-of-lifecycle ERROR ਰਿਪੋਰਟ ਕਰੋ
@@ -355,141 +372,231 @@
ਪਿੱਛਲਾ ਐਕਸਪੋਰਟ
ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਇੰਪੋਰਟ ਨਹੀਂ ਹੋ ਸਕੀਆਂ
ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਐਕਸਪੋਰਟ ਨਹੀਂ ਹੋ ਸਕੀਆਂ
- ਯੂ ਟਿਊਬ ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਇੰਪੋਰਟ ਕਰਨ ਲਈ ਐਕਸਪੋਰਟ ਫਾਈਲ ਡਾਊਨਲੋਡ ਕਰੋ:
-\n
-\n1. ਇਸ URL ਤੇ ਜਾਓ: %1$s
-\n2. ਆਪਣੇ ਖਾਤੇ ਚ ਲੌਗ-ਇਨ ਕਰੋ
-\n3. ਇੱਕ ਡਾਉਨਲੋਡ ਸ਼ੁਰੂ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ (ਇਹੀ ਐਕਸਪੋਰਟ ਫਾਈਲ ਹੈ)
+ ਗੂਗਲ ਟੇਕਅਊਟ ਤੋਂ ਯੂਟਿਊਬ ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਇੰਪੋਰਟ ਕਰਨ ਲਈ ਐਕਸਪੋਰਟ ਫਾਈਲ ਡਾਊਨਲੋਡ ਕਰੋ:
+\n
+\n1. ਇਸ URL ਤੇ ਜਾਓ: %1$s
+\n2. ਮੰਗਣ ਤੇ ਆਪਣੇ ਖਾਤੇ \'ਚ ਲਾਗ-ਇਨ ਕਰੋ
+\n3. ਕਲਿੱਕ ਕਰੋ \" All data incuded\" ਤੇ, ਫੇਰ \"Deselect all\" ਤੇ ਫੇਰ ਸਿਰਫ \"subscriprion\" ਚੁਣੋ ਅਤੇ \"OK\" ਕਰੋ
+\n4. \"Next step\" ਤੇ ਕਲਿੱਕ ਕਰੋ ਤੇ ਫੇਰ \"create export\" ਤੇ
+\n5. ਡਾਊਨਲੋਡ ਬਟਨ ਦਿਖਾਈ ਦੇਣ ਤੇ ਇਸ ਤੇ ਕਲਿੱਕ ਕਰੋ।ਇੱਕ ਡਾਉਨਲੋਡ ਸ਼ੁਰੂ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ (ਇਹੀ ਐਕਸਪੋਰਟ ਫਾਈਲ ਹੈ)
+\n6. ਥੱਲੇ ਇੰਪੋਰਟ ਫਾਈਲ ਤੇ ਕਲਿੱਕ ਕਰੋ ਤੇ ਡਾਊਨਲੋਡ ਕੀਤੀ .zip ਫਾਈਲ ਚੁਣੋ
+\n7. [ਜੇ .zip ਤੋਂ ਐਕਸਪੋਰਟ ਫੇਲ ਹੋ ਜਾਂਦੀ ਹੈ] ਤਾਂ .csv ਫਾਈਲ ਐਕਸਟਰੈਕਟ ਕਰੋ (ਆਮ ਤੌਰ ਤੇ \"YouTube and YouTube Music/subscriptions/subscriptions.csv\"), ਥੱਲੇ ਦਿੱਤੇ ਇੰਪੋਰਟ ਫਾਈਲ ਤੇ ਕਲਿੱਕ ਕਰਕੇ ਐਕਸਟਰੈਕਟ ਕੀਤੀ csv ਫਾਈਲ ਚੁਣੋ
URL ਜਾਂ ਆਪਣੀ ID ਟਾਈਪ ਕਰਕੇ ਸਾਉੰਡ ਕਲਾਉਡ ਪ੍ਰੋਫਾਈਲ ਇੰਪੋਰਟ ਕਰੋ:
\n
\n1. ਇੱਕ ਵੈਬ-ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ \"ਡੈਸਕਟਾਪ ਮੋਡ\" ਨੂੰ ਚਾਲੂ ਕਰੋ (ਸਾਈਟ ਮੋਬਾਈਲ ਉਪਕਰਣਾਂ ਲਈ ਉਪਲਬਧ ਨਹੀਂ ਹੈ)
\n2. ਇਸ URL ਤੇ ਜਾਓ: %1$s
\n3. ਆਪਣੇ ਖਾਤੇ ਚ ਲੌਗ-ਇਨ ਕਰੋ
\n4. ਨਿਰਦੇਸ਼ਤ ਕੀਤੇ ਗਏ ਪ੍ਰੋਫਾਈਲ URL ਨੂੰ ਕਾਪੀ ਕਰੋ.
- yourID, Soundcloud.com/yourid
- ਯਾਦ ਰੱਖੋ ਕਿ ਇਹ ਕਾਰਜ ਡਾਟਾ consuming ਹੋ ਸਕਦਾ ਹੈ.
+ ਤੁਹਾਡੀ ਆਈਡੀ, soundcloud.com/ਤੁਹਾਡੀ ਆਈਡੀ
+ ਯਾਦ ਰੱਖੋ ਕਿ ਇਸ ਕਾਰਜ ਨਾਲ ਡਾਟਾ ਖਪਤ ਹੋ ਸਕਦਾ ਹੈ।
\n
\nਕੀ ਤੁਸੀਂ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ\?
ਪਲੇਅਬੈਕ ਸਪੀਡ ਕੰਟਰੋਲ
- tempo
- pitch
- ਅਨਲਿੰਕ (distortion ਪੈਦਾ ਹੋ ਸਕਦੀ ਹੈ)
+ ਤਾਲ
+ ਪਿੱਚ
+ ਅਲਹਿਦਾ ਕਰੋ (ਵਿਗਾੜ ਪੈ ਸਕਦਾ ਹੈ)
ਕੀ ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ ਨੂੰ ਵੀ ਇੰਪੋਰਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ\?
- NewPipe\'s ਗੋਪਨੀਯਤਾ ਨੀਤੀ
- NewPipe ਪ੍ਰੋਜੈਕਟ ਤੁਹਾਡੀ ਗੋਪਨੀਯਤਾ ਨੂੰ ਬਹੁਤ ਗੰਭੀਰਤਾ ਨਾਲ ਲੈਂਦਾ ਹੈ. ਇਸ ਲਈ ਐਪ ਤੁਹਾਡੀ ਸਹਿਮਤੀ ਤੋਂ ਬਿਨਾਂ ਕੋਈ ਵੀ ਡਾਟਾ ਇੱਕਠਾ ਨਹੀਂ ਕਰਦਾ.
-\nNewPipe ਦੀ ਗੋਪਨੀਯਤਾ ਨੀਤੀ ਵਿਸਥਾਰ ਵਿੱਚ ਦੱਸਦੀ ਹੈ ਕਿ ਜਦੋਂ ਤੁਸੀਂ ਕਰੈਸ਼ ਰਿਪੋਰਟ ਭੇਜਦੇ ਹੋ ਤਾਂ ਕਿਹੜਾ ਡੇਟਾ ਭੇਜਿਆ ਜਾਂ ਸਟੋਰ ਕੀਤਾ ਜਾਂਦਾ ਹੈ.
+ ਨਿਊਪਾਈਪ ਦੀ ਗੋਪਨੀਯਤਾ ਨੀਤੀ
+ ਨਿਊਪਾਈਪ ਪ੍ਰੋਜੈਕਟ ਤੁਹਾਡੀ ਗੋਪਨੀਯਤਾ ਨੂੰ ਬਹੁਤ ਗੰਭੀਰਤਾ ਨਾਲ ਲੈਂਦਾ ਹੈ। ਇਸ ਲਈ ਐਪ ਤੁਹਾਡੀ ਸਹਿਮਤੀ ਤੋਂ ਬਿਨਾਂ ਕੋਈ ਵੀ ਡਾਟਾ ਇੱਕਠਾ ਨਹੀਂ ਕਰਦਾ।
+\nਨਿਊਪਾਈਪ ਦੀ ਗੋਪਨੀਯਤਾ ਨੀਤੀ ਵਿਸਥਾਰ ਵਿੱਚ ਦੱਸਦੀ ਹੈ ਕਿ ਜਦੋਂ ਤੁਸੀਂ ਕਰੈਸ਼ ਰਿਪੋਰਟ ਭੇਜਦੇ ਹੋ ਤਾਂ ਕਿਹੜਾ ਡਾਟਾ ਭੇਜਿਆ ਜਾਂ ਸਟੋਰ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।
ਗੋਪਨੀਯਤਾ ਨੀਤੀ ਪੜ੍ਹੋ
- ਯੂਰਪੀਅਨ ਜਨਰਲ ਡੇਟਾ ਪ੍ਰੋਟੈਕਸ਼ਨ ਰੈਗੂਲੇਸ਼ਨ (ਜੀਡੀਪੀਆਰ) ਦੀ ਪਾਲਣਾ ਕਰਨ ਲਈ, ਅਸੀਂ ਤੁਹਾਡਾ ਧਿਆਨ NewPipe ਦੀ ਗੋਪਨੀਯਤਾ ਨੀਤੀ ਵੱਲ ਖਿੱਚਦੇ ਹਾਂ. ਕਿਰਪਾ ਕਰਕੇ ਇਸਨੂੰ ਧਿਆਨ ਨਾਲ ਪੜ੍ਹੋ.
-\nਸਾਨੂੰ BUG ਰਿਪੋਰਟ ਭੇਜਣ ਲਈ ਤੁਹਾਨੂੰ ਇਸ ਨੂੰ ਸਵੀਕਾਰ ਕਰਨਾ ਪਵੇਗਾ.
+ ਯੂਰਪੀਅਨ ਜਨਰਲ ਡਾਟਾ ਪ੍ਰੋਟੈਕਸ਼ਨ ਰੈਗੂਲੇਸ਼ਨ (ਜੀਡੀਪੀਆਰ) ਦੀ ਪਾਲਣਾ ਕਰਨ ਲਈ, ਅਸੀਂ ਤੁਹਾਡਾ ਧਿਆਨ ਨਿਊਪਾਈਪ ਦੀ ਗੋਪਨੀਯਤਾ ਨੀਤੀ ਵੱਲ ਖਿੱਚਦੇ ਹਾਂ. ਕਿਰਪਾ ਕਰਕੇ ਇਸਨੂੰ ਧਿਆਨ ਨਾਲ ਪੜ੍ਹੋ।
+\nਸਾਨੂੰ BUG ਰਿਪੋਰਟ ਭੇਜਣ ਲਈ ਤੁਹਾਨੂੰ ਇਸ ਨੂੰ ਸਵੀਕਾਰ ਕਰਨਾ ਪਵੇਗਾ।
ਸਵੀਕਾਰ ਕਰੋ
ਅਸਵੀਕਾਰ
ਕੋਈ ਸੀਮਾ ਨਹੀਂ
- ਮੋਬਾਈਲ ਡਾਟਾ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਸਮੇਂ Resolution ਨੂੰ ਸੀਮਿਤ ਕਰੋ
- ਐਪ switch ਕਰਨ ਤੇ minimize ਕਰੋ
+ ਮੋਬਾਈਲ ਡਾਟਾ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਸਮੇਂ ਰੈਜ਼ੋਲਿਊਸ਼ਨ ਨੂੰ ਸੀਮਿਤ ਕਰੋ
+ ਐਪ ਸਵਿੱਚ ਕਰਨ ਤੇ ਮਿਨੀਮਾਈਜ਼ ਕਰੋ
ਮੁੱਖ ਵੀਡੀਓ ਪਲੇਅਰ ਤੋਂ ਦੂਜੇ ਐਪ \'ਤੇ ਜਾਣ ਵੇਲੇ ਐਕਸ਼ਨ
ਕੋਈ ਨਹੀਂ
- ਬੈਕਗ੍ਰਾਉਂਡ ਪਲੇਅਰ ਵਿੱਚ Minimize ਕਰੋ
- ਪੌਪ-ਅਪ ਪਲੇਅਰ ਵਿੱਚ minimize ਕਰੋ
+ ਬੈਕਗ੍ਰਾਊਂਡ ਪਲੇਅਰ ਵਿੱਚ ਮਿਨੀਮਾਈਜ਼ ਕਰੋ
+ ਪੌਪ-ਅਪ ਪਲੇਅਰ ਵਿੱਚ ਮਿਨੀਮਾਈਜ਼ ਕਰੋ
ਚੁੱਪ ਦੌਰਾਨ ਤੇਜ਼ੀ ਨਾਲ ਅੱਗੇ ਕਰੋ
- ਸਟੇਪ
+ ਕਦਮ
ਰੀਸੈੱਟ
- ਚੈਨਲਾਂ
+ ਚੈਨਲ
ਪਲੇ ਸੂਚੀਆਂ
ਟਰੈਕਸ
ਯੂਜ਼ਰਸ
- ਅਨ-ਸਬਸਕ੍ਰਾਈਬ
- ਨਵਾਂ ਟੈਬ
- ਟੈਬ ਚੁਣੋ
- ਆਵਾਜ਼ gesture ਕੰਟਰੋਲ
- ਆਵਾਜ਼ ਕੰਟਰੋਲ ਕਰਨ ਲਈ gestures ਦੀ ਵਰਤੋਂ ਕਰੋ
- ਸਕ੍ਰੀਨ ਲਾਈਟ gesture ਕੰਟਰੋਲ
- ਵੀਡੀਓ ਸਕ੍ਰੀਨ ਲਾਈਟ ਕੰਟਰੋਲ ਕਰਨ ਲਈ gestures ਦੀ ਵਰਤੋਂ ਕਰੋ
- ਮੂਲ ਭਾਸ਼ਾ Content
+ ਆਵਾਜ਼ ਸੰਕੇਤ ਕੰਟਰੋਲ
+ ਆਵਾਜ਼ ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਸੰਕੇਤ ਦੀ ਵਰਤੋਂ ਕਰੋ
+ ਸਕ੍ਰੀਨ ਲਾਈਟ ਸੰਕੇਤ ਕੰਟਰੋਲ
+ ਵੀਡੀਓ ਸਕ੍ਰੀਨ ਦੀ ਚਮਕ ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਸੰਕੇਤ ਦੀ ਵਰਤੋਂ ਕਰੋ
+ ਮੂਲ ਭਾਸ਼ਾ ਸਮੱਗਰੀ
ਅਪਡੇਟਾਂ
ਫਾਈਲ ਮਿਟਾ ਦਿੱਤੀ ਗਈ ਹੈ
ਐਪ ਅੱਪਡੇਟ ਨੋਟੀਫਿਕੇਸ਼ਨ
- ਨਵੇਂ NewPipe ਸੰਸਕਰਣ ਲਈ ਸੂਚਨਾਵਾਂ
+ ਨਿਊ-ਪਾਈਪ ਦੀ ਅਪਡੇਟ ਉਪਲਬੱਧ ਹੈ!
+ ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ ਦਬਾਓ
ਬਾਹਰੀ ਸਟੋਰੇਜ ਉਪਲਬਧ ਨਹੀਂ ਹੈ
- ਬਾਹਰੀ SD ਕਾਰਡ ਤੇ ਡਾਊਨਲੋਡ ਕਰਨਾ ਸੰਭਵ ਨਹੀਂ ਹੈ. ਕੀ ਡਾਊਨਲੋਡ ਫੋਲਡਰ ਦੀ ਸਥਿਤੀ ਨੂੰ ਰੀਸੈਟ ਕੀਤਾ ਜਾਵੇ \?
- Default ਟੈਬ ਦੀ ਵਰਤੋਂ ਕਰਦਿਆਂ, save ਕੀਤੇ ਟੈਬਾਂ ਨੂੰ ਪੜ੍ਹਨ ਵੇਲੇ ERROR
- Default ਮੁੜ-ਪ੍ਰਾਪਤ ਕਰੋ
- ਕੀ ਤੁਸੀਂ Default ਲਾਗੂ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ \?
+ ਬਾਹਰੀ SD ਕਾਰਡ ਤੇ ਡਾਊਨਲੋਡ ਕਰਨਾ ਸੰਭਵ ਨਹੀਂ ਹੈ. ਕੀ ਡਾਊਨਲੋਡ ਫ਼ੋਲਡਰ ਦੀ ਸਥਿਤੀ ਨੂੰ ਰੀਸੈਟ ਕੀਤਾ ਜਾਵੇ \?
+ ਸਾਂਭੀਆਂ ਟੈਬਾਂ ਨਹੀਂ ਪੜ੍ਹ ਹੋਈਆਂ, ਇਸ ਲਈ ਡਿਫਾਲਟ ਟੈਬਾਂ ਦੀ ਵਰਤੋਂ ਹੋ ਰਹੀ ਹੈ
+ ਡਿਫਾਲਟ ਮੁੜ-ਪ੍ਰਾਪਤ ਕਰੋ
+ ਕੀ ਤੁਸੀਂ ਡਿਫਾਲਟ ਮੁੜ-ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ\?
+ ਦੂਜੀਆਂ ਐਪਾਂ ਦੇ ਉੱਤੇ ਵਿਖਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ
ਸਬਸਕ੍ਰਾਇਬਰਾਂ ਦੀ ਗਿਣਤੀ ਉਪਲਬਧ ਨਹੀਂ ਹੈ
ਮੁੱਖ ਪੰਨੇ ਤੇ ਕਿਹੜੇ ਟੈਬ ਦਿਖਾਏ ਜਾਣਗੇ
ਚੋਣ
ਅਪਡੇਟਾਂ
- ਜਦੋਂ ਨਵਾਂ ਸੰਸਕਰਣ ਉਪਲਬਧ ਹੁੰਦਾ ਹੈ ਤਾਂ ਐਪ ਅਪਡੇਟ ਨੂੰ ਪੁੱਛਣ ਲਈ ਇੱਕ ਨੋਟੀਫਿਕੇਸ਼ਨ ਦਿਖਾਓ
- ਲਿਸਟ view ਮੋਡ
+ ਜਦੋਂ ਨਵਾਂ ਸੰਸਕਰਣ ਉਪਲਬਧ ਹੁੰਦਾ ਹੈ ਤਾਂ ਐਪ ਅਪਡੇਟ ਨੂੰ ਪੁੱਛਣ ਲਈ ਇੱਕ ਨੋਟੀਫਿਕੇਸ਼ਨ ਵਿਖਾਓ
+ ਲਿਸਟ ਵਿਊ ਮੋਡ
ਲਿਸਟ
ਗਰਿੱਡ
ਆਟੋ
- ਸਵਿੱਚ view
- NewPipe ਅੱਪਡੇਟ ਉਪਲਬੱਧ!
- ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ ਦਬਾਓ
+ ਸਵਿੱਚ ਵਿਊ
ਮੁਕੰਮਲ ਹੋਇਆ
ਬਕਾਇਆ
ਰੁਕਿਆ
ਕਤਾਰਬੱਧ
Post-processing
+ ਮੁੜ-ਪ੍ਰਾਪਤੀ
ਕਤਾਰ
- ਸਿਸਟਮ ਦੁਆਰਾ ਕਾਰਵਾਈ ਤੋਂ ਇਨਕਾਰ ਕੀਤਾ ਗਿਆ
+ ਸਿਸਟਮ ਦੁਆਰਾ permission Deny ਕੀਤੀ ਗਈ
ਡਾਊਨਲੋਡ ਫੇਲ੍ਹ
ਡਾਊਨਲੋਡ ਮੁਕੰਮਲ
%s ਡਾਊਨਲੋਡ ਮੁਕੰਮਲ
- ਵਿਲੱਖਣ ਨਾਮ Generate ਕਰੋ
- overwrite
+ ਵਿਲੱਖਣ ਨਾਮ ਬਣਾਓ
+ ਓਵਰਰਾਈਟ
ਇਸ ਨਾਮ ਦੇ ਨਾਲ ਇੱਕ ਡਾਊਨਲੋਡ ਪਹਿਲਾਂ ਤੋਂ ਜਾਰੀ ਹੈ
- ERROR ਵਿਖਾਓ
+ ਤਰੁੱਟੀ ਵਿਖਾਓ
ਕੋਡ
- Destination ਫੋਲਡਰ ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ
+ ਟਿਕਾਣਾ ਫ਼ੋਲਡਰ ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ
ਫਾਈਲ ਨਹੀਂ ਬਣਾਈ ਜਾ ਸਕਦੀ
ਸਿਸਟਮ ਦੁਆਰਾ permission Deny ਕੀਤੀ ਗਈ
- ਸੁਰੱਖਿਅਤ ਕੁਨੈਕਸ਼ਨ ਫੇਲ੍ਹ
+ ਸੁਰੱਖਿਅਤ ਕੁਨੈਕਸ਼ਨ ਸਥਾਪਤ ਨਹੀਂ ਹੋ ਸਕਿਆ
ਸਰਵਰ ਨਹੀਂ ਲੱਭ ਸਕਿਆ
ਸਰਵਰ ਨਾਲ ਜੁੜ ਨਹੀਂ ਸਕਦਾ
ਸਰਵਰ ਨੇ ਡਾਟਾ ਨਹੀਂ ਭੇਜਿਆ
ਸਰਵਰ ਮਲਟੀ-Threaded ਡਾਊਨਲੋਡਸ ਨੂੰ ਸਵੀਕਾਰ ਨਹੀਂ ਕਰਦਾ, ਇਸ ਨਾਲ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ @string/msg_threads = 1
ਨਹੀਂ ਲਭਿਆ
- Post-processing ਫੇਲ੍ਹ
- ਰੁੱਕੋ
+ Post-processing ਫੇਲ੍ਹ ਹੋਈ
+ ਰੁਕੋ
ਵੱਧ ਤੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ
ਡਾਉਨਲੋਡ ਰੱਦ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਵੱਧ ਤੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ
- Metered ਨੈਟਵਰਕਸ ਤੇ ਰੁਕਾਵਟ
- ਮੋਬਾਈਲ ਡਾਟਾ ਤੇ switch ਕਰਨ ਵੇਲੇ ਲਾਭਦਾਇਕ ਹੈ, ਹਾਲਾਂਕਿ ਕੁਝ ਡਾਉਨਲੋਡਾਂ ਨੂੰ suspend ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ
- ਇਵੇੰਟਸ
+ ਮੀਟਰ ਕੀਤੇ ਨੈਟਵਰਕਸ ਤੇ ਰੁਕਾਵਟ
+ ਮੋਬਾਈਲ ਡਾਟਾ ਤੇ ਸਵਿੱਚ ਕਰਨ ਵੇਲੇ ਲਾਭਦਾਇਕ ਹੈ, ਹਾਲਾਂਕਿ ਕੁਝ ਡਾਉਨਲੋਡਾਂ ਨੂੰ ਮੁਅੱਤਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ
+ ਇਵੇਂਟਸ
ਕਾਨਫਰੰਸਾਂ
- ਟਿੱਪਣੀਆਂ ਦਿਖਾਓ
- ਟਿੱਪਣੀਆਂ ਵਿਖਾਉਣਾ ਬੰਦ ਕਰਨ ਲਈ ਇਸਨੂੰ ਬੰਦ ਕਰੋ
- ਆਟੋ-ਪਲੇ
+ ਟਿੱਪਣੀਆਂ ਵਿਖਾਓ
+ ਟਿੱਪਣੀਆਂ ਲੁਕਾਉਣ ਲਈ ਇਸਨੂੰ ਬੰਦ ਕਰੋ
+ ਆਟੋ-ਪਲੇਅ
ਕੋਈ ਟਿੱਪਣੀ ਨਹੀਂ ਕੀਤੀ ਗਈ
+ ਟਿੱਪਣੀਆਂ ਬੰਦ ਕੀਤੀਆਂ ਹੋਈਆਂ ਹਨ
ਟਿੱਪਣੀਆਂ ਲੋਡ ਨਹੀਂ ਹੋ ਸਕੀਆਂ
ਬੰਦ ਕਰੋ
ਪਲੇਅਬੈਕ ਦੋਬਾਰਾ ਸ਼ੁਰੂ ਕਰੋ
ਪਿਛਲੀ ਪਲੇਅਬੈਕ ਸਥਿਤੀ ਤੋਂ ਮੁੜ ਚਲਾਓ
ਸੂਚੀਆਂ ਦੀ ਸਥਿਤੀ
- ਸੂਚੀਆਂ ਵਿੱਚ ਪਲੇਅਬੈਕ ਸਥਿਤੀ ਸੂਚਕ ਦਿਖਾਓ
+ ਸੂਚੀਆਂ ਵਿੱਚ ਪਲੇਅਬੈਕ ਸਥਿਤੀ ਸੂਚਕ ਵਿਖਾਓ
ਡਾਟਾ ਮਿਟਾਓ
- Watch ਹਿਸਟਰੀ ਮਿਟਾ ਦਿੱਤੀ ਗਈ ਹੈ.
- ਪਲੇਬੈਕ ਸਥਿਤੀ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ.
- ਫਾਈਲ ਮੂਵ ਕੀਤੀ ਜਾਂ ਮਿਟਾਈ ਗਈ ਹੈ
+ ਵੇਖੀਆਂ ਸਟ੍ਰੀਮਾਂ ਦੀ ਇਤਿਹਾਸ ਸੂਚੀ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ
+ ਪਲੇਬੈਕ ਸਥਿਤੀਆਂ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ.
+ ਫਾਈਲ ਤਬਦੀਲ ਕੀਤੀ ਜਾਂ ਮਿਟਾਈ ਗਈ
ਇਸ ਨਾਮ ਵਾਲੀ ਇੱਕ ਫਾਈਲ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ
ਇਸ ਨਾਮ ਨਾਲ ਡਾਉਨਲੋਡ ਕੀਤੀ ਫਾਈਲ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ
ਫਾਈਲ Overwrite ਨਹੀਂ ਹੋ ਸਕਦੀ
ਇਸ ਨਾਮ ਦੇ ਨਾਲ ਇੱਥੇ ਇੱਕ ਬਕਾਇਆ ਡਾਊਨਲੋਡ ਹੈ
- ਫਾਈਲ ਤੇ ਕੰਮ ਕਰਦੇ ਸਮੇਂ NewPipe ਬੰਦ ਕੀਤੀ ਗਈ ਸੀ
+ ਫਾਈਲ ਤੇ ਕੰਮ ਕਰਦੇ ਸਮੇਂ ਨਿਊਪਾਈਪ ਬੰਦ ਕੀਤੀ ਗਈ ਸੀ
ਡਿਵਾਈਸ ਤੇ ਕੋਈ ਜਗ੍ਹਾ ਨਹੀਂ ਬਚੀ ਹੈ
Progress lost, ਕਿਉਂਕਿ ਫਾਈਲ ਮਿਟ ਗਈ ਸੀ
- ਕੁਨੈਕਸ਼ਨ timeout
- are you sure\?
+ ਕੁਨੈਕਸ਼ਨ ਟਾਈਮ-ਆਉਟ
+ ਡਾਊਨਲੋਡ ਇਤਿਹਾਸ ਸਾਫ਼ ਕਰੋ
+ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਫ਼ਾਈਲਾਂ ਮਿਟਾਓ
+ ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਡਾਊਨਲੋਡ ਇਤਿਹਾਸ ਸਾਫ਼ ਕਰਨਾ ਜਾਂ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਸਾਰੀਆਂ ਫ਼ਾਈਲਾਂ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ\?
ਡਾਊਨਲੋਡ ਸੀਮਾ ਕਤਾਰ ਵਿੱਚ
- one download will run at the same time
- ਡਾਊਨਲੋਡ ਸ਼ੁਰੂ ਕਰੋ
+ ਇੱਕ ਸਮੇਂ ਇੱਕੋ ਡਾਊਨਲੋਡ ਚੱਲੇਗਾ, ਬਾਕੀ ਕਤਾਰਬੱਧ ਹੋਣਗੇ
+ ਡਾਊਨਲੋਡਸ ਸ਼ੁਰੂ ਕਰੋ
ਡਾਊਨਲੋਡਸ ਰੋਕੋ
ਪੁੱਛੋ ਕਿੱਥੇ ਡਾਊਨਲੋਡ ਕਰਨਾ ਹੈ
- ਤੁਹਾਨੂੰ ਪੁੱਛਿਆ ਜਾਵੇਗਾ ਕਿ ਹਰ ਡਾਉਨਲੋਡ ਨੂੰ ਕਿੱਥੇ save ਕਰਨਾ ਹੈ
- ਤੁਹਾਨੂੰ ਪੁੱਛਿਆ ਜਾਵੇਗਾ ਕਿ ਹਰ ਡਾਉਨਲੋਡ ਨੂੰ ਕਿੱਥੇ save ਕਰਨਾ ਹੈ.
+ ਤੁਹਾਨੂੰ ਹਰ ਵਾਰ ਪੁੱਛਿਆ ਜਾਵੇਗਾ ਕਿ ਡਾਊਨਲੋਡ ਨੂੰ ਕਿੱਥੇ ਸਾਂਭਣਾ ਹੈ
+ ਤੁਹਾਨੂੰ ਪੁੱਛਿਆ ਜਾਵੇਗਾ ਕਿ ਡਾਊਨਲੋਡ ਨੂੰ ਕਿੱਥੇ ਸਾਂਭਣਾ ਹੈ।
\nਜੇ ਤੁਸੀਂ ਬਾਹਰੀ SD ਕਾਰਡ ਤੇ ਡਾਊਨਲੋਡ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ ਤਾਂ ਇਸ ਨੂੰ ਚਾਲੂ ਕਰੋ
SAF ਦੀ ਵਰਤੋਂ ਕਰੋ
ਸਟੋਰੇਜ਼ ਐਕਸੈਸ ਫਰੇਮਵਰਕ ਬਾਹਰੀ SD ਕਾਰਡ ਵਿੱਚ ਡਾਊਨਲੋਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ.
\nਨੋਟ: ਕੁਝ ਉਪਕਰਣ ਅਨੁਕੂਲ ਨਹੀਂ ਹਨ
+ ਇਹ ਸਮੱਗਰੀ ਹਾਲੇ ਨਿਊ-ਪਾਈਪ \'ਤੇ ਕੰਮ ਨਹੀਂ ਕਰਦੀ।
+\n
+\nਉਮੀਦ ਹੈ ਆਉਣ ਵਾਲ਼ੇ ਕਿਸੇ ਵਰਜਨ ਵਿੱਚ ਇਹ ਕੰਮ ਕਰੇਗੀ।
+ ਪਲੇ-ਸੂਚੀ ਪੰਨਾ
+ ਜਦੋਂ ਉਪਲਬਧ ਹੋਵੇ ਤਾਂ ਖ਼ਾਸ ਫ਼ੀਡ ਤੋਂ ਮੰਗਵਾਓ
+ ਕੁਝ ਸੇਵਾਵਾਂ ਵਿੱਚ ਹੀ ਉਪਲੱਬਧ ਇਹ ਤਰੀਕਾ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਫੀਡ ਵਿੱਚ ਤੇਜ ਜਾਣਕਾਰੀ ਮੁਹੱਈਆ ਕਰਵਾਉਂਦਾ ਹੈ, ਪਰ ਕੁਝ ਗਿਣਤੀ ਦੀਆਂ ਹੀ ਆਈਟਮ ਪ੍ਰਦਾਨ ਕਰ ਸਕਦਾ ਹੈ ਉਹ ਵੀ ਅਕਸਰ ਬਿਨਾਂ ਪੂਰੀ ਜਾਣਕਾਰੀ( ਜਿਵੇਂ ਅਵਧੀ, ਸਟਰੀਮ ਦੀ ਕਿਸਮ, ਕੋਈ ਸਟਰੀਮ ਲਾਈਵ ਹੈ, ਨਹੀਂ ਦੱਸਦਾ)
+ ਤੇਜ਼ ਮੋਡ ਬੰਦ ਕਰੋ
+ ਤੇਜ਼ ਮੋਡ ਚਾਲੂ ਕਰੋ
+ ਐਕਸੋਪਲੇਅਰ ਦੀਆਂ ਬੰਦਿਸ਼ਾਂ ਕਰਕੇ ਲੱਭਣ ਮਿਆਦ %d ਸਕਿੰਟ ਸੈੱਟ ਕੀਤੀ ਗਈ ਸੀ
+ ਹਮੇਸ਼ਾਂ ਅਪਡੇਟ ਕਰੋ
+ ਫ਼ੀਡ ਅਪਡੇਟ ਦੀ ਹੱਦ
+ ਅੰਤਰਾਲ ਜਿਸ ਬਾਅਦ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਫੀਡਾਂ ਦੁਬਾਰਾ ਅੱਪਡੇਟ ਕੀਤੀਆਂ ਜਾ ਸਕਣ — %s
+ ਫ਼ੀਡ
+ ਸਿਰਫ਼ ਉਹ ਸਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਵਿਖਾਓ ਜੋ ਕਿਸੇ ਗਰੁੱਪ ਵਿੱਚ ਨਹੀਂ ਪਾਈਆਂ ਹੋਈਆਂ
+ ਨਵਾਂ
+ ਕੀ ਤੁਸੀਂ ਇਸ ਗਰੁੱਪ ਨੂੰ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ\?
+ ਗਰੁੱਪ ਨਾਮ ਖ਼ਾਲੀ ਕਰੋ
+
+ - %d ਚੁਣੀ
+ - %d ਚੁਣੀਆਂ
+
+ ਕੋਈ ਸਬਸਕ੍ਰਿਪਸ਼ਨ ਨਹੀਂ ਚੁਣੀ ਹੋਈ
+ ਸੁਬਸਕ੍ਰਿਪਸ਼ਨਾਂ ਚੁਣੋ
+ ਫ਼ੀਡ \'ਤੇ ਅਮਲ ਹੋ ਰਿਹੈ…
+ ਫ਼ੀਡ ਲੋਡ ਹੋ ਰਹੀ ਹੈ…
+ ਲੋਡ ਨਹੀਂ ਹੋਇਆ: %d
+ ਫ਼ੀਡ ਆਖ਼ਰੀ ਵਾਰ %s ਨੂੰ ਅਪਡੇਟ ਹੋਈ ਸੀ
+ ਚੈਨਲ ਗਰੁੱਪ
+
+ - %d ਸਕਿੰਟ
+ - %d ਸਕਿੰਟ
+
+
+ - %d ਮਿੰਟ
+ - %d ਮਿੰਟ
+
+
+ - %d ਘੰਟਾ
+ - %d ਘੰਟੇ
+
+
+ - %d ਦਿਨ
+ - %d ਦਿਨ
+
+ ਹਾਂ, ਅਤੇ ਅੱਧ-ਪਚੱਧੀਆਂ ਵੇਖੀਆਂ ਹੋਈਆਂ ਵੀ
+ ਵੀਡੀਓਜ਼ ਜੋ ਪਲੇ-ਸੂਚੀ ਵਿੱਚ ਜੋੜੇ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਅਤੇ ਬਾਅਦ ਵਿੱਚ ਵੇਖੀਆਂ ਜਾ ਚੁੱਕੀਆਂ ਹਨ, ਉਹ ਹਟਾ ਦਿੱਤੀਆਂ ਜਾਣਗੀਆਂ.
+\nਕੀ ਵਾਕਿਆ ਹੀ ਤੁਸੀਂ ਇਹਨਾਂ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ\? ਇਸ ਕਾਰਵਾਈ ਨੂੰ ਵਾਪਸ ਨਹੀਂ ਮੋੜਿਆ ਜਾ ਸਕਣਾ!
+ ਇਹ ਡਾਊਨਲੋਡ ਮੁੜ-ਪ੍ਰਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ
+ ਸੇਵਾਵਾਂ ਵੱਲੋਂ ਆਈ ਅਸਲ ਲਿਖਤ ਸਟ੍ਰੀਮ ਨਗਾਂ ਵਿੱਚ ਵਿਖਾਈ ਦੇਵੇਗੀ
+ ਨਗਾਂ ਦੇ ਆਉਣ ਦਾ ਅਸਲੀ ਸਮਾਂ ਵਿਖਾਓ
+ ਟੌਗਲ ਸੇਵਾ, ਮੌਜੂਦਾ ਚੋਣ:
+ ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਜਾਂਚ ਲਓ ਕਿ ਤੁਹਾਡੇ ਵਾਲ਼ੇ ਕ੍ਰੈਸ਼ ਦੀ ਗੱਲ ਕਰਦਾ ਕੋਈ ਮਸਲਾ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਤਾਂ ਨਹੀਂ। ਇੱਕੋ ਮਸਲੇ ਦੀਆਂ ਦੋ ਜਾਂ ਵੱਧ ਨਕਲਾਂ ਬਣਾ ਕੇ ਤੁਸੀਂ ਸਾਡੇ ਤੋਂ ਉਹ ਵਕਤ ਖੋਹ ਲੈਂਦੇ ਹੋ ਜੋ ਅਸੀਂ ਅਸਲੀ ਮਸਲੇ ਦੇ ਹੱਲ ਲਈ ਲਾਉਣਾ ਸੀ।
+ ਸਿਸਟਮ ਡਿਫ਼ਾਲਟ
+ ਐਪ ਭਾਸ਼ਾ
+ ਕੋਈ ਸਥਿਤੀ ਚੁਣੋ
+ ਸਥਿਤੀ ਪਹਿਲਾਂ ਤੋਂ ਮੌਜੂਦ ਹੈ
+ ਸਿਰਫ਼ HTTP URLs ਹੀ ਮੰਨਣਯੋਗ ਹਨ
+ ਸਥਿਤੀ ਦੀ ਜਾਇਜ਼ਗੀ ਤਸਦੀਕ ਨਹੀਂ ਹੋ ਸਕੀ
+ ਸਥਿਤੀ URL ਦਾਖ਼ਲ ਕਰੋ
+ ਸਥਿਤੀਆਂ ਜੋੜੋ
+ ਤੁਹਾਡੀਆਂ ਪਸੰਦੀਦਾ ਸਥਿਤੀਆਂ %s \'ਤੇ ਲੱਭੋ
+ ਆਪਣੀ ਪਸੰਦੀਦਾ ਪੀਅਰਟਿਊਬ ਸਥਿਤੀਆਂ ਚੁਣੋ
+ ਪੀਅਰਟਿਊਬ ਸਥਿਤੀਆਂ
+ ਸਾਰੀਆਂ ਪਲੇ-ਸਥਿਤੀਆਂ ਮਿਟਾਉਣੀਆਂ ਹਨ\?
+ ਸਾਰੀਆਂ ਪਲੇ-ਸਥਿਤੀਆਂ ਮਿਟਾਉਂਦਾ ਹੈ
+ ਪਲੇ-ਸਥਿਤੀਆਂ ਮਿਟਾਓ
+ ਹਾਲੀਆ
+ ਉਚੇਚੀ ਪੇਸ਼ਕਸ਼
+ ਰੇਡੀਓ
+ ਐਲਬਮ
+ ਕਲਾਕਾਰ
+ ਗੀਤ
+ ਵੀਡੀਓਜ਼
+ ਇਹ ਵੀਡੀਓ ਉਮਰ-ਪਾਬੰਦੀਸ਼ੁਦਾ ਹੈ।
+\n
+\nਜੇ ਤੁਸੀਂ ਇਸਨੂੰ ਵੇਖਣਾ ਚਾਹੁੰਦੇ ਹੋ ਤਾਂ ਸੈਟਿੰਗਾਂ ਵਿੱਚੋਂ \"%1$s\" ਚਾਲੂ ਕਰੋ।
+ ਇਹ ਵੀਡੀਓ ਉਮਰ-ਹੱਦ ਮੁਤਾਬਕ ਪਾਬੰਦੀਸ਼ੁਦਾ ਹੈ।
+\nਯੂਟਿਊਬ ਦੀਆਂ ਉਮਰ-ਹੱਦ ਪਾਬੰਦੀਸ਼ੁਦਾ ਵੀਡੀਓਜ਼ ਬਾਰੇ ਨੀਤੀਆਂ ਦੇ ਕਾਰਨ ਨਿਊ-ਪਾਈਪ ਇਸਦੀਆਂ ਵੀਡੀਓ ਸਟ੍ਰੀਮ ਤੱਕ ਨਹੀਂ ਪਹੁੰਚ ਸਕਦੀ ਅਤੇ ਇਸੇ ਕਰਕੇ ਇਸਨੂੰ ਚਲਾ ਵੀ ਨਹੀਂ ਸਕਦੀ।
+ ਯੂਟਿਊਬ ਦਾ ਪਾਬੰਦੀਸ਼ੁਦਾ ਮੋਡ ਚਾਲੂ ਕਰੋ
+ ਇਹ ਸਮੱਗਰੀ ਸਿਰਫ਼ ਉਹਨਾਂ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਉਪਲਬਧ ਹੈ ਜਿੰਨ੍ਹਾਂ ਨੇ ਇਸਦੇ ਲਈ ਕੀਮਤ ਦਿੱਤੀ ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।
+ ਇਹ ਵੀਡੀਓ ਸਿਰਫ਼ ਯੂਟਿਊਬ ਮਿਊਜ਼ਿਕ ਦੇ ਪ੍ਰੀਮੀਅਮ ਮੈਂਬਰਾਂ ਲਈ ਉਪਲਬਧ ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।
+ ਇਹ ਸਮੱਗਰੀ ਨਿੱਜੀ (ਪ੍ਰਾਈਵੇਟ) ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।
+ ਇਹ ਸਮੱਗਰੀ ਤੁਹਾਡੇ ਮੁਲਕ ਵਿੱਚ ਉਪਲਬਧ ਨਹੀਂ।
+ %s ਲਈ ਨਤੀਜੇ ਵਿਖਾਏ ਜਾ ਰਹੇ ਹਨ
+ ਦਿਲੋਂ ਪਸੰਦ ਕੀਤਾ
\ No newline at end of file
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index deef7afbd..d7ef37a81 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -38,7 +38,7 @@
Pobrane
Dalej
Pokaż \"następne\" i \"podobne\" filmy
- Nieobsługiwany adres URL
+ Nieobsługiwany adres URL
Domyślny język treści
Wideo i audio
Wygląd
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index a0a62ca91..e5c0a25e3 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -69,7 +69,7 @@
Permita acesso ao armazenamento primeiramente
Tema
Publicado em %1$s
- Link não suportado
+ Link não suportado
Ao vivo
Downloads
Downloads
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index a52946dbd..fed41b35d 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -29,7 +29,7 @@
Transferir
Seguinte
Mostrar vídeos \'Seguintes\' e \'Semelhantes\'
- URL não suportado
+ URL não suportado
Idioma do conteúdo predefinido
Vídeo e áudio
Miniatura de pré-visualização de vídeo
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 2d08addb9..dfe732d5f 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -36,7 +36,7 @@
Descărcați
Următorul
Arată videoclipurile care urmează și similare
- URL nesuportat
+ URL nesuportat
Limba dorită a conținutului
Video & Audio
Aspect
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 913475d8e..67c920ff1 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -26,7 +26,7 @@
Формат аудио по умолчанию
Скачать
Следующее
- URL не поддерживается
+ URL не поддерживается
\"Следующее\" и похожие видео
Язык контента по умолчанию
Видео и аудио
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 3352c81f8..3d1e234a3 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -35,7 +35,7 @@
Prevziať
Ďalšie
Ukázať \'Ďalšie\' a \'Podobné\' videá
- URL nie je podporovaná
+ URL nie je podporovaná
Preferovaný jazyk obsahu
Video & zvuk
Vzhľad
diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml
index c1cc8d963..189cf86fe 100644
--- a/app/src/main/res/values-sl/strings.xml
+++ b/app/src/main/res/values-sl/strings.xml
@@ -28,7 +28,7 @@
Prejem
Naslednji video
Pokaži naslednji video in podobne posnetke
- Zapis naslova URL ni podprt.
+ Zapis naslova URL ni podprt.
Privzeti jezik vsebine
Video in Zvok
Sličica predogleda videa
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 278e27371..ca9af4d00 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -546,7 +546,7 @@
Zgjidhni instancat tuaja të preferuara të PeerTube
Instancat PeerTube
Shteti i parazgjedhur i përmbajtjes
- URL e pambështetur
+ URL e pambështetur
Trego ndihmën kur shtypet sfondi ose butoni i popup në \"Detajet:\" e videos
Trego ndihmën \"Mbaje shtypur për ta shtuar në listë\"
Trego \'Tjetra\' dhe videot \'E ngjashme\'
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 1cd4d2baa..af0002879 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -26,7 +26,7 @@
Подразумевани формат звука
Преузми
Следећи видео
- УРЛ није подржан
+ УРЛ није подржан
Прикажи следећи и слични видео
Подразумевани језик садржаја
Видео и аудио
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 23be5276b..588868814 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -52,7 +52,7 @@
Ladda ned
Nästa
Visa \'Nästa\' och \'Liknande\' videor
- Webbadressen stöds inte
+ Webbadressen stöds inte
Standard innehållsspråk
Video & Ljud
Popup-ruta
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
index 52ef33abe..257b39b75 100644
--- a/app/src/main/res/values-ta/strings.xml
+++ b/app/src/main/res/values-ta/strings.xml
@@ -119,7 +119,7 @@
"தொலைபேசி அழைப்பு போன்ற குறுக்கீடுகளுக்கு பிறகு தொடரவும் "
\'அடுத்து\' மற்றும் \'ஒப்பான\' காணொளிகலை காண்பி
தேடப்பட்ட வாக்கியத்தை அமைவிடத்தில் சேமிக்கவும்
- ஆதரிக்கப்படாத URL
+ ஆதரிக்கப்படாத URL
இயல்புநிலை தகவல்களின் நாடு
பிழைதிருத்து
உள்ளடக்கம்
diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml
index 1f2fd4ce1..1f344bf23 100644
--- a/app/src/main/res/values-te/strings.xml
+++ b/app/src/main/res/values-te/strings.xml
@@ -46,7 +46,7 @@
తదుపరి వీడియో
తదుపరి వీడియో మరియు ఇలాంటి వీడియో
చిట్కాను అనుబంధించడానికి హోల్డ్ను చూపు
- Url మద్దతు లేదు
+ Url మద్దతు లేదు
డిఫాల్ట్ భాష
ప్లేయర్
ప్రవర్తన
diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml
index 7910a29c7..20999eb9a 100644
--- a/app/src/main/res/values-th/strings.xml
+++ b/app/src/main/res/values-th/strings.xml
@@ -91,7 +91,7 @@
แสดงวิดีโอ \'ถัดไป\' และ \'ที่คล้ายกัน\'
แสดงเคล็ดลับ \"แตะค้างเพื่อเพิ่ม\"
แสดงเคล็ดลับเมื่อกดปุ่มพื้นหลังหรือป๊อปอัพในหน้ารายละเอียดวิดีโอ
- URL ที่ไม่สนับสนุน
+ URL ที่ไม่สนับสนุน
ประเทศเริ่มต้นของเนื้อหา
บริการ
ภาษาของเนื้อหาเริ่มต้น
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 95c25987d..a99010e62 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -37,7 +37,7 @@
İndir
Sonraki
\'Sonraki\' ve \'Benzer\' videoları göster
- Desteklenmeyen URL
+ Desteklenmeyen URL
Öntanımlı içerik dili
Ses
Video ve ses
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 20e9dbde1..57d432e5c 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -37,7 +37,7 @@
Завантажити
Наступне
Показувати \"Наступне\" і \"Схожі\" відео
- URL не підтримується
+ URL не підтримується
Переважна мова контенту
Відео та Аудіо
Зовнішній вигляд
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index 5609902e3..9963592d9 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -85,7 +85,7 @@
\'اگلی\' اور \'ملتی جلتی\' ویڈیوز دکھائیں
’’شامل کرنے کے لئے پکڑیں‘‘ اشارہ دکھائیں
ویڈیو تفصیلات کے صفحے پر جب پس منظر یا پاپ اپ بٹن دبائے جانے پر ٹپ دکھائیں
- غیر موافق URL
+ غیر موافق URL
مشمولات کا طے شدہ ملک
خدمت
پلیئر
diff --git a/app/src/main/res/values-v21/styles_services.xml b/app/src/main/res/values-v21/styles_services.xml
index d40065059..5037d875b 100644
--- a/app/src/main/res/values-v21/styles_services.xml
+++ b/app/src/main/res/values-v21/styles_services.xml
@@ -12,6 +12,7 @@
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml
index de68702d3..5472cfcdd 100644
--- a/app/src/main/res/xml/content_settings.xml
+++ b/app/src/main/res/xml/content_settings.xml
@@ -96,6 +96,12 @@
android:key="@string/export_data"
android:summary="@string/export_data_summary"/>
+
+
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
new file mode 100644
index 000000000..284be8c6e
--- /dev/null
+++ b/app/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml
index d1757919b..191afe1d6 100644
--- a/app/src/main/res/xml/video_audio_settings.xml
+++ b/app/src/main/res/xml/video_audio_settings.xml
@@ -162,5 +162,16 @@
android:key="@string/seek_duration_key"
android:summary="%s"
android:title="@string/seek_duration_title"/>
+
+
+
diff --git a/app/src/test/java/org/schabi/newpipe/ReCaptchaActivityTest.kt b/app/src/test/java/org/schabi/newpipe/ReCaptchaActivityTest.kt
new file mode 100644
index 000000000..0cea81be6
--- /dev/null
+++ b/app/src/test/java/org/schabi/newpipe/ReCaptchaActivityTest.kt
@@ -0,0 +1,27 @@
+package org.schabi.newpipelegacy
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.schabi.newpipelegacy.ReCaptchaActivity.YT_URL
+
+class ReCaptchaActivityTest {
+ private fun assertSanitized(expected: String, actual: String?) {
+ assertEquals(expected, ReCaptchaActivity.sanitizeRecaptchaUrl(actual))
+ }
+
+ @Test fun `null, empty or blank url is sanitized correctly`() {
+ assertSanitized(YT_URL, null)
+ assertSanitized(YT_URL, "")
+ assertSanitized(YT_URL, " \n \t ")
+ }
+
+ @Test fun `YouTube url containing pbj=1 is sanitized correctly`() {
+ val sanitizedUrl = "https://m.youtube.com/results?search_query=test"
+ assertSanitized(sanitizedUrl, "https://m.youtube.com/results?search_query=test")
+ assertSanitized(sanitizedUrl, "https://m.youtube.com/results?search_query=test&pbj=1&pbj=1")
+ assertSanitized(sanitizedUrl, "https://m.youtube.com/results?pbj=1&search_query=test")
+ assertSanitized("pbj://pbj.pbj.pbj/pbj", "pbj://pbj.pbj.pbj/pbj?pbj=1")
+ assertSanitized("http://www.host.com/b?p1=7&p2=9", "http://www.host.com/b?p1=7&pbj=1&p2=9")
+ assertSanitized("http://www.host.com/a?pbj=0", "http://www.host.com/a?pbj=0")
+ }
+}
diff --git a/app/src/test/java/org/schabi/newpipe/ktx/OffsetDateTimeToCalendarTest.kt b/app/src/test/java/org/schabi/newpipe/ktx/OffsetDateTimeToCalendarTest.kt
new file mode 100644
index 000000000..37170935d
--- /dev/null
+++ b/app/src/test/java/org/schabi/newpipe/ktx/OffsetDateTimeToCalendarTest.kt
@@ -0,0 +1,30 @@
+package org.schabi.newpipelegacy.ktx
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.time.LocalDate
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.util.Calendar
+import java.util.TimeZone
+
+class OffsetDateTimeToCalendarTest {
+ @Test
+ fun testRelativeTimeWithCurrentOffsetDateTime() {
+ val calendar = LocalDate.of(2020, 1, 1).atStartOfDay().atOffset(ZoneOffset.UTC)
+ .toCalendar()
+
+ assertEquals(2020, calendar[Calendar.YEAR])
+ assertEquals(0, calendar[Calendar.MONTH])
+ assertEquals(1, calendar[Calendar.DAY_OF_MONTH])
+ assertEquals(0, calendar[Calendar.HOUR])
+ assertEquals(0, calendar[Calendar.MINUTE])
+ assertEquals(0, calendar[Calendar.SECOND])
+ assertEquals(TimeZone.getTimeZone("UTC"), calendar.timeZone)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testRelativeTimeWithFarOffOffsetDateTime() {
+ OffsetDateTime.MAX.minusYears(1).toCalendar()
+ }
+}
diff --git a/app/src/test/java/org/schabi/newpipe/settings/ContentSettingsManagerTest.kt b/app/src/test/java/org/schabi/newpipe/settings/ContentSettingsManagerTest.kt
new file mode 100644
index 000000000..b577cbd70
--- /dev/null
+++ b/app/src/test/java/org/schabi/newpipe/settings/ContentSettingsManagerTest.kt
@@ -0,0 +1,76 @@
+package org.schabi.newpipelegacy.settings
+
+import android.content.SharedPreferences
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Suite
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
+import org.schabi.newpipelegacy.settings.ContentSettingsManagerTest.ExportTest
+import java.io.File
+import java.io.ObjectInputStream
+import java.util.zip.ZipFile
+
+@RunWith(Suite::class)
+@Suite.SuiteClasses(ExportTest::class)
+class ContentSettingsManagerTest {
+
+ @RunWith(MockitoJUnitRunner::class)
+ class ExportTest {
+
+ companion object {
+ private lateinit var newpipeDb: File
+ private lateinit var newpipeSettings: File
+
+ @JvmStatic
+ @BeforeClass
+ fun setupFiles() {
+ val dbPath = ExportTest::class.java.classLoader?.getResource("settings/newpipe.db")?.file
+ val settingsPath = ExportTest::class.java.classLoader?.getResource("settings/newpipe.settings")?.path
+ Assume.assumeNotNull(dbPath)
+ Assume.assumeNotNull(settingsPath)
+
+ newpipeDb = File(dbPath!!)
+ newpipeSettings = File(settingsPath!!)
+ }
+ }
+
+ private lateinit var preferences: SharedPreferences
+
+ @Before
+ fun setupMocks() {
+ preferences = Mockito.mock(SharedPreferences::class.java, Mockito.withSettings().stubOnly())
+ }
+
+ @Test
+ fun `The settings must be exported successfully in the correct format`() {
+ val expectedPreferences = mapOf("such pref" to "much wow")
+ `when`(preferences.all).thenReturn(expectedPreferences)
+
+ val manager = ContentSettingsManager(newpipeDb, newpipeSettings)
+
+ val output = File.createTempFile("newpipe_", "")
+ manager.exportDatabase(preferences, output.absolutePath)
+
+ val zipFile = ZipFile(output.absoluteFile)
+ val entries = zipFile.entries().toList()
+ Assert.assertEquals(2, entries.size)
+
+ zipFile.getInputStream(entries.first { it.name == "newpipe.db" }).use { actual ->
+ newpipeDb.inputStream().use { expected ->
+ Assert.assertEquals(expected.reader().readText(), actual.reader().readText())
+ }
+ }
+
+ zipFile.getInputStream(entries.first { it.name == "newpipe.settings" }).use { actual ->
+ val actualPreferences = ObjectInputStream(actual).readObject()
+ Assert.assertEquals(expectedPreferences, actualPreferences)
+ }
+ }
+ }
+}
diff --git a/app/src/test/java/org/schabi/newpipelegacy/local/subscription/FeedGroupIconTest.kt b/app/src/test/java/org/schabi/newpipelegacy/local/subscription/FeedGroupIconTest.kt
index 9ead9b5c7..7debb7d1e 100644
--- a/app/src/test/java/org/schabi/newpipelegacy/local/subscription/FeedGroupIconTest.kt
+++ b/app/src/test/java/org/schabi/newpipelegacy/local/subscription/FeedGroupIconTest.kt
@@ -13,8 +13,10 @@ class FeedGroupIconTest {
val added = usedIds.add(currentIcon.id)
assertTrue("Repeated ids (current item: ${currentIcon.name} - ${currentIcon.id})", added)
- assertEquals("Gap between ids detected (current item: ${currentIcon.name} - ${currentIcon.id} → should be: $shouldBeId)",
- shouldBeId, currentIcon.id)
+ assertEquals(
+ "Gap between ids detected (current item: ${currentIcon.name} - ${currentIcon.id} → should be: $shouldBeId)",
+ shouldBeId, currentIcon.id
+ )
}
}
diff --git a/app/src/test/java/org/schabi/newpipelegacy/local/subscription/services/ImportExportJsonHelperTest.java b/app/src/test/java/org/schabi/newpipelegacy/local/subscription/services/ImportExportJsonHelperTest.java
index 888bf3255..63269de32 100644
--- a/app/src/test/java/org/schabi/newpipelegacy/local/subscription/services/ImportExportJsonHelperTest.java
+++ b/app/src/test/java/org/schabi/newpipelegacy/local/subscription/services/ImportExportJsonHelperTest.java
@@ -24,7 +24,7 @@ public void testEmptySource() throws Exception {
"{\"app_version\":\"0.11.6\",\"app_version_int\": 47,\"subscriptions\":[]}";
List items = ImportExportJsonHelper.readFrom(new ByteArrayInputStream(
- emptySource.getBytes(StandardCharsets.UTF_8)), null);
+ emptySource.getBytes("utf-8")), null);
assertTrue(items.isEmpty());
}
@@ -39,7 +39,7 @@ public void testInvalidSource() {
for (String invalidContent : invalidList) {
try {
if (invalidContent != null) {
- byte[] bytes = invalidContent.getBytes(StandardCharsets.UTF_8);
+ byte[] bytes = invalidContent.getBytes("utf-8");
ImportExportJsonHelper.readFrom((new ByteArrayInputStream(bytes)), null);
} else {
ImportExportJsonHelper.readFrom(null, null);
@@ -112,7 +112,7 @@ private String testWriteTo(final List itemsFromFile) throws Ex
private List readFromWriteTo(final String jsonOut) throws Exception {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(
- jsonOut.getBytes(StandardCharsets.UTF_8));
+ jsonOut.getBytes("utf-8"));
final List secondReadItems = ImportExportJsonHelper.readFrom(
inputStream, null);
diff --git a/app/src/test/java/org/schabi/newpipelegacy/util/ExceptionUtilsTest.kt b/app/src/test/java/org/schabi/newpipelegacy/util/ExceptionUtilsTest.kt
index c787975e8..4a69588db 100644
--- a/app/src/test/java/org/schabi/newpipelegacy/util/ExceptionUtilsTest.kt
+++ b/app/src/test/java/org/schabi/newpipelegacy/util/ExceptionUtilsTest.kt
@@ -1,14 +1,14 @@
package org.schabi.newpipelegacy.util
-import java.io.IOException
-import java.io.InterruptedIOException
-import java.net.SocketException
-import javax.net.ssl.SSLException
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.schabi.newpipelegacy.util.ExceptionUtils.Companion.hasAssignableCause
import org.schabi.newpipelegacy.util.ExceptionUtils.Companion.hasExactCause
+import java.io.IOException
+import java.io.InterruptedIOException
+import java.net.SocketException
+import javax.net.ssl.SSLException
class ExceptionUtilsTest {
@Test fun `assignable causes`() {
diff --git a/app/src/test/java/org/schabi/newpipelegacy/util/ListHelperTest.java b/app/src/test/java/org/schabi/newpipelegacy/util/ListHelperTest.java
index 5cf709888..87acb028b 100644
--- a/app/src/test/java/org/schabi/newpipelegacy/util/ListHelperTest.java
+++ b/app/src/test/java/org/schabi/newpipelegacy/util/ListHelperTest.java
@@ -11,38 +11,41 @@
import static org.junit.Assert.assertEquals;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
public class ListHelperTest {
private static final String BEST_RESOLUTION_KEY = "best_resolution";
private static final List AUDIO_STREAMS_TEST_LIST = Arrays.asList(
- new AudioStream("", MediaFormat.M4A, /**/ 128),
- new AudioStream("", MediaFormat.WEBMA, /**/ 192),
- new AudioStream("", MediaFormat.MP3, /**/ 64),
- new AudioStream("", MediaFormat.WEBMA, /**/ 192),
- new AudioStream("", MediaFormat.M4A, /**/ 128),
- new AudioStream("", MediaFormat.MP3, /**/ 128),
- new AudioStream("", MediaFormat.WEBMA, /**/ 64),
- new AudioStream("", MediaFormat.M4A, /**/ 320),
- new AudioStream("", MediaFormat.MP3, /**/ 192),
- new AudioStream("", MediaFormat.WEBMA, /**/ 320));
+ generateAudioStream("m4a-128-1", MediaFormat.M4A, 128),
+ generateAudioStream("webma-192", MediaFormat.WEBMA, 192),
+ generateAudioStream("mp3-64", MediaFormat.MP3, 64),
+ generateAudioStream("webma-192", MediaFormat.WEBMA, 192),
+ generateAudioStream("m4a-128-2", MediaFormat.M4A, 128),
+ generateAudioStream("mp3-128", MediaFormat.MP3, 128),
+ generateAudioStream("webma-64", MediaFormat.WEBMA, 64),
+ generateAudioStream("m4a-320", MediaFormat.M4A, 320),
+ generateAudioStream("mp3-192", MediaFormat.MP3, 192),
+ generateAudioStream("webma-320", MediaFormat.WEBMA, 320));
private static final List VIDEO_STREAMS_TEST_LIST = Arrays.asList(
- new VideoStream("", MediaFormat.MPEG_4, /**/ "720p"),
- new VideoStream("", MediaFormat.v3GPP, /**/ "240p"),
- new VideoStream("", MediaFormat.WEBM, /**/ "480p"),
- new VideoStream("", MediaFormat.v3GPP, /**/ "144p"),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "360p"),
- new VideoStream("", MediaFormat.WEBM, /**/ "360p"));
+ generateVideoStream("mpeg_4-720", MediaFormat.MPEG_4, "720p", false),
+ generateVideoStream("v3gpp-240", MediaFormat.v3GPP, "240p", false),
+ generateVideoStream("webm-480", MediaFormat.WEBM, "480p", false),
+ generateVideoStream("v3gpp-144", MediaFormat.v3GPP, "144p", false),
+ generateVideoStream("mpeg_4-360", MediaFormat.MPEG_4, "360p", false),
+ generateVideoStream("webm-360", MediaFormat.WEBM, "360p", false));
private static final List VIDEO_ONLY_STREAMS_TEST_LIST = Arrays.asList(
- new VideoStream("", MediaFormat.MPEG_4, /**/ "720p", true),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "720p", true),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "2160p", true),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "1440p60", true),
- new VideoStream("", MediaFormat.WEBM, /**/ "720p60", true),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "2160p60", true),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "720p60", true),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "1080p", true),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "1080p60", true));
+ generateVideoStream("mpeg_4-720-1", MediaFormat.MPEG_4, "720p", true),
+ generateVideoStream("mpeg_4-720-2", MediaFormat.MPEG_4, "720p", true),
+ generateVideoStream("mpeg_4-2160", MediaFormat.MPEG_4, "2160p", true),
+ generateVideoStream("mpeg_4-1440_60", MediaFormat.MPEG_4, "1440p60", true),
+ generateVideoStream("webm-720_60", MediaFormat.WEBM, "720p60", true),
+ generateVideoStream("mpeg_4-2160_60", MediaFormat.MPEG_4, "2160p60", true),
+ generateVideoStream("mpeg_4-720_60", MediaFormat.MPEG_4, "720p60", true),
+ generateVideoStream("mpeg_4-1080", MediaFormat.MPEG_4, "1080p", true),
+ generateVideoStream("mpeg_4-1080_60", MediaFormat.MPEG_4, "1080p60", true));
@Test
public void getSortedStreamVideosListTest() {
@@ -59,7 +62,8 @@ public void getSortedStreamVideosListTest() {
assertEquals(result.size(), expected.size());
for (int i = 0; i < result.size(); i++) {
- assertEquals(result.get(i).resolution, expected.get(i));
+ assertEquals(result.get(i).getResolution(), expected.get(i));
+ assertEquals(expected.get(i), result.get(i).getResolution());
}
////////////////////
@@ -72,14 +76,14 @@ public void getSortedStreamVideosListTest() {
"720p", "480p", "360p", "240p", "144p");
assertEquals(result.size(), expected.size());
for (int i = 0; i < result.size(); i++) {
- assertEquals(result.get(i).resolution, expected.get(i));
+ assertEquals(result.get(i).getResolution(), expected.get(i));
}
}
@Test
public void getSortedStreamVideosExceptHighResolutionsTest() {
////////////////////////////////////
- // Don't show Higher resolutions //
+ // Don't show Higher getResolution()s //
//////////////////////////////////
List result = ListHelper.getSortedStreamVideosList(MediaFormat.MPEG_4,
@@ -95,59 +99,59 @@ public void getSortedStreamVideosExceptHighResolutionsTest() {
@Test
public void getDefaultResolutionTest() {
List testList = Arrays.asList(
- new VideoStream("", MediaFormat.MPEG_4, /**/ "720p"),
- new VideoStream("", MediaFormat.v3GPP, /**/ "240p"),
- new VideoStream("", MediaFormat.WEBM, /**/ "480p"),
- new VideoStream("", MediaFormat.WEBM, /**/ "240p"),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "240p"),
- new VideoStream("", MediaFormat.WEBM, /**/ "144p"),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "360p"),
- new VideoStream("", MediaFormat.WEBM, /**/ "360p"));
+ generateVideoStream("mpeg_4-720", MediaFormat.MPEG_4, "720p", false),
+ generateVideoStream("v3gpp-240", MediaFormat.v3GPP, "240p", false),
+ generateVideoStream("webm-480", MediaFormat.WEBM, "480p", false),
+ generateVideoStream("webm-240", MediaFormat.WEBM, "240p", false),
+ generateVideoStream("mpeg_4-240", MediaFormat.MPEG_4, "240p", false),
+ generateVideoStream("webm-144", MediaFormat.WEBM, "144p", false),
+ generateVideoStream("mpeg_4-360", MediaFormat.MPEG_4, "360p", false),
+ generateVideoStream("webm-360", MediaFormat.WEBM, "360p", false));
VideoStream result = testList.get(ListHelper.getDefaultResolutionIndex(
"720p", BEST_RESOLUTION_KEY, MediaFormat.MPEG_4, testList));
- assertEquals("720p", result.resolution);
+ assertEquals("720p", result.getResolution());
assertEquals(MediaFormat.MPEG_4, result.getFormat());
- // Have resolution and the format
+ // Have getResolution() and the format
result = testList.get(ListHelper.getDefaultResolutionIndex(
"480p", BEST_RESOLUTION_KEY, MediaFormat.WEBM, testList));
- assertEquals("480p", result.resolution);
+ assertEquals("480p", result.getResolution());
assertEquals(MediaFormat.WEBM, result.getFormat());
- // Have resolution but not the format
+ // Have getResolution() but not the format
result = testList.get(ListHelper.getDefaultResolutionIndex(
"480p", BEST_RESOLUTION_KEY, MediaFormat.MPEG_4, testList));
- assertEquals("480p", result.resolution);
+ assertEquals("480p", result.getResolution());
assertEquals(MediaFormat.WEBM, result.getFormat());
- // Have resolution and the format
+ // Have getResolution() and the format
result = testList.get(ListHelper.getDefaultResolutionIndex(
"240p", BEST_RESOLUTION_KEY, MediaFormat.WEBM, testList));
- assertEquals("240p", result.resolution);
+ assertEquals("240p", result.getResolution());
assertEquals(MediaFormat.WEBM, result.getFormat());
- // The best resolution
+ // The best getResolution()
result = testList.get(ListHelper.getDefaultResolutionIndex(
BEST_RESOLUTION_KEY, BEST_RESOLUTION_KEY, MediaFormat.WEBM, testList));
- assertEquals("720p", result.resolution);
+ assertEquals("720p", result.getResolution());
assertEquals(MediaFormat.MPEG_4, result.getFormat());
// Doesn't have the 60fps variant and format
result = testList.get(ListHelper.getDefaultResolutionIndex(
"720p60", BEST_RESOLUTION_KEY, MediaFormat.WEBM, testList));
- assertEquals("720p", result.resolution);
+ assertEquals("720p", result.getResolution());
assertEquals(MediaFormat.MPEG_4, result.getFormat());
// Doesn't have the 60fps variant
result = testList.get(ListHelper.getDefaultResolutionIndex(
"480p60", BEST_RESOLUTION_KEY, MediaFormat.WEBM, testList));
- assertEquals("480p", result.resolution);
+ assertEquals("480p", result.getResolution());
assertEquals(MediaFormat.WEBM, result.getFormat());
- // Doesn't have the resolution, will return the best one
+ // Doesn't have the getResolution(), will return the best one
result = testList.get(ListHelper.getDefaultResolutionIndex(
"2160p60", BEST_RESOLUTION_KEY, MediaFormat.WEBM, testList));
- assertEquals("720p", result.resolution);
+ assertEquals("720p", result.getResolution());
assertEquals(MediaFormat.MPEG_4, result.getFormat());
}
@@ -155,17 +159,17 @@ public void getDefaultResolutionTest() {
public void getHighestQualityAudioFormatTest() {
AudioStream stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getHighestQualityAudioIndex(
MediaFormat.M4A, AUDIO_STREAMS_TEST_LIST));
- assertEquals(320, stream.average_bitrate);
+ assertEquals(320, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat());
stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getHighestQualityAudioIndex(
MediaFormat.WEBMA, AUDIO_STREAMS_TEST_LIST));
- assertEquals(320, stream.average_bitrate);
+ assertEquals(320, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat());
stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getHighestQualityAudioIndex(
MediaFormat.MP3, AUDIO_STREAMS_TEST_LIST));
- assertEquals(192, stream.average_bitrate);
+ assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.MP3, stream.getFormat());
}
@@ -177,13 +181,13 @@ public void getHighestQualityAudioFormatPreferredAbsent() {
////////////////////////////////////////
List testList = Arrays.asList(
- new AudioStream("", MediaFormat.M4A, /**/ 128),
- new AudioStream("", MediaFormat.WEBMA, /**/ 192));
+ generateAudioStream("m4a-128", MediaFormat.M4A, 128),
+ generateAudioStream("webma-192", MediaFormat.WEBMA, 192));
// List doesn't contains this format
// It should fallback to the highest bitrate audio no matter what format it is
AudioStream stream = testList.get(ListHelper.getHighestQualityAudioIndex(
MediaFormat.MP3, testList));
- assertEquals(192, stream.average_bitrate);
+ assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat());
////////////////////////////////////////////////////////
@@ -191,24 +195,24 @@ public void getHighestQualityAudioFormatPreferredAbsent() {
//////////////////////////////////////////////////////
testList = new ArrayList<>(Arrays.asList(
- new AudioStream("", MediaFormat.WEBMA, /**/ 192),
- new AudioStream("", MediaFormat.M4A, /**/ 192),
- new AudioStream("", MediaFormat.WEBMA, /**/ 192),
- new AudioStream("", MediaFormat.M4A, /**/ 192),
- new AudioStream("", MediaFormat.WEBMA, /**/ 192),
- new AudioStream("", MediaFormat.M4A, /**/ 192),
- new AudioStream("", MediaFormat.WEBMA, /**/ 192)));
+ generateAudioStream("webma-192-1", MediaFormat.WEBMA, 192),
+ generateAudioStream("m4a-192-1", MediaFormat.M4A, 192),
+ generateAudioStream("webma-192-2", MediaFormat.WEBMA, 192),
+ generateAudioStream("m4a-192-2", MediaFormat.M4A, 192),
+ generateAudioStream("webma-192-3", MediaFormat.WEBMA, 192),
+ generateAudioStream("m4a-192-3", MediaFormat.M4A, 192),
+ generateAudioStream("webma-192-4", MediaFormat.WEBMA, 192)));
// List doesn't contains this format, it should fallback to the highest bitrate audio and
// the highest quality format.
stream = testList.get(ListHelper.getHighestQualityAudioIndex(MediaFormat.MP3, testList));
- assertEquals(192, stream.average_bitrate);
+ assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat());
// Adding a new format and bitrate. Adding another stream will have no impact since
// it's not a prefered format.
- testList.add(new AudioStream("", MediaFormat.WEBMA, /**/ 192));
+ testList.add(generateAudioStream("webma-192-5", MediaFormat.WEBMA, /**/ 192));
stream = testList.get(ListHelper.getHighestQualityAudioIndex(MediaFormat.MP3, testList));
- assertEquals(192, stream.average_bitrate);
+ assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat());
}
@@ -222,17 +226,17 @@ public void getHighestQualityAudioNull() {
public void getLowestQualityAudioFormatTest() {
AudioStream stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getMostCompactAudioIndex(
MediaFormat.M4A, AUDIO_STREAMS_TEST_LIST));
- assertEquals(128, stream.average_bitrate);
+ assertEquals(128, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat());
stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getMostCompactAudioIndex(
MediaFormat.WEBMA, AUDIO_STREAMS_TEST_LIST));
- assertEquals(64, stream.average_bitrate);
+ assertEquals(64, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat());
stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getMostCompactAudioIndex(
MediaFormat.MP3, AUDIO_STREAMS_TEST_LIST));
- assertEquals(64, stream.average_bitrate);
+ assertEquals(64, stream.getAverageBitrate());
assertEquals(MediaFormat.MP3, stream.getFormat());
}
@@ -244,19 +248,19 @@ public void getLowestQualityAudioFormatPreferredAbsent() {
////////////////////////////////////////
List testList = new ArrayList<>(Arrays.asList(
- new AudioStream("", MediaFormat.M4A, /**/ 128),
- new AudioStream("", MediaFormat.WEBMA, /**/ 192)));
+ generateAudioStream("m4a-128", MediaFormat.M4A, 128),
+ generateAudioStream("webma-192-1", MediaFormat.WEBMA, 192)));
// List doesn't contains this format
// It should fallback to the most compact audio no matter what format it is.
AudioStream stream = testList.get(ListHelper.getMostCompactAudioIndex(
MediaFormat.MP3, testList));
- assertEquals(128, stream.average_bitrate);
+ assertEquals(128, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat());
// WEBMA is more compact than M4A
- testList.add(new AudioStream("", MediaFormat.WEBMA, /**/ 128));
+ testList.add(generateAudioStream("webma-192-2", MediaFormat.WEBMA, 128));
stream = testList.get(ListHelper.getMostCompactAudioIndex(MediaFormat.MP3, testList));
- assertEquals(128, stream.average_bitrate);
+ assertEquals(128, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat());
////////////////////////////////////////////////////////
@@ -264,21 +268,21 @@ public void getLowestQualityAudioFormatPreferredAbsent() {
//////////////////////////////////////////////////////
testList = new ArrayList<>(Arrays.asList(
- new AudioStream("", MediaFormat.WEBMA, /**/ 192),
- new AudioStream("", MediaFormat.M4A, /**/ 192),
- new AudioStream("", MediaFormat.WEBMA, /**/ 256),
- new AudioStream("", MediaFormat.M4A, /**/ 192),
- new AudioStream("", MediaFormat.WEBMA, /**/ 192),
- new AudioStream("", MediaFormat.M4A, /**/ 192)));
+ generateAudioStream("webma-192-1", MediaFormat.WEBMA, 192),
+ generateAudioStream("m4a-192-1", MediaFormat.M4A, 192),
+ generateAudioStream("webma-256", MediaFormat.WEBMA, 256),
+ generateAudioStream("m4a-192-2", MediaFormat.M4A, 192),
+ generateAudioStream("webma-192-2", MediaFormat.WEBMA, 192),
+ generateAudioStream("m4a-192-3", MediaFormat.M4A, 192)));
// List doesn't contain this format
// It should fallback to the most compact audio no matter what format it is.
stream = testList.get(ListHelper.getMostCompactAudioIndex(MediaFormat.MP3, testList));
- assertEquals(192, stream.average_bitrate);
+ assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat());
// Should be same as above
stream = testList.get(ListHelper.getMostCompactAudioIndex(null, testList));
- assertEquals(192, stream.average_bitrate);
+ assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat());
}
@@ -291,14 +295,14 @@ public void getLowestQualityAudioNull() {
@Test
public void getVideoDefaultStreamIndexCombinations() {
List testList = Arrays.asList(
- new VideoStream("", MediaFormat.MPEG_4, /**/ "1080p"),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "720p60"),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "720p"),
- new VideoStream("", MediaFormat.WEBM, /**/ "480p"),
- new VideoStream("", MediaFormat.MPEG_4, /**/ "360p"),
- new VideoStream("", MediaFormat.WEBM, /**/ "360p"),
- new VideoStream("", MediaFormat.v3GPP, /**/ "240p60"),
- new VideoStream("", MediaFormat.WEBM, /**/ "144p"));
+ generateVideoStream("mpeg_4-1080", MediaFormat.MPEG_4, "1080p", false),
+ generateVideoStream("mpeg_4-720_60", MediaFormat.MPEG_4, "720p60", false),
+ generateVideoStream("mpeg_4-720", MediaFormat.MPEG_4, "720p", false),
+ generateVideoStream("webm-480", MediaFormat.WEBM, "480p", false),
+ generateVideoStream("mpeg_4-360", MediaFormat.MPEG_4, "360p", false),
+ generateVideoStream("webm-360", MediaFormat.WEBM, "360p", false),
+ generateVideoStream("v3gpp-240_60", MediaFormat.v3GPP, "240p60", false),
+ generateVideoStream("webm-144", MediaFormat.WEBM, "144p", false));
// exact matches
assertEquals(1, ListHelper.getVideoStreamIndex("720p60", MediaFormat.MPEG_4, testList));
@@ -320,7 +324,7 @@ public void getVideoDefaultStreamIndexCombinations() {
assertEquals(0, ListHelper.getVideoStreamIndex("1080p60", null, testList));
assertEquals(6, ListHelper.getVideoStreamIndex("240p", null, testList));
- // match closest lower resolution
+ // match closest lower getResolution()
assertEquals(7, ListHelper.getVideoStreamIndex("200p", MediaFormat.WEBM, testList));
assertEquals(7, ListHelper.getVideoStreamIndex("200p60", MediaFormat.WEBM, testList));
assertEquals(7, ListHelper.getVideoStreamIndex("200p", MediaFormat.MPEG_4, testList));
@@ -331,4 +335,30 @@ public void getVideoDefaultStreamIndexCombinations() {
// Can't find a match
assertEquals(-1, ListHelper.getVideoStreamIndex("100p", null, testList));
}
+
+ @NonNull
+ private static AudioStream generateAudioStream(@NonNull final String id,
+ @Nullable final MediaFormat mediaFormat,
+ final int averageBitrate) {
+ return new AudioStream.Builder()
+ .setId(id)
+ .setContent("", true)
+ .setMediaFormat(mediaFormat)
+ .setAverageBitrate(averageBitrate)
+ .build();
+ }
+
+ @NonNull
+ private static VideoStream generateVideoStream(@NonNull final String id,
+ @Nullable final MediaFormat mediaFormat,
+ @NonNull final String resolution,
+ final boolean isVideoOnly) {
+ return new VideoStream.Builder()
+ .setId(id)
+ .setContent("", true)
+ .setIsVideoOnly(isVideoOnly)
+ .setResolution(resolution)
+ .setMediaFormat(mediaFormat)
+ .build();
+ }
}
diff --git a/app/src/test/java/org/schabi/newpipelegacy/util/urlfinder/UrlFinderTest.kt b/app/src/test/java/org/schabi/newpipelegacy/util/urlfinder/UrlFinderTest.kt
index 25c8bd46d..3c43c18f0 100644
--- a/app/src/test/java/org/schabi/newpipelegacy/util/urlfinder/UrlFinderTest.kt
+++ b/app/src/test/java/org/schabi/newpipelegacy/util/urlfinder/UrlFinderTest.kt
@@ -9,7 +9,8 @@ import org.junit.Test
class UrlFinderTest {
@Test fun `first url from long text`() {
val expected = "https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_"
- val result = UrlFinder.firstUrlFromInput("""
+ val result = UrlFinder.firstUrlFromInput(
+ """
|Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|Eu tincidunt tortor aliquam nulla. URL: https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_ Sed dictum consequat dui.
|Pharetra diam sit amet nisl suscipit adipiscing bibendum est.
@@ -18,13 +19,15 @@ class UrlFinderTest {
|Dapibus ultrices in iaculis nunc sed augue lacus viverra. Nisl purus in mollis nunc.
|Viverra nibh cras pulvinar mattis. ####!@!@!@!#### Not this one: https://www.youtube.com/playlist?list=SHOULD_NOT Nunc sed blandit libero volutpat.
|Nisl tincidunt eget nullam non nisi est sit amet. Purus in massa tempor nec feugiat nisl pretium fusce id.
- |Vulputate eu scelerisque felis imperdiet proin fermentum leo vel.""".trimMargin())
+ |Vulputate eu scelerisque felis imperdiet proin fermentum leo vel.""".trimMargin()
+ )
assertEquals(expected, result)
}
@Test fun `no url from long text`() {
- val result = UrlFinder.firstUrlFromInput("""
+ val result = UrlFinder.firstUrlFromInput(
+ """
|Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|Eu tincidunt tortor aliquam nulla. Sed dictum consequat dui. Pharetra diam sit amet nisl suscipit adipiscing bibendum est.
|Volutpat sed cras ornare arcu dui vivamus. Nulla posuere sollicitudin aliquam ultrices sagittis.
@@ -32,7 +35,8 @@ class UrlFinderTest {
|Dapibus ultrices in iaculis nunc sed augue lacus viverra. Nisl purus in mollis nunc.
|Viverra nibh cras pulvinar mattis. Not this one: sed blandit libero volutpat.
|Nisl tincidunt eget nullam non nisi est sit amet. Purus in massa tempor nec feugiat nisl pretium fusce id.
- |Vulputate eu scelerisque felis imperdiet proin fermentum leo vel.""".trimMargin())
+ |Vulputate eu scelerisque felis imperdiet proin fermentum leo vel.""".trimMargin()
+ )
assertEquals(null, result)
}
@@ -44,14 +48,20 @@ class UrlFinderTest {
}
@Test fun `normal urls`() {
- assertEquals("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
- UrlFinder.firstUrlFromInput("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_"))
+ assertEquals(
+ "https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
+ UrlFinder.firstUrlFromInput("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_")
+ )
- assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
- UrlFinder.firstUrlFromInput("https://www.youtube.com/watch?v=dQw4w9WgXcQ"))
+ assertEquals(
+ "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ UrlFinder.firstUrlFromInput("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
+ )
- assertEquals("http://www.youtube.com/watch?v=dQw4w9WgXcQ",
- UrlFinder.firstUrlFromInput("http://www.youtube.com/watch?v=dQw4w9WgXcQ"))
+ assertEquals(
+ "http://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ UrlFinder.firstUrlFromInput("http://www.youtube.com/watch?v=dQw4w9WgXcQ")
+ )
assertEquals("https://www.google.com", UrlFinder.firstUrlFromInput("https://www.google.com"))
assertEquals("http://www.google.com/test/", UrlFinder.firstUrlFromInput("http://www.google.com/test/"))
@@ -79,21 +89,33 @@ class UrlFinderTest {
}
@Test fun `random prefixes and suffixes`() {
- assertEquals("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
- UrlFinder.firstUrlFromInput("$#!@#@!#https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_ @@@@@@@@@@@"))
-
- assertEquals("https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
- UrlFinder.firstUrlFromInput("(___\"https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_\")))_"))
-
- assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
- UrlFinder.firstUrlFromInput(" https://www.youtube.com/watch?v=dQw4w9WgXcQ "))
-
- assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
- UrlFinder.firstUrlFromInput(" ------_---__-https://www.youtube.com/watch?v=dQw4w9WgXcQ !!!!!!"))
-
- assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
- UrlFinder.firstUrlFromInput("****https://www.youtube.com/watch?v=dQw4w9WgXcQ _"))
- assertEquals("https://www.youtube.com/watch?v=dQw4w9WgXcQ",
- UrlFinder.firstUrlFromInput("https://www.youtube.com/watch?v=dQw4w9WgXcQ\"Not PartOfTheUrl"))
+ assertEquals(
+ "https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
+ UrlFinder.firstUrlFromInput("$#!@#@!#https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_ @@@@@@@@@@@")
+ )
+
+ assertEquals(
+ "https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_",
+ UrlFinder.firstUrlFromInput("(___\"https://www.youtube.com/playlist?list=PLabcdefghij-ABCDEFGHIJ1234567890_\")))_")
+ )
+
+ assertEquals(
+ "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ UrlFinder.firstUrlFromInput(" https://www.youtube.com/watch?v=dQw4w9WgXcQ ")
+ )
+
+ assertEquals(
+ "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ UrlFinder.firstUrlFromInput(" ------_---__-https://www.youtube.com/watch?v=dQw4w9WgXcQ !!!!!!")
+ )
+
+ assertEquals(
+ "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ UrlFinder.firstUrlFromInput("****https://www.youtube.com/watch?v=dQw4w9WgXcQ _")
+ )
+ assertEquals(
+ "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ UrlFinder.firstUrlFromInput("https://www.youtube.com/watch?v=dQw4w9WgXcQ\"Not PartOfTheUrl")
+ )
}
}
diff --git a/build.gradle b/build.gradle
index f15900bc6..9468a2998 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,13 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.72'
+ ext.kotlin_version = '1.4.10'
repositories {
+ mavenCentral()
jcenter()
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.3'
+ classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
@@ -19,6 +20,7 @@ allprojects {
repositories {
jcenter()
google()
+ mavenCentral()
maven { url "https://jitpack.io" }
maven { url "https://clojars.org/repo" }
}
diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml
deleted file mode 100644
index b4faf2098..000000000
--- a/checkstyle-suppressions.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/checkstyle.xml b/checkstyle.xml
deleted file mode 100644
index 60a1406f9..000000000
--- a/checkstyle.xml
+++ /dev/null
@@ -1,184 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/fastlane/metadata/android/en-US/changelogs/750.txt b/fastlane/metadata/android/en-US/changelogs/750.txt
new file mode 100644
index 000000000..a22084523
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/750.txt
@@ -0,0 +1,23 @@
+New
+Playback resume #2288
+• Resume streams where you stopped last time
+Downloader Enhancements #2149
+• Use Storage Access Framework to store downloads on external SD-cards
+• New mp4 muxer
+• Optionally change the download directory before starting a download
+• Respect metered networks
+
+
+Improved
+• Removed gema strings #2295
+• Handle (auto)rotation changes during activity lifecycle #2444
+• Make long-press menus consistent #2368
+
+Fixed
+• Fixed selected subtitle track name not being shown #2394
+• Do not crash when check for app update fails (GitHub version) #2423
+• Fixed downloads stuck at 99.9% #2440
+• Update play queue metadata #2453
+• [SoundCloud] Fixed crash when loading playlists TeamNewPipe/NewPipeExtractor#170
+• [YouTube] Fixed duration can not be paresd TeamNewPipe/NewPipeExtractor#177
+
diff --git a/fastlane/metadata/android/en-US/changelogs/760.txt b/fastlane/metadata/android/en-US/changelogs/760.txt
new file mode 100644
index 000000000..66858f3ad
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/760.txt
@@ -0,0 +1,44 @@
+Changes in 0.17.1
+
+New
+• Thai localization
+
+
+Improved
+• Add start playing here action in long-press menus for playlists again #2518
+• Add switch for SAF / legacy file picker #2521
+
+Fixed
+• Fix disappearing buttons in downloads view when switching apps #2487
+• Fix playback position is stored although watch history is disabled
+• Fix reduced performance caused by playback position in list views #2517
+• [Extractor] Fix ReCaptchaActivity #2527, TeamNewPipe/NewPipeExtractor#186
+• [Extractor] [YouTube] Fix casual search error when playlists are in results TeamNewPipe/NewPipeExtractor#185
+
+
+
+Changes in 0.17.0
+
+New
+Playback resume #2288
+• Resume streams where you stopped last time
+Downloader Enhancements #2149
+• Use Storage Access Framework to store downloads on external SD-cards
+• New mp4 muxer
+• Optionally change the download directory before starting a download
+• Respect metered networks
+
+
+Improved
+• Removed gema strings #2295
+• Handle (auto)rotation changes during activity lifecycle #2444
+• Make long-press menus consistent #2368
+
+Fixed
+• Fixed selected subtitle track name not being shown #2394
+• Do not crash when check for app update fails (GitHub version) #2423
+• Fixed downloads stuck at 99.9% #2440
+• Update play queue metadata #2453
+• [SoundCloud] Fixed crash when loading playlists TeamNewPipe/NewPipeExtractor#170
+• [YouTube] Fixed duration can not be paresd TeamNewPipe/NewPipeExtractor#177
+
diff --git a/fastlane/metadata/android/en-US/changelogs/770.txt b/fastlane/metadata/android/en-US/changelogs/770.txt
new file mode 100644
index 000000000..439c6532b
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/770.txt
@@ -0,0 +1,4 @@
+Changes in 0.17.2
+
+Fix
+• Fix no video was available
diff --git a/fastlane/metadata/android/en-US/changelogs/780.txt b/fastlane/metadata/android/en-US/changelogs/780.txt
new file mode 100644
index 000000000..9100d7335
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/780.txt
@@ -0,0 +1,12 @@
+Changes in 0.17.3
+
+Improved
+• Added option to clear playback states #2550
+• Show hidden directories in the file picker #2591
+• Support URLs from `invidio.us` instances to be opened with NewPipe #2488
+• Add support for `music.youtube.com` URLs TeamNewPipe/NewPipeExtractor#194
+
+Fixed
+• [YouTube] Fixed 'java.lang.IllegalArgumentException #192
+• [YouTube] Fixed live streams not working TeamNewPipe/NewPipeExtractor#195
+• Fixed performance problem in android pie when downloading a stream #2592
diff --git a/fastlane/metadata/android/en-US/changelogs/790.txt b/fastlane/metadata/android/en-US/changelogs/790.txt
new file mode 100644
index 000000000..ec77b2acb
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/790.txt
@@ -0,0 +1,14 @@
+Improved
+• Add more titles to improve accessibility for blind people #2655
+• Make language of download folder setting more consistent and less ambiguous #2637
+
+Fixed
+• Check if last byte in the block is downloaded #2646
+• Fixed scrolling in video detail fragment #2672
+• Remove double search clear box animations to one #2695
+• [SoundCloud] Fix client_id extraction #2745
+
+Development
+• Add missing dependencies inherited from NewPipeExtractor into NewPipe #2535
+• Migrate to AndroidX #2685
+• Update to ExoPlayer 2.10.6 #2697, #2736
diff --git a/fastlane/metadata/android/en-US/changelogs/800.txt b/fastlane/metadata/android/en-US/changelogs/800.txt
new file mode 100644
index 000000000..0d9a29a64
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/800.txt
@@ -0,0 +1,28 @@
+New
+• PeerTube support without P2P (#2201) [Beta]:
+ ◦ Watch and download videos from PeerTube instances
+ ◦ Add instances in the settings to access the complete PeerTube world
+ ◦ There might be problems with SSL handshakes on Android 4.4 and 7.1 when accessing certain instances resulting in a network error.
+
+• Downloader (#2679):
+ ◦ Calculate download ETA
+ ◦ Download opus (webm files) as ogg
+ ◦ Recover expired download links to resume downloads after a long pause
+
+Improved
+• Make the KioskFragment aware of changes in the preferred content country and improve performance of all main tabs #2742
+• Use new Localization and Downloader implementations from extractor #2713
+• Make "Default kiosk" string translatable
+• Black navigation bar for black theme #2569
+
+Fixed
+• Fixed a bug that could not move the popup player if another finger was placed while moving the popup player #2772
+• Allow playlists missing an uploader and fix crashes related to this problem #2724, TeamNewPipe/NewPipeExtractor#219
+• Enabling TLS1.1/1.2 on Android 4.4 devices (API 19/KitKat) to fix TLS handshake with MediaCCC and some PeerTube instances #2792
+• [SoundCloud] Fixed client_id extraction TeamNewPipe/NewPipeExtractor#217
+• [SoundCloud] Fix audio stream extraction
+
+Development
+• Update ExoPlayer to 2.10.8 #2791, #2816
+• Update Gradle to 3.5.1 and add Kotlin support #2714
+
diff --git a/fastlane/metadata/android/en-US/changelogs/810.txt b/fastlane/metadata/android/en-US/changelogs/810.txt
new file mode 100644
index 000000000..43fcc5287
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/810.txt
@@ -0,0 +1,19 @@
+New
+• Show video thumbnail on the lock screen when playing in the background
+
+Improved
+• Add local playlist to queue when long pressing on background / popup button
+• Make main page tabs scrollable and hide when there is only a single tab
+• Limit amount of notification thumbnail updates in background player
+• Add dummy thumbnail for empty local playlists
+• Use *.opus file extension instead of *.webm and show "opus" in format label instead of "WebM Opus" in the download dropdown
+• Add button to delete downloaded files or download history in "Downloads"
+• [YouTube] Add support to /c/shortened_url channel links
+
+Fixed
+• Fixed multiple issues when sharing a video to NewPipe and downloading its streams directly
+• Fixed player access out of its creation thread
+• Fixed search result paging
+• [YouTube] Fixed switching on null causing NPE
+• [YouTube] Fixed viewing comments when opening an invidio.us url
+• [SoundCloud] Updated client_id
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/820.txt b/fastlane/metadata/android/en-US/changelogs/820.txt
new file mode 100644
index 000000000..d56291711
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/820.txt
@@ -0,0 +1 @@
+Fixed decrypt function name regex making YouTube unusable.
diff --git a/fastlane/metadata/android/en-US/changelogs/830.txt b/fastlane/metadata/android/en-US/changelogs/830.txt
new file mode 100644
index 000000000..c9876f338
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/830.txt
@@ -0,0 +1 @@
+Updated SoundCloud client_id to fix SoundCloud issues.
diff --git a/fastlane/metadata/android/en-US/changelogs/840.txt b/fastlane/metadata/android/en-US/changelogs/840.txt
new file mode 100644
index 000000000..c79321b45
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/840.txt
@@ -0,0 +1,22 @@
+New
+• Added language selector to change the app language
+• Added send to Kodi button to player collapsible menu
+• Added ability to copy comments on long press
+
+Improved
+• Fix ReCaptcha activity and correctly save obtained cookies
+• Removed dot-menu in favour of drawer and hide history button when watch history is not enabled in settings
+• Ask for display over other apps permission in settings correctly on Android 6 and later
+• Rename local playlist by long-clicking in BookmarkFragment
+• Various PeerTube improvements
+• Improved several English source strings
+
+Fixed
+• Fixed player starting again although it is paused when option "minimize on app switch" enabled and NewPipe is minimized
+• Fix initial brightness value for gesture
+• Fixed .srt subtitle downloads containing not all line breaks
+• Fixed download to SD card failing because some Android 5 devices are not CTF compliant
+• Fixed downloading on Android KitKat
+• Fixed corrupt video .mp4 file being recognized as audio file
+• Fixed multiple localization problems, including wrong Chinese language codes
+• [YouTube] Timestamps in description are clickable again
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/850.txt b/fastlane/metadata/android/en-US/changelogs/850.txt
new file mode 100644
index 000000000..90f343f22
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/850.txt
@@ -0,0 +1 @@
+Tn this release the YouTube website version was updated. The old website version is going to be discontinued in March and therefore you are required to upgrade NewPipe.
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/860.txt b/fastlane/metadata/android/en-US/changelogs/860.txt
new file mode 100644
index 000000000..f01b86d47
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/860.txt
@@ -0,0 +1,7 @@
+Improved
+• Save and restore whether pitch and tempo are unhooked or not
+• Support display cutout in player
+• Round view and subscriber count
+• Optimized YouTube to use less data
+
+More than 15 YouTube-related bugs were fixed in this release.
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/870.txt b/fastlane/metadata/android/en-US/changelogs/870.txt
new file mode 100644
index 000000000..859a1f8b9
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/870.txt
@@ -0,0 +1,2 @@
+This is a hotfix release updating NewPipe to allow using SoundCloud without major hassles again.
+SoundCloud's v2 API is used in the extractor now and the detection of invalid client IDs has been improved.
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/900.txt b/fastlane/metadata/android/en-US/changelogs/900.txt
new file mode 100644
index 000000000..b3086b13a
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/900.txt
@@ -0,0 +1,14 @@
+New
+• Subscription groups and sorted feeds
+• Mute button in players
+
+Improved
+• Allow opening music.youtube.com and media.ccc.de links in NewPipe
+• Relocate two settings from Appearance to Content
+• Hide 5, 15, 25 second seek options if inexact seek is enabled
+
+Fixed
+• some WebM videos are not seekable
+• database backup on Android P
+• crash when sharing a downloaded file
+• tons of YouTube extraction issue and more ...
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/910.txt b/fastlane/metadata/android/en-US/changelogs/910.txt
new file mode 100644
index 000000000..f3e6891dd
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/910.txt
@@ -0,0 +1 @@
+Fixed database migration which prevented NewPipe from starting in some rare cases.
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/920.txt b/fastlane/metadata/android/en-US/changelogs/920.txt
new file mode 100644
index 000000000..85bce5286
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/920.txt
@@ -0,0 +1,9 @@
+Improved
+
+• Added upload date and view count on stream grid items
+• Improvements for the drawer header layout
+
+Fixed
+
+• Fixed mute button causing crashes on API 19
+• Fixed downloading of long 1080p 60fps videos
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/930.txt b/fastlane/metadata/android/en-US/changelogs/930.txt
new file mode 100644
index 000000000..b23b01ea8
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/930.txt
@@ -0,0 +1,19 @@
+New
+• Search on YouTube Music
+• Basic Android TV support
+
+Improved
+• Added the ability to remove all watched videos from a local playlist
+• Show message when content isn't supported yet instead of crashing
+• Improved popup player resize with pinch gestures
+• Enqueue streams on long press on background and popup buttons in channel
+• Improved size handling of the drawer header title
+
+Fixed
+• Fixed age restricted content setting not working
+• Fixed certain kinds of reCAPTCHAs
+• Fixed crash when opening bookmarks while playlist is `null`
+• Fixed detection of network related exceptions
+• Fixed visibility of group sort button in the subscriptions fragment
+
+and more
diff --git a/fastlane/metadata/android/en-US/changelogs/940.txt b/fastlane/metadata/android/en-US/changelogs/940.txt
new file mode 100644
index 000000000..f9530bc68
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/940.txt
@@ -0,0 +1,16 @@
+New
+• Add support for SoundCloud comments
+• Add YouTube restricted mode setting
+• Show PeerTube parent channel details
+
+Improved
+• Show Kore button only for supported services
+• Block player gestures that begin at the NavigationBar or StatusBar
+• Change retry & subscribe buttons background color based on service color
+
+Fixed
+• Fix download dialog freeze
+• Open in browser button now really opens in browser
+• Fix crash on opening videos and "Could not play this stream"
+
+and more
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png
index 803fb6428..b5d413142 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png
new file mode 100644
index 000000000..c1f4599c2
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png
new file mode 100644
index 000000000..12cf3002e
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png
new file mode 100644
index 000000000..fca7a0128
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5f9210e27..a79cc04b6 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri May 01 19:39:41 CEST 2020
+#Sat Oct 17 06:10:46 IST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip
diff --git a/settings.gradle b/settings.gradle
index 9d495b34f..6fa91ca93 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,11 @@
-include ':app'
\ No newline at end of file
+include ':app'
+
+// Use a local copy of NewPipe Extractor by uncommenting the lines below.
+// We assume, that NewPipe and NewPipe Extractor have the same parent directory.
+// If this is not the case, please change the path in includeBuild().
+
+//includeBuild('../NewPipeExtractor') {
+// dependencySubstitution {
+// substitute module('com.github.TeamNewPipe:NewPipeExtractor') with project(':extractor')
+// }
+//}
\ No newline at end of file